def test_instance_template_version(template_path: pathlib.Path,
                                   instance_path: pathlib.Path,
                                   status: bool) -> None:
    """Test for x-trestle-template-version header in instances."""
    md_api = MarkdownAPI()
    md_api.load_validator_with_template(template_path, False, True)
    result = md_api.validate_instance(instance_path)
    assert result == status
def test_md_validator_with_md_header(template_path: pathlib.Path,
                                     instance_path: pathlib.Path, status: bool,
                                     yaml_header_validate: bool) -> None:
    """Test with validation of heading."""
    md_api = MarkdownAPI()
    md_api.load_validator_with_template(template_path, yaml_header_validate,
                                        False, 'Governed Document')
    result = md_api.validate_instance(instance_path)
    assert result == status
def test_md_validator_substitutions(template_path: pathlib.Path,
                                    instance_path: pathlib.Path, status: bool,
                                    header_validate: bool,
                                    validate_md_body: bool) -> None:
    """Run markdown validator to expected outcome."""
    md_api = MarkdownAPI()
    md_api.load_validator_with_template(template_path, header_validate,
                                        validate_md_body)
    result = md_api.validate_instance(instance_path)
    assert result == status
def test_validate_for_governed_header(template_path: pathlib.Path,
                                      instance_path: pathlib.Path,
                                      status: bool,
                                      governed_header: str) -> None:
    """Test scenarios for validate w.r.t the governed header."""
    md_api = MarkdownAPI()
    md_api.load_validator_with_template(template_path, False, False,
                                        governed_header)
    result = md_api.validate_instance(instance_path)
    assert result == status
def test_md_by_hand() -> None:
    """Simpler test to enable debugging."""
    template_path = pathlib.Path(
        'tests/data/author/0.0.1/test_3_md_hand_edited/template.md')
    instance_path = pathlib.Path(
        'tests/data/author/0.0.1/test_3_md_hand_edited/decisions_000.md')
    header_validate = False
    status = True
    md_api = MarkdownAPI()
    md_api.load_validator_with_template(template_path, header_validate, False,
                                        'Governed Document')
    result = md_api.validate_instance(instance_path)
    assert result == status
示例#6
0
    def _validate_dir(self, candidate_dir: pathlib.Path, recurse: bool,
                      readme_validate: bool,
                      relative_exclusions: List[pathlib.Path],
                      template_version: str, ignore: str) -> bool:
        """Validate a directory within the trestle project."""
        all_versioned_templates = {}
        instance_version = template_version
        instance_file_names: List[pathlib.Path] = []
        # Fetch all instances versions and build dictionary of required template files
        instances = list(candidate_dir.iterdir())
        if recurse:
            instances = candidate_dir.rglob('*')
            if ignore:
                p = re.compile(ignore)
                instances = list(
                    filter(
                        lambda f: len(
                            list(
                                filter(
                                    p.match,
                                    str(f.relative_to(candidate_dir)).split(
                                        '/')))) == 0, instances))
        for instance_file in instances:
            if not file_utils.is_local_and_visible(instance_file):
                continue
            if instance_file.name.lower(
            ) == 'readme.md' and not readme_validate:
                continue
            if instance_file.is_dir() and not recurse:
                continue
            if any(
                    str(ex) in str(instance_file)
                    for ex in relative_exclusions):
                continue
            if ignore:
                p = re.compile(ignore)
                matched = p.match(instance_file.parts[-1])
                if matched is not None:
                    logger.info(
                        f'Ignoring file {instance_file} from validation.')
                    continue
            instance_file_name = instance_file.relative_to(candidate_dir)
            instance_file_names.append(instance_file_name)
            if instance_file.suffix == '.md':
                md_api = MarkdownAPI()
                versioned_template_dir = None
                if template_version != '':
                    versioned_template_dir = self.template_dir
                else:
                    instance_version = md_api.processor.fetch_value_from_header(
                        instance_file, author_const.TEMPLATE_VERSION_HEADER)
                    if instance_version is None:
                        instance_version = '0.0.1'  # backward compatibility
                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))
                    all_versioned_templates[instance_version] = {}
                    all_versioned_templates[instance_version]['drawio'] = list(
                        filter(lambda p: p.suffix == '.drawio', templates))[0]
                    all_versioned_templates[instance_version]['md'] = list(
                        filter(lambda p: p.suffix == '.md', templates))[0]

                # validate
                md_api.load_validator_with_template(
                    all_versioned_templates[instance_version]['md'], True,
                    False)
                status = md_api.validate_instance(instance_file)
                if not status:
                    logger.info(f'INVALID: {self.rel_dir(instance_file)}')
                    return False
                else:
                    logger.info(f'VALID: {self.rel_dir(instance_file)}')

            elif instance_file.suffix == '.drawio':
                drawio = DrawIO(instance_file)
                metadata = drawio.get_metadata()[0]

                versioned_template_dir = None
                if template_version != '':
                    versioned_template_dir = self.template_dir
                else:
                    if author_const.TEMPLATE_VERSION_HEADER in metadata.keys():
                        instance_version = metadata[
                            author_const.TEMPLATE_VERSION_HEADER]
                    else:
                        instance_version = '0.0.1'  # backward compatibility

                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))
                    all_versioned_templates[instance_version] = {}
                    all_versioned_templates[instance_version]['drawio'] = list(
                        filter(lambda p: p.suffix == '.drawio', templates))[0]
                    all_versioned_templates[instance_version]['md'] = list(
                        filter(lambda p: p.suffix == '.md', templates))[0]

                # validate
                drawio_validator = DrawIOMetadataValidator(
                    all_versioned_templates[instance_version]['drawio'])
                status = drawio_validator.validate(instance_file)
                if not status:
                    logger.info(f'INVALID: {self.rel_dir(instance_file)}')
                    return False
                else:
                    logger.info(f'VALID: {self.rel_dir(instance_file)}')

            else:
                logger.debug(
                    f'Unsupported extension of the instance file: {instance_file}, will not be validated.'
                )

        return True
示例#7
0
    def _validate_dir(self,
                      governed_heading: str,
                      md_dir: pathlib.Path,
                      validate_header: bool,
                      validate_only_header: bool,
                      recurse: bool,
                      readme_validate: bool,
                      template_version: Optional[str] = None,
                      ignore: Optional[str] = None) -> int:
        """
        Validate md files in a directory with option to recurse.

        Template version will be fetched from the instance header.
        """
        # status is a linux returncode
        status = 0
        for item_path in md_dir.iterdir():
            if file_utils.is_local_and_visible(item_path):
                if item_path.is_file():
                    if not item_path.suffix == '.md':
                        logger.info(
                            f'Unexpected file {self.rel_dir(item_path)} in folder {self.rel_dir(md_dir)}, skipping.'
                        )
                        continue
                    if not readme_validate and item_path.name.lower(
                    ) == 'readme.md':
                        continue

                    if ignore:
                        p = re.compile(ignore)
                        matched = p.match(item_path.parts[-1])
                        if matched is not None:
                            logger.info(
                                f'Ignoring file {item_path} from validation.')
                            continue

                    md_api = MarkdownAPI()
                    if template_version != '':
                        template_file = self.template_dir / self.template_name
                    else:
                        instance_version = md_api.processor.fetch_value_from_header(
                            item_path, author_const.TEMPLATE_VERSION_HEADER)
                        if instance_version is None:
                            instance_version = '0.0.1'
                        versione_template_dir = TemplateVersioning.get_versioned_template_dir(
                            self.template_dir, instance_version)
                        template_file = versione_template_dir / self.template_name
                    if not template_file.is_file():
                        raise TrestleError(
                            f'Required template file: {self.rel_dir(template_file)} does not exist. Exiting.'
                        )
                    md_api.load_validator_with_template(
                        template_file, validate_header,
                        not validate_only_header, governed_heading)
                    if not md_api.validate_instance(item_path):
                        logger.info(f'INVALID: {self.rel_dir(item_path)}')
                        status = 1
                    else:
                        logger.info(f'VALID: {self.rel_dir(item_path)}')
                elif recurse:
                    if ignore:
                        p = re.compile(ignore)
                        if len(
                                list(
                                    filter(
                                        p.match,
                                        str(item_path.relative_to(
                                            md_dir)).split('/')))) > 0:
                            logger.info(
                                f'Ignoring directory {item_path} from validation.'
                            )
                            continue
                    rc = self._validate_dir(governed_heading, item_path,
                                            validate_header,
                                            validate_only_header, recurse,
                                            readme_validate, template_version,
                                            ignore)
                    if rc != 0:
                        status = rc

        return status
示例#8
0
    def _measure_template_folder(self, instance_dir: pathlib.Path,
                                 validate_header: bool,
                                 validate_only_header: bool,
                                 governed_heading: str, readme_validate: bool,
                                 template_version: str, ignore: str) -> bool:
        """
        Validate instances against templates.

        Validation will succeed iff:
            1. All template files from the specified version are present in the task
            2. All of the instances are valid
        """
        all_versioned_templates = {}
        instance_version = template_version
        instance_file_names: List[pathlib.Path] = []
        # Fetch all instances versions and build dictionary of required template files
        for instance_file in instance_dir.iterdir():
            if not file_utils.is_local_and_visible(instance_file):
                continue
            if not instance_file.is_file():
                continue
            if instance_file.name.lower(
            ) == 'readme.md' and not readme_validate:
                continue
            if ignore:
                p = re.compile(ignore)
                matched = p.match(instance_file.parts[-1])
                if matched is not None:
                    logger.info(
                        f'Ignoring file {instance_file} from validation.')
                    continue
            instance_file_name = instance_file.relative_to(instance_dir)
            instance_file_names.append(instance_file_name)
            if instance_file.suffix == '.md':
                md_api = MarkdownAPI()
                versioned_template_dir = None
                if template_version != '':
                    template_file = self.template_dir / instance_file_name
                    versioned_template_dir = self.template_dir
                else:
                    instance_version = md_api.processor.fetch_value_from_header(
                        instance_file, author_const.TEMPLATE_VERSION_HEADER)
                    if instance_version is None:
                        instance_version = '0.0.1'  # backward compatibility
                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)
                    template_file = versioned_template_dir / instance_file_name

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))

                    all_versioned_templates[instance_version] = dict.fromkeys([
                        t.relative_to(versioned_template_dir)
                        for t in templates
                    ], False)

                if instance_file_name in all_versioned_templates[
                        instance_version]:
                    # validate
                    md_api.load_validator_with_template(
                        template_file, validate_header,
                        not validate_only_header, governed_heading)
                    status = md_api.validate_instance(instance_file)
                    if not status:
                        logger.warning(
                            f'INVALID: Markdown file {instance_file} failed validation against'
                            + f' {template_file}')
                        return False
                    else:
                        logger.info(f'VALID: {instance_file}')
                    # mark template as present
                    all_versioned_templates[instance_version][
                        instance_file_name] = True

            elif instance_file.suffix == '.drawio':
                drawio = draw_io.DrawIO(instance_file)
                metadata = drawio.get_metadata()[0]
                versioned_template_dir = None
                if template_version != '':
                    template_file = self.template_dir / instance_file_name
                    versioned_template_dir = self.template_dir
                else:
                    if author_const.TEMPLATE_VERSION_HEADER in metadata.keys():
                        instance_version = metadata[
                            author_const.TEMPLATE_VERSION_HEADER]
                    else:
                        instance_version = '0.0.1'  # backward compatibility

                    versioned_template_dir = TemplateVersioning.get_versioned_template_dir(
                        self.template_dir, instance_version)
                    template_file = versioned_template_dir / instance_file_name

                if instance_version not in all_versioned_templates.keys():
                    templates = list(
                        filter(lambda p: file_utils.is_local_and_visible(p),
                               versioned_template_dir.iterdir()))
                    if not readme_validate:
                        templates = list(
                            filter(lambda p: p.name.lower() != 'readme.md',
                                   templates))

                    all_versioned_templates[instance_version] = dict.fromkeys([
                        t.relative_to(versioned_template_dir)
                        for t in templates
                    ], False)

                if instance_file_name in all_versioned_templates[
                        instance_version]:
                    # validate
                    drawio_validator = draw_io.DrawIOMetadataValidator(
                        template_file)
                    status = drawio_validator.validate(instance_file)
                    if not status:
                        logger.warning(
                            f'INVALID: Drawio file {instance_file} failed validation against'
                            + f' {template_file}')
                        return False
                    else:
                        logger.info(f'VALID: {instance_file}')
                    # mark template as present
                    all_versioned_templates[instance_version][
                        instance_file_name] = True

            else:
                logger.debug(
                    f'Unsupported extension of the instance file: {instance_file}, will not be validated.'
                )

        # Check that all template files are present
        for version in all_versioned_templates.keys():
            for template in all_versioned_templates[version]:
                if not all_versioned_templates[version][template]:
                    logger.warning(
                        f'Required template file {template} does not exist in measured instance'
                        + f'{instance_dir}')
                    return False

        return True