From c8714db5a6a2bc97227a7bd7e8a4760e238d8130 Mon Sep 17 00:00:00 2001 From: Shatakshi Mishra Date: Thu, 22 Aug 2024 19:10:06 +0530 Subject: [PATCH] Use project specific name in devfile (#280) * Use unique project specific name in devfile * Fix existing unit tests by mocking the behavior of new method * Refactor logic for lines that were not covered by tests * Add unit test for new method * Code cleanup --- .../resources/common/devfile/devfile.yaml.j2 | 2 +- src/ansible_creator/subcommands/init.py | 17 +++ src/ansible_creator/types.py | 2 + tests/defaults.py | 2 + .../collection/testorg/testcol/devfile.yaml | 2 +- .../project/playbook_project/devfile.yaml | 2 +- tests/units/test_init.py | 114 +++++++++++++++++- 7 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 b/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 index aca20ba8..afb2a31f 100644 --- a/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 +++ b/src/ansible_creator/resources/common/devfile/devfile.yaml.j2 @@ -1,6 +1,6 @@ schemaVersion: 2.2.2 metadata: - name: ansible-demo + name: {{ dev_file_name }} components: - name: tooling-container container: diff --git a/src/ansible_creator/subcommands/init.py b/src/ansible_creator/subcommands/init.py index ec5f3b34..644e8962 100644 --- a/src/ansible_creator/subcommands/init.py +++ b/src/ansible_creator/subcommands/init.py @@ -3,6 +3,7 @@ from __future__ import annotations import shutil +import uuid from pathlib import Path from typing import TYPE_CHECKING @@ -110,6 +111,20 @@ def init_exists(self) -> None: err = f"failed to remove existing directory {self._init_path}: {e}" raise CreatorError(err) from e + def unique_name_in_devfile(self) -> str: + """Use project specific name in devfile. + + Returns: + Unique name entry. + """ + final_name: str + if self._project == "collection": + final_name = f"{self._namespace}.{self._collection_name}" + if self._project == "ansible-project": + final_name = f"{self._scm_org}.{self._scm_project}" + final_uuid = str(uuid.uuid4())[:8] + return f"{final_name}-{final_uuid}" + def _scaffold_collection(self) -> None: """Scaffold a collection project.""" self.output.debug(msg="started copying collection skeleton to destination") @@ -117,6 +132,7 @@ def _scaffold_collection(self) -> None: namespace=self._namespace, collection_name=self._collection_name, creator_version=self._creator_version, + dev_file_name=self.unique_name_in_devfile(), ) copier = Copier( resources=["collection_project", *self.common_resources], @@ -141,6 +157,7 @@ def _scaffold_playbook(self: Init) -> None: creator_version=self._creator_version, scm_org=self._scm_org, scm_project=self._scm_project, + dev_file_name=self.unique_name_in_devfile(), ) copier = Copier( diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index 34535d08..23e735a0 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -22,6 +22,7 @@ class TemplateData: creator_version: The version of the creator. dev_container_image: The devcontainer image. dev_file_image: The devfile image. + dev_file_name: The unique name entry in devfile. namespace: The namespace of the collection. recommended_extensions: A list of recommended VsCode extensions. scm_org: The organization of the source control management. @@ -33,6 +34,7 @@ class TemplateData: creator_version: str = "" dev_container_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_CONTAINER_IMAGE"] dev_file_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_FILE_IMAGE"] + dev_file_name: str = "" namespace: str = "" recommended_extensions: Sequence[str] = field( default_factory=lambda: GLOBAL_TEMPLATE_VARS["RECOMMENDED_EXTENSIONS"], diff --git a/tests/defaults.py b/tests/defaults.py index dfe1faac..7d7c719f 100644 --- a/tests/defaults.py +++ b/tests/defaults.py @@ -6,3 +6,5 @@ FIXTURES_DIR = (Path(__file__).parent / "fixtures").resolve() + +UUID_LENGTH = 8 diff --git a/tests/fixtures/collection/testorg/testcol/devfile.yaml b/tests/fixtures/collection/testorg/testcol/devfile.yaml index 6937a7f0..2312bd2b 100644 --- a/tests/fixtures/collection/testorg/testcol/devfile.yaml +++ b/tests/fixtures/collection/testorg/testcol/devfile.yaml @@ -1,6 +1,6 @@ schemaVersion: 2.2.2 metadata: - name: ansible-demo + name: testorg.testcol components: - name: tooling-container container: diff --git a/tests/fixtures/project/playbook_project/devfile.yaml b/tests/fixtures/project/playbook_project/devfile.yaml index 6937a7f0..ee159979 100644 --- a/tests/fixtures/project/playbook_project/devfile.yaml +++ b/tests/fixtures/project/playbook_project/devfile.yaml @@ -1,6 +1,6 @@ schemaVersion: 2.2.2 metadata: - name: ansible-demo + name: weather.demo components: - name: tooling-container container: diff --git a/tests/units/test_init.py b/tests/units/test_init.py index 692634d8..d9c9ee0a 100644 --- a/tests/units/test_init.py +++ b/tests/units/test_init.py @@ -17,7 +17,7 @@ from ansible_creator.output import Output from ansible_creator.subcommands.init import Init from ansible_creator.utils import TermFeatures -from tests.defaults import FIXTURES_DIR +from tests.defaults import FIXTURES_DIR, UUID_LENGTH class ConfigDict(TypedDict): @@ -51,7 +51,7 @@ def fixture_cli_args(tmp_path: Path, output: Output) -> ConfigDict: """Create a dict to use for a Init class object as fixture. Args: - tmp_path: App configuration object. + tmp_path: Temporary directory path. output: Output class object. Returns: @@ -94,6 +94,7 @@ def test_run_success_for_collection( capsys: pytest.CaptureFixture[str], tmp_path: Path, cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test Init.run(). @@ -101,11 +102,26 @@ def test_run_success_for_collection( capsys: Pytest fixture to capture stdout and stderr. tmp_path: Temporary directory path. cli_args: Dictionary, partial Init class object. + monkeypatch: Pytest monkeypatch fixture. """ cli_args["project"] = "collection" init = Init( Config(**cli_args), ) + + # Mock the "unique_name_in_devfile" method + def mock_unique_name_in_devfile(self: Init) -> str: + coll_namespace = self._namespace + coll_name = self._collection_name + return f"{coll_namespace}.{coll_name}" + + # Apply the mock + monkeypatch.setattr( + Init, + "unique_name_in_devfile", + mock_unique_name_in_devfile, + ) + init.run() result = capsys.readouterr().out @@ -140,6 +156,7 @@ def test_run_success_ansible_project( capsys: pytest.CaptureFixture[str], tmp_path: Path, cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test Init.run(). @@ -149,6 +166,7 @@ def test_run_success_ansible_project( capsys: Pytest fixture to capture stdout and stderr. tmp_path: Temporary directory path. cli_args: Dictionary, partial Init class object. + monkeypatch: Pytest monkeypatch fixture. """ cli_args["collection"] = "" cli_args["project"] = "ansible-project" @@ -158,6 +176,20 @@ def test_run_success_ansible_project( init = Init( Config(**cli_args), ) + + # Mock the "unique_name_in_devfile" method + def mock_unique_name_in_devfile(self: Init) -> str: + coll_namespace = self._scm_org + coll_name = self._scm_project + return f"{coll_namespace}.{coll_name}" + + # Apply the mock + monkeypatch.setattr( + Init, + "unique_name_in_devfile", + mock_unique_name_in_devfile, + ) + init.run() result = capsys.readouterr().out @@ -292,3 +324,81 @@ def test_is_file_error(tmp_path: Path) -> None: with pytest.raises(CreatorError) as exc_info: init.run() assert "but is a file" in str(exc_info.value) + + +@pytest.fixture(name="cli_args_collection") +def fixture_collection_project(tmp_path: Path, output: Output) -> Config: + """Fixture for Config object with collection project. + + Args: + tmp_path: Temporary directory path. + output: Output class object. + + Returns: + Config: Config class object. + """ + return Config( + subcommand="init", + namespace="testns", + collection_name="testname", + init_path=str(tmp_path / "test_path"), + force=False, + creator_version="1.0.0", + project="collection", + scm_org="", + scm_project="", + output=output, + ) + + +@pytest.fixture(name="cli_args_playbook") +def fixture_playbook_project(tmp_path: Path, output: Output) -> Config: + """Fixture for Config object with ansible-project. + + Args: + tmp_path: Temporary directory path. + output: Output class object. + + Returns: + Config: Config class object. + """ + return Config( + subcommand="init", + namespace="", + collection_name="", + init_path=str(tmp_path / "test_path"), + force=False, + creator_version="1.0.0", + project="ansible-project", + scm_org="foo", + scm_project="bar", + output=output, + ) + + +def test_name_in_devfile_collection(cli_args_collection: Config) -> None: + """Test unique_name_in_devfile method for collection project. + + Args: + cli_args_collection: Configuration object for collection project. + """ + init = Init(cli_args_collection) + unique_name = init.unique_name_in_devfile() + assert unique_name.startswith("testns.testname-") + uuid_part = unique_name.split("-")[-1] # Extract the UUID part + assert len(uuid_part) == UUID_LENGTH, "UUID part length mismatch" + + +def test_name_in_devfile_playbook( + cli_args_playbook: Config, +) -> None: + """Test unique_name_in_devfile method for playbook project. + + Args: + cli_args_playbook: Configuration object for playbook project. + """ + init = Init(cli_args_playbook) + unique_name = init.unique_name_in_devfile() + assert unique_name.startswith("foo.bar-") + uuid_part = unique_name.split("-")[-1] # Extract the UUID part + assert len(uuid_part) == UUID_LENGTH, "UUID part length mismatch"