コード例 #1
0
    def setup_template_governed_docs(self, template_version: str) -> int:
        """Create structure to allow markdown template enforcement.

        Returns:
            Unix return code.
        """
        if not self.task_path.exists():
            self.task_path.mkdir(exist_ok=True, parents=True)
        elif self.task_path.is_file():
            raise TrestleError(
                f'Task path: {self.rel_dir(self.task_path)} is a file not a directory.'
            )
        if not self.template_dir.exists():
            self.template_dir.mkdir(exist_ok=True, parents=True)
        elif self.template_dir.is_file():
            raise TrestleError(
                f'Template path: {self.rel_dir(self.template_dir)} is a file not a directory.'
            )
        logger.debug(self.template_dir)
        if not self._validate_template_dir():
            raise TrestleError('Aborting setup')
        template_file = self.template_dir / self.template_name
        if template_file.is_file():
            return CmdReturnCodes.SUCCESS.value
        TemplateVersioning.write_versioned_template('template.md',
                                                    self.template_dir,
                                                    template_file,
                                                    template_version)
        logger.info(
            f'Template file setup for task {self.task_name} at {self.rel_dir(template_file)}'
        )
        logger.info(f'Task directory is {self.rel_dir(self.task_path)}')
        return CmdReturnCodes.SUCCESS.value
コード例 #2
0
    def setup(self, template_version: str) -> int:
        """Create template directory and templates."""
        # Step 1 - validation

        if self.task_name and not self.task_path.exists():
            self.task_path.mkdir(exist_ok=True, parents=True)
        elif self.task_name and self.task_path.is_file():
            raise TrestleError(
                f'Task path: {self.rel_dir(self.task_path)} is a file not a directory.'
            )

        if not self.template_dir.exists():
            self.template_dir.mkdir(exist_ok=True, parents=True)
        logger.info(
            f'Populating template files to {self.rel_dir(self.template_dir)}')
        for template in author_const.REFERENCE_TEMPLATES.values():
            destination_path = self.template_dir / template
            TemplateVersioning.write_versioned_template(
                template, self.template_dir, destination_path,
                template_version)

            logger.info(
                f'Template directory populated {self.rel_dir(destination_path)}'
            )
        return CmdReturnCodes.SUCCESS.value
コード例 #3
0
def test_template_versioning(tmp_path: pathlib.Path) -> None:
    """Full on testing of updating the template folder structure and retrieving it back."""
    task_path = tmp_path.joinpath('trestle/author/sample_task/')

    task_path.mkdir(parents=True)
    task_path.joinpath('0.0.1').mkdir(parents=True)
    task_path.joinpath('0.0.2').mkdir(parents=True)
    template = task_path.joinpath('0.0.2/template.md')
    template2 = task_path.joinpath('0.0.2/template2.md')

    old_template = task_path.joinpath('old_template.md')
    old_template.touch()

    TemplateVersioning.update_template_folder_structure(task_path)
    TemplateVersioning.write_versioned_template('template.md',
                                                task_path.joinpath('0.0.2'),
                                                template, '0.0.2')
    TemplateVersioning.write_versioned_template('template.md',
                                                task_path.joinpath('0.0.2'),
                                                template2, None)

    v1_dir = TemplateVersioning.get_versioned_template_dir(
        task_path, START_TEMPLATE_VERSION)
    latest_dir, version = TemplateVersioning.get_latest_version_for_task(
        task_path)
    v2_dir = TemplateVersioning.get_versioned_template_dir(task_path, '0.0.2')

    assert not old_template.exists()
    assert task_path.joinpath(START_TEMPLATE_VERSION).joinpath(
        'old_template.md').exists()
    assert task_path.joinpath('0.0.2').joinpath('template.md').exists()
    assert task_path.joinpath('0.0.2').joinpath('template2.md').exists()
    assert v1_dir.exists()
    assert v2_dir == latest_dir
    assert version == '0.0.2'
コード例 #4
0
def test_get_versioned_template(tmp_path: pathlib.Path) -> None:
    """Test get template of the specified version."""
    task_path = tmp_path.joinpath('trestle/author/sample_task/')

    with pytest.raises(TrestleError):
        TemplateVersioning.get_versioned_template_dir(task_path)

    task_path.mkdir(parents=True)

    with pytest.raises(TrestleError):
        TemplateVersioning.get_versioned_template_dir(task_path)

    v1_dir = task_path.joinpath(START_TEMPLATE_VERSION)
    v2_dir = task_path.joinpath('0.1.5/')
    v3_dir = task_path.joinpath('10.02.1/')
    v1_dir.mkdir(parents=True)
    v2_dir.mkdir(parents=True)
    v3_dir.mkdir(parents=True)

    first_version = TemplateVersioning.get_versioned_template_dir(
        task_path, START_TEMPLATE_VERSION)
    assert first_version == v1_dir

    latest_version = TemplateVersioning.get_versioned_template_dir(task_path)
    assert latest_version == v3_dir

    second_version = TemplateVersioning.get_versioned_template_dir(
        task_path, '0.1.5')
    assert second_version == v2_dir

    with pytest.raises(TrestleError):
        TemplateVersioning.get_versioned_template_dir(task_path, '6.7.8')
コード例 #5
0
ファイル: common.py プロジェクト: IBM/compliance-trestle
    def _setup_template_dir(self, args: argparse.Namespace) -> int:
        """Set template directory and update to new format."""
        if not self.global_ and self.task_name is None:
            logger.error(
                'At least a global flag or a task name should be provided.')
            return CmdReturnCodes.INCORRECT_ARGS.value
        if self.global_:
            old_template_dir = self.trestle_root / TRESTLE_CONFIG_DIR / 'author' / '__global__'
            self._set_template_version_to_latest(args, old_template_dir)
            self.template_dir = old_template_dir / args.template_version
        elif self.task_name and not self.global_:
            old_template_dir = self.trestle_root / TRESTLE_CONFIG_DIR / 'author' / self.task_name
            self._set_template_version_to_latest(args, old_template_dir)
            self.template_dir = old_template_dir / args.template_version

        if old_template_dir.exists():
            TemplateVersioning.validate_template_folder(old_template_dir)
            TemplateVersioning.update_template_folder_structure(
                old_template_dir)

        return CmdReturnCodes.SUCCESS.value
コード例 #6
0
ファイル: folders.py プロジェクト: IBM/compliance-trestle
    def setup_template(self, template_version: str) -> int:
        """Create structure to allow markdown template enforcement."""
        if not self.task_path.exists():
            self.task_path.mkdir(exist_ok=True, parents=True)
        elif self.task_path.is_file():
            raise TrestleError(
                f'Task path: {self.rel_dir(self.task_path)} is a file not a directory.'
            )
        if not self.template_dir.exists():
            self.template_dir.mkdir(exist_ok=True, parents=True)
        elif self.template_dir.is_file():
            raise TrestleError(
                f'Template path: {self.rel_dir(self.template_dir)} is a file not a directory.'
            )

        template_file_a_md = self.template_dir / 'a_template.md'
        template_file_another_md = self.template_dir / 'another_template.md'
        template_file_drawio = self.template_dir / 'architecture.drawio'
        TemplateVersioning.write_versioned_template('template.md',
                                                    self.template_dir,
                                                    template_file_a_md,
                                                    template_version)
        TemplateVersioning.write_versioned_template('template.md',
                                                    self.template_dir,
                                                    template_file_another_md,
                                                    template_version)
        TemplateVersioning.write_versioned_template('template.drawio',
                                                    self.template_dir,
                                                    template_file_drawio,
                                                    template_version)

        return CmdReturnCodes.SUCCESS.value
コード例 #7
0
def test_valid_version() -> None:
    """Test is valid verion."""
    assert TemplateVersioning.is_valid_version('0.0.1')
    assert not TemplateVersioning.is_valid_version("'0.0.1'")
    assert not TemplateVersioning.is_valid_version('not_valid.0.0.1')
    assert not TemplateVersioning.is_valid_version('0.1')
    assert not TemplateVersioning.is_valid_version('1')
    assert not TemplateVersioning.is_valid_version('0.0.0.1')
コード例 #8
0
ファイル: common.py プロジェクト: IBM/compliance-trestle
 def _set_template_version_to_latest(self, args: argparse.Namespace,
                                     template_dir: pathlib.Path):
     """Set template version argument to the latest version if none was given."""
     if not TemplateVersioning.is_valid_version(args.template_version):
         raise TrestleError(
             f'Version {args.template_version} is invalid, version format should be: 0.0.1'
         )
     if args.template_version is None and args.mode == ARG_VALIDATE:
         # in validate mode no version will validate instances based on header version
         args.template_version = ''
     if args.template_version is None:
         args.template_version = START_TEMPLATE_VERSION
         if template_dir.exists():
             all_versions = TemplateVersioning.get_all_versions_for_task(
                 template_dir)
             if all_versions:
                 args.template_version = max(all_versions)
     if args.template_version == '':
         logger.info(
             'Instances will be validated against template version specified in their headers.'
         )
     else:
         logger.info(f'Set template version to {args.template_version}.')
コード例 #9
0
def test_template_version_folder_update(tmp_path: pathlib.Path) -> None:
    """Test template folder update from old structure to new."""
    task_path = tmp_path.joinpath('trestle/author/sample_task/')
    task_path.mkdir(parents=True)

    old_template = task_path.joinpath('template.md')
    old_template.touch()

    with pytest.raises(TrestleError):
        TemplateVersioning.update_template_folder_structure(old_template)

    # make sure file gets put to first version
    TemplateVersioning.update_template_folder_structure(task_path)

    assert task_path.joinpath('0.0.1/').joinpath('template.md').exists()
    assert not old_template.exists()

    # make sure new file also gets put to the first version
    another_template = task_path.joinpath('missplaced_template.md')
    another_template.touch()

    TemplateVersioning.update_template_folder_structure(task_path)
    assert task_path.joinpath('0.0.1/').joinpath('template.md').exists()
    assert task_path.joinpath('0.0.1/').joinpath(
        'missplaced_template.md').exists()
    assert not another_template.exists()

    # make sure other versions get no modifications
    v2_dir = task_path.joinpath('11.12.113')
    v2_dir.mkdir()

    yet_another_template = task_path.joinpath('new_template.md')
    yet_another_template.touch()
    TemplateVersioning.update_template_folder_structure(task_path)

    assert len(list(v2_dir.iterdir())) == 0
    assert task_path.joinpath('0.0.1/').joinpath('template.md').exists()
    assert task_path.joinpath('0.0.1/').joinpath(
        'missplaced_template.md').exists()
    assert task_path.joinpath('0.0.1/').joinpath('new_template.md').exists()
    assert not yet_another_template.exists()
コード例 #10
0
def test_get_latest_version(tmp_path: pathlib.Path) -> None:
    """Test get latest version of template."""
    task_path = tmp_path.joinpath('trestle/author/sample_task/')

    with pytest.raises(TrestleError):
        TemplateVersioning.get_latest_version_for_task(task_path)

    task_path.mkdir(parents=True)

    latest_path, version = TemplateVersioning.get_latest_version_for_task(
        task_path)
    assert latest_path == task_path.joinpath('0.0.1')
    assert version == '0.0.1'

    v1_dir = task_path.joinpath('0.0.1/')
    v2_dir = task_path.joinpath('11.10.1234/')
    v1_dir.mkdir(parents=True)
    v2_dir.mkdir(parents=True)

    latest_path, version = TemplateVersioning.get_latest_version_for_task(
        task_path)
    assert latest_path == v2_dir
    assert version == '11.10.1234'

    template_v1 = v1_dir.joinpath('template.md')
    template_v2 = v2_dir.joinpath('template.md')
    template_v1.touch()
    template_v2.touch()

    latest_path, version = TemplateVersioning.get_latest_version_for_task(
        task_path)
    assert latest_path == v2_dir
    assert version == '11.10.1234'

    with pytest.raises(TrestleError):
        TemplateVersioning.get_latest_version_for_task(template_v1)
コード例 #11
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
コード例 #12
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
コード例 #13
0
ファイル: folders.py プロジェクト: IBM/compliance-trestle
    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
コード例 #14
0
def test_write_versioned_template(tmp_path: pathlib.Path) -> None:
    """Test writing a template to the folder."""
    task_path = tmp_path.joinpath('trestle/author/sample_task/')

    with pytest.raises(TrestleError):
        TemplateVersioning.get_versioned_template_dir(task_path)

    task_path.mkdir(parents=True)
    tmp_path_01 = task_path.joinpath('0.0.1')
    tmp_path_02 = task_path.joinpath('0.0.2')
    tmp_path_01.mkdir(parents=True)
    tmp_path_02.mkdir(parents=True)
    template = tmp_path_01.joinpath('template.md')

    TemplateVersioning.write_versioned_template('template.md', tmp_path_01,
                                                template, None)

    assert task_path.joinpath(START_TEMPLATE_VERSION).joinpath(
        'template.md').exists()
    assert template.exists()

    template2 = tmp_path_01.joinpath('template2.md')
    template3 = tmp_path_02.joinpath('template3.md')
    template4 = tmp_path_02.joinpath('template4.md')
    TemplateVersioning.write_versioned_template('template.md', tmp_path_01,
                                                template2, '0.0.1')
    TemplateVersioning.write_versioned_template('template.md', tmp_path_02,
                                                template3, '0.0.2')
    TemplateVersioning.write_versioned_template('template.md', tmp_path_02,
                                                template4, None)

    assert tmp_path_01.joinpath('template.md').exists()
    assert tmp_path_01.joinpath('template2.md').exists()
    assert tmp_path_02.joinpath('template3.md').exists()
    assert tmp_path_02.joinpath('template4.md').exists()
    assert template.exists()

    md_api = MarkdownAPI()
    header, _ = md_api.processor.read_markdown_wo_processing(
        tmp_path_02.joinpath('template3.md'))

    assert header[TEMPLATE_VERSION_HEADER] == '0.0.2'

    template_drawio = tmp_path_02.joinpath('template.drawio')
    TemplateVersioning.write_versioned_template('template.drawio', tmp_path_02,
                                                template_drawio, '0.0.2')

    drawio = DrawIO(template_drawio)
    metadata = drawio.get_metadata()[0]

    assert metadata[TEMPLATE_VERSION_HEADER] == '0.0.2'