예제 #1
0
def test_element_path_to_file_path():
    """Test to file path method."""
    assert ElementPath('component-definition.metadata.title').to_file_path(
    ) == pathlib.Path('./component-definition/metadata/title')

    assert ElementPath('component-definition.metadata.title').to_file_path(
        FileContentType.YAML) == pathlib.Path(
            './component-definition/metadata/title.yaml')

    assert ElementPath('component-definition.metadata.parties').to_file_path(
        FileContentType.JSON) == pathlib.Path(
            './component-definition/metadata/parties.json')

    assert ElementPath('component-definition.metadata.parties.*').to_file_path(
        FileContentType.YAML) == pathlib.Path(
            './component-definition/metadata/parties.yaml')

    assert ElementPath('group.controls.*').to_file_path(
        FileContentType.YAML,
        '00000__group') == pathlib.Path('./00000__group/controls.yaml')

    element_path = ElementPath(
        'component.control-implementations.*',
        ElementPath('component-definition.components.*'))
    assert element_path.to_file_path() == pathlib.Path(
        './component/control-implementations')

    # error for invalid content type
    with pytest.raises(TrestleError):
        assert ElementPath(
            'component-definition.metadata.parties.*').to_file_path(-1)
예제 #2
0
def test_remove_failure(tmp_path, sample_catalog_minimal):
    """Test failure of RemoveCmd.remove() method for trestle remove."""
    # Note: minimal catalog does have responsible-parties but doesn't have Roles.
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog.json')
    catalog_with_responsible_parties = Element(Catalog.oscal_read(file_path))

    # Supply nonexistent element Roles for removal:
    element_path = ElementPath('catalog.metadata.roles')
    try:
        actual_remove_action, actual_catalog_removed_responsible_parties = RemoveCmd.remove(
            element_path, Catalog, catalog_with_responsible_parties)
    except Exception:
        assert True
    else:
        AssertionError()

    # Supply a wildcard element for removal:
    element_path = ElementPath('catalog.*')
    try:
        actual_remove_action, actual_catalog_removed_responsible_parties = RemoveCmd.remove(
            element_path, Catalog, catalog_with_responsible_parties)
    except Exception:
        assert True
    else:
        AssertionError()
예제 #3
0
def test_add(tmp_path: pathlib.Path, keep_cwd: pathlib.Path) -> None:
    """Test Add.add() method used by trestle CreateCmd."""
    file_path = pathlib.Path(
        test_utils.JSON_TEST_DATA_PATH) / 'minimal_catalog_missing_roles.json'
    minimal_catalog_missing_roles = Catalog.oscal_read(file_path)

    # expected catalog after first add of Role
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles.json')
    expected_catalog_roles1 = Element(Catalog.oscal_read(file_path))

    # expected catalog after second add of Role
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles_double.json')
    expected_catalog_roles2 = Element(Catalog.oscal_read(file_path))

    # expected catalog after add of Responsible-Party
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles_double_rp.json')
    expected_catalog_roles2_rp = Element(Catalog.oscal_read(file_path))

    content_type = FileContentType.JSON

    _, _ = test_utils.prepare_trestle_project_dir(
        tmp_path, content_type, minimal_catalog_missing_roles,
        test_utils.CATALOGS_DIR)

    # Execute first _add
    element_path = ElementPath('catalog.metadata.roles')
    catalog_element = Element(minimal_catalog_missing_roles)
    expected_update_action_1 = UpdateAction(
        expected_catalog_roles1.get_at(element_path), catalog_element,
        element_path)
    actual_update_action, actual_catalog_roles = Add.add(
        element_path, catalog_element, False)

    assert actual_catalog_roles == expected_catalog_roles1
    assert actual_update_action == expected_update_action_1

    # Execute second _add - this time roles already exists, so this adds a roles object to roles array
    catalog_element = actual_catalog_roles
    expected_update_action_2 = UpdateAction(
        expected_catalog_roles2.get_at(element_path), catalog_element,
        element_path)
    actual_update_action2, actual_catalog_roles2 = Add.add(
        element_path, catalog_element, False)
    assert actual_catalog_roles2 == expected_catalog_roles2
    assert actual_update_action2 == expected_update_action_2

    # Execute _add for responsible-parties to the same catalog
    element_path = ElementPath('catalog.metadata.responsible-parties')
    catalog_element = actual_catalog_roles2
    expected_update_action_3 = UpdateAction(
        expected_catalog_roles2_rp.get_at(element_path), catalog_element,
        element_path)
    actual_update_action3, actual_catalog_roles2_rp = Add.add(
        element_path, catalog_element, False)
    assert actual_catalog_roles2_rp == expected_catalog_roles2_rp
    assert actual_update_action3 == expected_update_action_3
예제 #4
0
def test_make_relative():
    """Test make relative path."""
    p = ElementPath('catalog.groups')
    path = pathlib.Path('catalog/groups/controls')
    assert p.make_relative(path) == 1

    path = pathlib.Path('profiles/controls')
    assert p.make_relative(path) == 1
예제 #5
0
def test_element_path_eq(sample_nist_component_def):
    """Test for magic method eq."""
    assert ElementPath('component.metadata') == ElementPath(
        'component.metadata')
    assert not (ElementPath('component.metadata')
                == ElementPath('component.title'))
    assert not (ElementPath('component.metadata')
                == Element(sample_nist_component_def))
예제 #6
0
def test_element_path_get_element_name():
    """Test get element name method."""
    assert ElementPath('component-definition.metadata.last-modified'
                       ).get_element_name() == 'last-modified'
    assert ElementPath(
        'component-definition.metadata.title').get_element_name() == 'title'
    assert ElementPath(
        'component-definition.metadata').get_element_name() == 'metadata'
    assert ElementPath('component-definition.metadata.parties.*'
                       ).get_element_name() == 'parties'
예제 #7
0
def test_full_path():
    """Test full path paths method."""
    element_arg = 'catalog.groups.*.controls.*.controls.*'
    p1 = ElementPath('catalog.groups.*')
    p2 = ElementPath('group.controls.*', parent_path=p1)
    p3 = ElementPath('control.controls.*', parent_path=p2)

    full_path_parts = p3.get_full_path_parts()
    full_path = ElementPath.PATH_SEPARATOR.join(full_path_parts)
    assert element_arg == full_path
예제 #8
0
def test_split_model_at_path_chain_failures(tmp_path,
                                            sample_catalog: oscatalog.Catalog):
    """Test for split_model_at_path_chain method failure scenarios."""
    content_type = FileContentType.JSON

    # prepare trestle project dir with the file
    catalog_dir, catalog_file = test_utils.prepare_trestle_project_dir(
        tmp_path, content_type, sample_catalog, test_utils.CATALOGS_DIR)

    split_plan = Plan()
    element_paths = [ElementPath('catalog.metadata.parties.*')]

    # long chain of path should error
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                           catalog_dir, content_type, 0,
                                           split_plan, False)

    # no plan should error
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                           catalog_dir, content_type, 0, None,
                                           False)

    # negative path index should error
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                           catalog_dir, content_type, -1,
                                           split_plan, False)

    # too large path index should return the path index
    cur_path_index = len(element_paths) + 1
    SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                       catalog_dir, content_type,
                                       cur_path_index, split_plan, False)

    # invalid model path should return withour doing anything
    element_paths = [ElementPath('catalog.meta')]
    cur_path_index = 0
    SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                       catalog_dir, content_type,
                                       cur_path_index, split_plan, False)

    # invalid path for multi item sub-model
    p0 = ElementPath(
        'catalog.uuid.*'
    )  # uuid exists, but it is not a multi-item sub-model object
    p1 = ElementPath(
        'uuid.metadata.*', p0
    )  # this is invalid but we just need a path with the p0 as the parent
    element_paths = [p0, p1]
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(sample_catalog, element_paths,
                                           catalog_dir, content_type, 0,
                                           split_plan, False)
def test_element_path_get_element_name(
        sample_target_def: target.TargetDefinition):
    """Test get element name method."""
    assert ElementPath('target-definition.metadata.last-modified'
                       ).get_element_name() == 'last-modified'
    assert ElementPath(
        'target-definition.metadata.title').get_element_name() == 'title'
    assert ElementPath(
        'target-definition.metadata').get_element_name() == 'metadata'
    assert ElementPath(
        'target-definition.metadata.parties.*').get_element_name() == 'parties'
def test_element_path_to_root_path():
    """Test to file path method."""
    assert ElementPath('target-definition.metadata.title').to_root_path(
    ) == pathlib.Path('./target-definition')
    assert ElementPath('target-definition.metadata.title').to_root_path(
        FileContentType.YAML) == pathlib.Path('./target-definition.yaml')
    assert ElementPath('target-definition.metadata.title').to_root_path(
        FileContentType.JSON) == pathlib.Path('./target-definition.json')

    # error for invalid content type - 1
    with pytest.raises(TrestleError):
        assert ElementPath('target-definition.metadata.title').to_root_path(-1)
예제 #11
0
def test_get_obm_wrapped_type(element_path: str, collection: bool,
                              type_or_inner_type: Type[OscalBaseModel],
                              exception_expected: bool):
    """Test whether we can wrap a control properly."""
    if exception_expected:
        with pytest.raises(TrestleError):
            _ = ElementPath(element_path).get_obm_wrapped_type()
        return
    my_type = ElementPath(element_path).get_obm_wrapped_type()
    if collection:
        inner_type = utils.get_inner_type(my_type)
        assert type_or_inner_type == inner_type
    else:
        assert type_or_inner_type == my_type
예제 #12
0
def test_add(tmp_dir, sample_catalog_minimal):
    """Test AddCmd.add() method for trestle add."""
    # expected catalog after first add of Role
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles.json')
    expected_catalog_roles1 = Catalog.oscal_read(file_path)

    # expected catalog after second add of Role
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles_double.json')
    expected_catalog_roles2 = Catalog.oscal_read(file_path)

    # expected catalog after add of Responsible-Party
    file_path = pathlib.Path.joinpath(test_utils.JSON_TEST_DATA_PATH,
                                      'minimal_catalog_roles_double_rp.json')
    expected_catalog_roles2_rp = Catalog.oscal_read(file_path)

    content_type = FileContentType.JSON

    catalog_def_dir, catalog_def_file = test_utils.prepare_trestle_project_dir(
        tmp_dir, content_type, sample_catalog_minimal, test_utils.CATALOGS_DIR)

    # Execute first _add
    element_path = ElementPath('catalog.metadata.roles')
    catalog_element = Element(sample_catalog_minimal)
    AddCmd.add(catalog_def_dir / catalog_def_file, element_path, Catalog,
               catalog_element)

    actual_catalog_roles = Catalog.oscal_read(catalog_def_dir /
                                              catalog_def_file)
    assert actual_catalog_roles == expected_catalog_roles1

    # Execute second _add - this time roles already exists, so this adds a roles object to roles array
    catalog_element = Element(actual_catalog_roles)
    AddCmd.add(catalog_def_dir / catalog_def_file, element_path, Catalog,
               catalog_element)
    actual_catalog_roles2 = Catalog.oscal_read(catalog_def_dir /
                                               catalog_def_file)
    assert actual_catalog_roles2 == expected_catalog_roles2

    # Execute _add for responsible-parties to the same catalog
    element_path = ElementPath('catalog.metadata.responsible-parties')
    catalog_element = Element(actual_catalog_roles2)
    AddCmd.add(catalog_def_dir / catalog_def_file, element_path, Catalog,
               catalog_element)
    actual_catalog_roles2_rp = Catalog.oscal_read(catalog_def_dir /
                                                  catalog_def_file)
    assert actual_catalog_roles2_rp == expected_catalog_roles2_rp
예제 #13
0
def test_update_action(sample_nist_component_def):
    """Test update action."""
    element = Element(sample_nist_component_def)

    metadata = common.Metadata(
        **{
            'title': 'My simple catalog',
            'last-modified': datetime.now().astimezone(),
            'version': '0.0.0',
            'oscal-version': OSCAL_VERSION
        })

    sub_element_path = ElementPath('component-definition.metadata')
    prev_metadata = element.get_at(sub_element_path)

    uac = UpdateAction(metadata, element, sub_element_path)

    uac.execute()

    assert element.get_at(sub_element_path) is not prev_metadata
    assert element.get_at(sub_element_path) == metadata

    uac.rollback()

    assert element.get_at(sub_element_path) == prev_metadata
    assert element.get_at(sub_element_path) is not metadata
예제 #14
0
    def add_from_args(self, args: argparse.Namespace) -> int:
        """Parse args for add element to file."""
        file_path = pathlib.Path(args.file).resolve()

        # Get parent model and then load json into parent model
        parent_model, _ = ModelUtils.get_stripped_model_type(file_path, args.trestle_root)
        parent_object = parent_model.oscal_read(file_path)
        parent_element = Element(parent_object, classname_to_alias(parent_model.__name__, AliasMode.JSON))

        add_plan = Plan()
        # Do _add for each element_path specified in args
        element_paths: List[str] = args.element.split(',')
        for elm_path_str in element_paths:
            element_path = ElementPath(elm_path_str)
            update_action, parent_element = self.add(element_path, parent_element, args.include_optional_fields)
            add_plan.add_action(update_action)

        create_action = CreatePathAction(file_path, True)
        # this will output json or yaml based on type of input file
        write_action = WriteFileAction(file_path, parent_element, FileContentType.to_content_type(file_path.suffix))

        add_plan.add_action(create_action)
        add_plan.add_action(write_action)

        add_plan.execute()
        return CmdReturnCodes.SUCCESS.value
예제 #15
0
def prepare_expected_element_paths(element_args: List[str]) -> List[ElementPath]:
    """Prepare a list of ElementPath from a list of path strings."""
    element_paths: List[ElementPath] = []
    for element_arg in element_args:
        element_paths.append(ElementPath(element_arg))

    return element_paths
예제 #16
0
def prepare_element(sample_target_def):
    """Prepare a target element for remove tests."""
    element = Element(sample_target_def)

    parties: List[target.Party] = []
    parties.append(
        target.Party(
            **{
                'uuid': 'ff47836c-877c-4007-bbf3-c9d9bd805000',
                'party-name': 'TEST1',
                'type': 'organization'
            }))
    parties.append(
        target.Party(
            **{
                'uuid': 'ee88836c-877c-4007-bbf3-c9d9bd805000',
                'party-name': 'TEST2',
                'type': 'organization'
            }))

    sub_element_path = ElementPath('target-definition.metadata.parties.*')
    ac = UpdateAction(parties, element, sub_element_path)
    ac.execute()

    assert element.get_at(sub_element_path) == parties

    return element
def test_update_action(sample_target):
    """Test update action."""
    element = Element(sample_target)

    metadata = target.Metadata(
        **{
            'title': 'My simple catalog',
            'last-modified': datetime.now().astimezone(),
            'version': '0.0.0',
            'oscal-version': '1.0.0-Milestone3'
        })

    sub_element_path = ElementPath('metadata')
    prev_metadata = element.get_at(sub_element_path)

    uac = UpdateAction(metadata, element, sub_element_path)

    uac.execute()

    assert element.get_at(sub_element_path) is not prev_metadata
    assert element.get_at(sub_element_path) == metadata

    uac.rollback()

    assert element.get_at(sub_element_path) == prev_metadata
    assert element.get_at(sub_element_path) is not metadata
예제 #18
0
def test_merge_everything_into_catalog_with_hidden_files_in_folders(
        testdata_dir, tmp_trestle_dir):
    """Test trestle merge -e 'catalog.*' when metadata and catalog are split and hidden files are present."""
    # Assume we are running a command like below
    # trestle merge -e catalog.*
    content_type = FileContentType.JSON
    fext = FileContentType.to_file_extension(content_type)

    # prepare trestle project dir with the file
    test_utils.ensure_trestle_config_dir(tmp_trestle_dir)

    test_data_source = testdata_dir / 'split_merge/step4_split_groups_array/catalogs'
    catalogs_dir = Path('catalogs/')
    mycatalog_dir = catalogs_dir / 'mycatalog'

    # Copy files from test/data/split_merge/step4
    shutil.rmtree(catalogs_dir)
    shutil.copytree(test_data_source, catalogs_dir)

    # Change directory to mycatalog_dir
    os.chdir(mycatalog_dir)
    catalog_file = Path(f'catalog{fext}').resolve()

    assert catalog_file.exists()

    # Read files

    # Create hand-crafter merge plan
    expected_plan: Plan = Plan()

    reset_destination_action = CreatePathAction(catalog_file,
                                                clear_content=True)
    expected_plan.add_action(reset_destination_action)

    _, _, merged_catalog_instance = ModelUtils.load_distributed(
        catalog_file, tmp_trestle_dir)

    element = Element(merged_catalog_instance)
    write_destination_action = WriteFileAction(catalog_file,
                                               element,
                                               content_type=content_type)
    expected_plan.add_action(write_destination_action)
    delete_element_action = RemovePathAction(Path('catalog').resolve())
    expected_plan.add_action(delete_element_action)

    test_utils.make_hidden_file(tmp_trestle_dir /
                                'catalogs/mycatalog/.DS_Store')
    test_utils.make_hidden_file(tmp_trestle_dir /
                                'catalogs/mycatalog/catalog/.DS_Store')
    test_utils.make_hidden_file(
        tmp_trestle_dir / 'catalogs/mycatalog/catalog/metadata/.DS_Store')
    test_utils.make_hidden_file(tmp_trestle_dir /
                                'catalogs/mycatalog/catalog/groups/.DS_Store')

    # Call merge()
    generated_plan = MergeCmd.merge(Path.cwd(), ElementPath('catalog.*'),
                                    tmp_trestle_dir)

    # Assert the generated plan matches the expected plan'
    assert generated_plan == expected_plan
예제 #19
0
    def _run(self, args):
        """Add an OSCAL component/subcomponent to the specified component.

        This method takes input a filename and a list of comma-seperated element path. Element paths are field aliases.
        The method first finds the parent model from the file and loads the file into the model.
        Then the method executes 'add' for each of the element paths specified.
        """
        args = args.__dict__
        if args[const.ARG_FILE] is None:
            raise err.TrestleError(
                f'Argument "-{const.ARG_FILE_SHORT}" is required')
        if args[const.ARG_ELEMENT] is None:
            raise err.TrestleError(
                f'Argument "-{const.ARG_ELEMENT}" is required')

        file_path = pathlib.Path(args[const.ARG_FILE])

        # Get parent model and then load json into parent model
        parent_model, parent_alias = fs.get_contextual_model_type(
            file_path.absolute())
        parent_object = parent_model.oscal_read(file_path.absolute())
        parent_element = Element(
            parent_object,
            utils.classname_to_alias(parent_model.__name__, 'json'))

        # Do _add for each element_path specified in args
        element_paths: list[str] = args[const.ARG_ELEMENT].split(',')
        for elm_path_str in element_paths:
            element_path = ElementPath(elm_path_str)
            self.add(file_path, element_path, parent_model, parent_element)
예제 #20
0
    def _run(self, args: argparse.Namespace) -> int:
        """
        Execute the create command.

        Notes
            Either a new model will be created of the specified type,
            or an existing file will have new elements added within it.
        """
        try:
            # Normal create path
            if args.type and args.output:
                object_type = ElementPath(args.type).get_type()
                return self.create_object(args.type, object_type, args)
            # Add path
            elif args.file and args.element:
                add = Add()
                return add.add_from_args(args)

            raise err.TrestleIncorrectArgsError(
                'Create requires either a model type and output name, or a file and element path.'
            )

        except Exception as e:  # pragma: no cover
            return err.handle_generic_command_exception(
                e, logger, 'Error while creating a sample OSCAL model')
예제 #21
0
def test_split_model_at_path_chain_failures(
        tmp_path, simplified_nist_catalog: oscatalog.Catalog) -> None:
    """Test for split_model_at_path_chain method failure scenarios."""
    content_type = FileContentType.JSON

    # prepare trestle project dir with the file
    catalog_dir, catalog_file = test_utils.prepare_trestle_project_dir(
        tmp_path, content_type, simplified_nist_catalog,
        test_utils.CATALOGS_DIR)

    split_plan = Plan()
    element_paths = [ElementPath('catalog.metadata.parties.*')]

    # no plan should error
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(simplified_nist_catalog,
                                           element_paths, catalog_dir,
                                           content_type, 0, None, False, '',
                                           None)

    # negative path index should error
    with pytest.raises(TrestleError):
        SplitCmd.split_model_at_path_chain(simplified_nist_catalog,
                                           element_paths, catalog_dir,
                                           content_type, -1, split_plan, False,
                                           '', None)

    # too large path index should return the path index
    cur_path_index = len(element_paths) + 1
    SplitCmd.split_model_at_path_chain(simplified_nist_catalog, element_paths,
                                       catalog_dir, content_type,
                                       cur_path_index, split_plan, False, '',
                                       None)
def test_update_list_sub_element_action(sample_target):
    """Test setting a list."""
    element = Element(sample_target)

    parties: List[target.Party] = []
    parties.append(
        target.Party(
            **{
                'uuid': 'ff47836c-877c-4007-bbf3-c9d9bd805000',
                'party-name': 'TEST1',
                'type': 'organization'
            }))
    parties.append(
        target.Party(
            **{
                'uuid': 'ee88836c-877c-4007-bbf3-c9d9bd805000',
                'party-name': 'TEST2',
                'type': 'organization'
            }))

    sub_element_path = ElementPath('metadata.parties.*')
    uac = UpdateAction(parties, element, sub_element_path)
    uac.execute()

    assert element.get_at(sub_element_path) == parties
예제 #23
0
def split_is_too_fine(split_paths: str, model_obj: OscalBaseModel) -> bool:
    """Determine if the element path list goes too fine, e.g. individual strings."""
    for split_path in split_paths.split(','):
        # find model type one level above if finishing with '.*'
        model_type = ElementPath(split_path.rstrip('.*')).get_type(type(model_obj))
        if model_type_is_too_granular(model_type):
            return True
    return False
예제 #24
0
    def add(element_path: ElementPath, parent_element: Element, include_optional: bool) -> None:
        """For a element_path, add a child model to the parent_element of a given parent_model.

        Args:
            element_path: element path of the item to create within the model
            parent_element: the parent element that will host the created element
            include_optional: whether to create optional attributes in the created element

        Notes:
            First we find the child model at the specified element path and instantiate it with default values.
            Then we check if there's already existing element at that path, in which case we append the child model
            to the existing list of dict.
            Then we set up an action plan to update the model (specified by file_path) in memory, create a file
            at the same location and write the file.
            We update the parent_element to prepare for next adds in the chain
        """
        if '*' in element_path.get_full_path_parts():
            raise err.TrestleError('trestle add does not support Wildcard element path.')
        # Get child model
        try:
            child_model = element_path.get_type(type(parent_element.get()))

            # Create child element with sample values
            child_object = gens.generate_sample_model(child_model, include_optional=include_optional)

            if parent_element.get_at(element_path) is not None:
                # The element already exists
                if type(parent_element.get_at(element_path)) is list:
                    child_object = parent_element.get_at(element_path) + child_object
                elif type(parent_element.get_at(element_path)) is dict:
                    child_object = {**parent_element.get_at(element_path), **child_object}
                else:
                    raise err.TrestleError('Already exists and is not a list or dictionary.')

        except Exception as e:
            raise err.TrestleError(f'Bad element path. {str(e)}')

        update_action = UpdateAction(
            sub_element=child_object, dest_element=parent_element, sub_element_path=element_path
        )
        parent_element = parent_element.set_at(element_path, child_object)

        return update_action, parent_element
예제 #25
0
def test_add_failure(tmp_dir, sample_catalog_minimal):
    """Test AddCmd.add() method for trestle add."""
    content_type = FileContentType.JSON

    catalog_def_dir, catalog_def_file = test_utils.prepare_trestle_project_dir(
        tmp_dir, content_type, sample_catalog_minimal, test_utils.CATALOGS_DIR)

    element_path = ElementPath('catalog.metadata.*')
    catalog_element = Element(sample_catalog_minimal)

    with pytest.raises(err.TrestleError):
        AddCmd.add(element_path, Catalog, catalog_element)

    element_path = ElementPath('catalog.metadata.title')
    with pytest.raises(err.TrestleError):
        AddCmd.add(element_path, Catalog, catalog_element)

    element_path = ElementPath('catalog.metadata.bad_path')
    with pytest.raises(err.TrestleError):
        AddCmd.add(element_path, Catalog, catalog_element)
예제 #26
0
def test_parse_element_arg():
    """Unit test parse a single element arg."""
    element_arg = 'target-definition.targets'
    expected_paths: List[ElementPath] = prepare_expected_element_paths(
        ['target-definition.targets'])
    element_paths: List[ElementPath] = cmd_utils.parse_element_arg(element_arg)
    assert expected_paths == element_paths

    element_arg = 'target-definition.targets.*'
    expected_paths: List[ElementPath] = prepare_expected_element_paths(
        ['target-definition.targets.*'])
    element_paths: List[ElementPath] = cmd_utils.parse_element_arg(element_arg)
    assert expected_paths == element_paths

    element_arg = 'catalog.groups.*.controls.*.controls.*'
    p1 = ElementPath('catalog.groups.*')
    p2 = ElementPath('controls.*', parent_path=p1)
    p3 = ElementPath('controls.*', parent_path=p2)
    expected_paths: List[ElementPath] = [p1, p2, p3]
    element_paths: List[ElementPath] = cmd_utils.parse_element_arg(element_arg)
    assert expected_paths == element_paths
예제 #27
0
def parse_element_arg(element_arg: str, contextual_mode: bool = True) -> List[ElementPath]:
    """Parse an element arg string into a list of ElementPath.

    contextual_mode specifies if the path is a valid project model path or not. For example,
    if we are processing a metadata.parties.*, we need to know which metadata we are processing. If we pass
    contextual_mode=True, we can infer the root model by inspecting the file directory

    If contextual_mode=False, then the path must include the full path, e.g. catalog.metadata.parties.* instead of just
    metadata.parties.*

    One option for caller to utilize this utility function: fs.is_valid_project_model_path(pathlib.Path.cwd())
    """
    element_paths: List[ElementPath] = []
    element_arg = element_arg.strip()

    # search for wildcards and create paths with its parent path
    path_parts = element_arg.split(ElementPath.PATH_SEPARATOR)
    if len(path_parts) <= 0:
        raise TrestleError(f'Invalid element path "{element_arg}" without any path separator')

    prev_element_path = None
    parent_model = path_parts[0]
    i = 1
    while i < len(path_parts):
        p = path_parts[i]
        if p == ElementPath.WILDCARD and len(element_paths) > 0:
            # append wildcard to the latest element path
            latest_path = element_paths.pop()
            if latest_path.get_last() == ElementPath.WILDCARD:
                raise TrestleError(f'Invalid element path with consecutive {ElementPath.WILDCARD}')

            latest_path_str = ElementPath.PATH_SEPARATOR.join([latest_path.to_string(), p])
            element_path = ElementPath(latest_path_str, latest_path.get_parent())
        else:
            # create and append elment_path
            p = ElementPath.PATH_SEPARATOR.join([parent_model, p])
            element_path = ElementPath(p, parent_path=prev_element_path)

        # if the path has wildcard and there is more parts later,
        # get the parent model for the alias path
        if element_path.get_last() == ElementPath.WILDCARD:
            full_path_str = ElementPath.PATH_SEPARATOR.join(element_path.get_full_path_parts()[:-1])
            parent_model = fs.get_singular_alias(full_path_str, contextual_mode)
        else:
            parent_model = element_path.get_element_name()

        # store values for next cycle
        prev_element_path = element_path
        element_paths.append(element_path)
        i += 1

    if len(element_paths) <= 0:
        raise TrestleError(f'Invalid element path "{element_arg}" without any path separator')

    return element_paths
예제 #28
0
def test_add_failure(tmp_path: pathlib.Path, sample_catalog_minimal: Catalog,
                     keep_cwd: pathlib.Path) -> None:
    """Test Add.add() method failures."""
    content_type = FileContentType.JSON

    _, _ = test_utils.prepare_trestle_project_dir(tmp_path, content_type,
                                                  sample_catalog_minimal,
                                                  test_utils.CATALOGS_DIR)

    element_path = ElementPath('catalog.metadata.*')
    catalog_element = Element(sample_catalog_minimal)

    with pytest.raises(err.TrestleError):
        Add.add(element_path, catalog_element, False)

    element_path = ElementPath('catalog.metadata.title')
    with pytest.raises(err.TrestleError):
        Add.add(element_path, catalog_element, False)

    element_path = ElementPath('catalog.metadata.bad_path')
    with pytest.raises(err.TrestleError):
        Add.add(element_path, catalog_element, False)
예제 #29
0
    def check_split_files():
        assert target_def_dir.joinpath(
            'target-definition/metadata.yaml').exists()
        assert target_def_dir.joinpath('target-definition.yaml').exists()
        assert target_def_dir.joinpath('target-definition/targets').exists()
        assert target_def_dir.joinpath('target-definition/targets').is_dir()

        targets: Dict = Element(sample_target_def).get_at(
            ElementPath('target-definition.targets.*'))
        for uuid in targets:
            target_file = target_def_dir / f'target-definition/targets/{uuid}{const.IDX_SEP}defined-target.yaml'
            assert target_file.exists()

        assert trash.to_trash_file_path(target_def_file).exists()
예제 #30
0
def test_parse_element_args():
    """Unit test parse multiple element args."""
    element_args = ['catalog.metadata', 'catalog.groups', 'catalog.controls']
    p0 = ElementPath('catalog.metadata')
    p1 = ElementPath('catalog.groups')
    p2 = ElementPath('catalog.controls')
    expected_paths: List[ElementPath] = [p0, p1, p2]
    element_paths = cmd_utils.parse_element_args(None, element_args, False)
    assert expected_paths == element_paths

    # element args with wildcard
    element_args = ['catalog.metadata', 'catalog.groups.*.controls.*.controls.*', 'catalog.controls.*.controls.*']
    p0 = ElementPath('catalog.metadata')
    p1 = ElementPath('catalog.groups.*')
    p2 = ElementPath('group.controls.*', parent_path=p1)
    p3 = ElementPath('control.controls.*', parent_path=p2)
    p4 = ElementPath('catalog.controls.*')
    p5 = ElementPath('control.controls.*', parent_path=p4)
    expected: List[ElementPath] = [p0, p1, p2, p3, p4, p5]
    assert cmd_utils.parse_element_args(None, element_args, False) == expected