From 0f179303f0bd363857972caf73c29d61bf7e928c Mon Sep 17 00:00:00 2001 From: Noah Canadea Date: Fri, 1 Dec 2023 10:00:43 +0000 Subject: [PATCH 1/4] feat(unit_tests): Add some unit tests. --- .../test_versioned_terraform_resource.py | 17 +- .../models/versioned_terraform_resources.py | 6 + .../core/utils/terraform/hcl_edit_cli.py | 9 +- .../core/utils/terraform/hcl_handler.py | 4 +- .../terraform/tests/test_hcl_edit_cli.py | 84 +++++++ .../utils/terraform/tests/test_hcl_handler.py | 217 ++++++++++++++++++ 6 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 infrapatch/core/utils/terraform/tests/test_hcl_edit_cli.py create mode 100644 infrapatch/core/utils/terraform/tests/test_hcl_handler.py diff --git a/infrapatch/core/models/tests/test_versioned_terraform_resource.py b/infrapatch/core/models/tests/test_versioned_terraform_resource.py index a4b568b..116a398 100644 --- a/infrapatch/core/models/tests/test_versioned_terraform_resource.py +++ b/infrapatch/core/models/tests/test_versioned_terraform_resource.py @@ -16,6 +16,13 @@ def test_attributes(): assert provider.base_domain is None assert provider.identifier == "test_provider/test_provider" + # test code source + module.code_source = "testing/module/test_module" + provider.code_source = "https://github.com/hashicorp/terraform-provider-test" + + assert module.is_github_hosted() is False + assert provider.is_github_hosted() is True + # test with custom registry module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="testregistry.ch/test/test_module/test_provider") provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="testregistry.ch/test_provider/test_provider") @@ -55,7 +62,13 @@ def test_find(): def test_to_dict(): module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") + provider = TerraformProvider( + name="test_resource", + current_version="1.0.0", + _source_file="test_file.py", + _source="test_provider/test_provider", + code_source="github.com/hashicorp/terraform-provider-test", + ) module_dict = module.to_dict() provider_dict = provider.to_dict() @@ -69,6 +82,7 @@ def test_to_dict(): "_source": "test/test_module/test_provider", "_base_domain": None, "_identifier": "test/test_module/test_provider", + "code_source": None, } assert provider_dict == { "name": "test_resource", @@ -79,4 +93,5 @@ def test_to_dict(): "_source": "test_provider/test_provider", "_base_domain": None, "_identifier": "test_provider/test_provider", + "code_source": "github.com/hashicorp/terraform-provider-test", } diff --git a/infrapatch/core/models/versioned_terraform_resources.py b/infrapatch/core/models/versioned_terraform_resources.py index 1882db0..b233116 100644 --- a/infrapatch/core/models/versioned_terraform_resources.py +++ b/infrapatch/core/models/versioned_terraform_resources.py @@ -11,6 +11,7 @@ class VersionedTerraformResource(VersionedResource): _base_domain: Union[str, None] = None _identifier: Union[str, None] = None _source: Union[str, None] = None + code_source: Union[str, None] = None @property def source(self) -> Union[str, None]: @@ -32,6 +33,11 @@ def find(self, resources): filtered_resources = super().find(resources) return [resource for resource in filtered_resources if resource._source == self._source] + def is_github_hosted(self) -> bool: + if self.code_source is None: + return False + return self.code_source.lower().startswith("https://github.com") + @dataclass class TerraformModule(VersionedTerraformResource): diff --git a/infrapatch/core/utils/terraform/hcl_edit_cli.py b/infrapatch/core/utils/terraform/hcl_edit_cli.py index 017bd8d..989257a 100644 --- a/infrapatch/core/utils/terraform/hcl_edit_cli.py +++ b/infrapatch/core/utils/terraform/hcl_edit_cli.py @@ -39,10 +39,13 @@ def update_hcl_value(self, resource: str, file: Path, value: str): self._run_hcl_edit_command("update", resource, file, value) def get_hcl_value(self, resource: str, file: Path) -> str: - result = self._run_hcl_edit_command("get", resource, file) - if result is None: + result = self._run_hcl_edit_command("read", resource, file) + if result is None or result == "": raise HclEditCliException(f"Could not get value for resource '{resource}' from file '{file}'.") - return result + resource_id, value = result.split(" ") + if resource_id != resource: + raise HclEditCliException(f"Could not get value for resource '{resource}' from file '{file}'.") + return value def _run_hcl_edit_command(self, action: str, resource: str, file: Path, value: Union[str, None] = None) -> Optional[str]: command = [self._binary_path.absolute().as_posix(), action, resource] diff --git a/infrapatch/core/utils/terraform/hcl_handler.py b/infrapatch/core/utils/terraform/hcl_handler.py index 88efb9f..133ad1f 100644 --- a/infrapatch/core/utils/terraform/hcl_handler.py +++ b/infrapatch/core/utils/terraform/hcl_handler.py @@ -7,7 +7,7 @@ import pygohcl from infrapatch.core.models.versioned_terraform_resources import TerraformModule, TerraformProvider, VersionedTerraformResource -from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCli +from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCli, HclEditCliInterface class HclParserException(Exception): @@ -29,7 +29,7 @@ def get_credentials_form_user_rc_file(self) -> dict[str, str]: class HclHandler(HclHandlerInterface): - def __init__(self, hcl_edit_cli: HclEditCli): + def __init__(self, hcl_edit_cli: HclEditCliInterface): self.hcl_edit_cli = hcl_edit_cli pass diff --git a/infrapatch/core/utils/terraform/tests/test_hcl_edit_cli.py b/infrapatch/core/utils/terraform/tests/test_hcl_edit_cli.py new file mode 100644 index 0000000..0e74cac --- /dev/null +++ b/infrapatch/core/utils/terraform/tests/test_hcl_edit_cli.py @@ -0,0 +1,84 @@ +from pathlib import Path +from unittest.mock import patch + +import pytest + +from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCli, HclEditCliException + + +@pytest.fixture +def hcl_edit_cli(): + return HclEditCli() + + +def test_init_with_existing_binary_path(hcl_edit_cli): + assert hcl_edit_cli._binary_path.exists() + + +def test_get_binary_path_windows(): + with patch("platform.system", return_value="Windows"): + hcl_edit_cli = HclEditCli() + assert hcl_edit_cli._get_binary_path().name == "hcledit_windows.exe" + + +def test_get_binary_path_linux(): + with patch("platform.system", return_value="Linux"): + hcl_edit_cli = HclEditCli() + assert hcl_edit_cli._get_binary_path().name == "hcledit_linux" + + +def test_get_binary_path_darwin(): + with patch("platform.system", return_value="Darwin"): + hcl_edit_cli = HclEditCli() + assert hcl_edit_cli._get_binary_path().name == "hcledit_darwin" + + +def test_get_binary_path_unsupported_platform(): + with patch("platform.system", return_value="Unsupported"): + with pytest.raises(Exception): + HclEditCli() + + +def test_update_hcl_value(hcl_edit_cli, tmp_path): + file_path = tmp_path / "test_file.hcl" + file_path.write_text('resource "test_resource" {\n value = "old_value"\n}') + + hcl_edit_cli.update_hcl_value("resource.test_resource.value", file_path, "new_value") + + assert file_path.read_text() == 'resource "test_resource" {\n value = "new_value"\n}' + + +def test_get_hcl_value(hcl_edit_cli, tmp_path): + file_path = tmp_path / "test_file.hcl" + file_path.write_text('resource "test_resource" {\n value = "test_value"\n}') + + value = hcl_edit_cli.get_hcl_value("resource.test_resource.value", file_path) + + assert value == "test_value" + + +def test_get_hcl_value_non_existing_resource(hcl_edit_cli, tmp_path): + file_path = tmp_path / "test_file.hcl" + file_path.write_text('resource "test_resource" {\n value = "test_value"\n}') + + with pytest.raises(HclEditCliException): + hcl_edit_cli.get_hcl_value("non_existing_resource.value", file_path) + + +def test_run_hcl_edit_command_success(hcl_edit_cli): + with patch("subprocess.run") as mock_run: + mock_run.return_value.returncode = 0 + mock_run.return_value.stdout = "command_output" + + result = hcl_edit_cli._run_hcl_edit_command("get", "test_resource.value", Path("test_file.hcl")) + + assert result == "command_output" + + +def test_run_hcl_edit_command_failure(hcl_edit_cli): + with patch("subprocess.run") as mock_run: + mock_run.return_value.returncode = 1 + mock_run.return_value.stdout = "command_output" + + with pytest.raises(HclEditCliException): + hcl_edit_cli._run_hcl_edit_command("get", "test_resource.value", Path("test_file.hcl")) diff --git a/infrapatch/core/utils/terraform/tests/test_hcl_handler.py b/infrapatch/core/utils/terraform/tests/test_hcl_handler.py new file mode 100644 index 0000000..566864f --- /dev/null +++ b/infrapatch/core/utils/terraform/tests/test_hcl_handler.py @@ -0,0 +1,217 @@ +from pathlib import Path +from unittest.mock import patch + +import pytest + +from infrapatch.core.models.versioned_terraform_resources import TerraformModule, TerraformProvider +from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCli +from infrapatch.core.utils.terraform.hcl_handler import HclHandler, HclParserException + + +@pytest.fixture +def tmp_user_home(tmp_path: Path): + return tmp_path + + +@pytest.fixture +def hcl_handler(tmp_user_home: Path): + return HclHandler(hcl_edit_cli=HclEditCli()) + + +@pytest.fixture +def valid_terraform_code(): + return """ + terraform { + required_providers { + test_provider = { + source = "test_provider/test_provider" + version = ">1.0.0" + } + test_provider2 = { + source = "spacelift.io/test_provider/test_provider2" + version = "1.0.5" + } + } + } + module "test_module" { + source = "test/test_module/test_provider" + version = "2.0.0" + name = "Test_module" + } + module "test_module2" { + source = "spacelift.io/test/test_module/test_provider" + version = "1.0.2" + name = "Test_module2" + } + # This module should be ignored since it has no version + module "test_module3" { + source = "C:/test/test_module/test_provider" + name = "Test_module3" + } + """ + + +@pytest.fixture +def invalid_terraform_code(): + return """ + terraform { + required_providers { + test_provider = { + source = "test_provider/test_provider" + version = ">1.0.0" + } + test_provider = { + source = "spacelift.io/test_provider/test_provider2" + version = "1.0.5 + } + } + } + module "test_module" { + source = "test/test_module/test_provider" + version = "2.0.0" + name = Test_module" + } + } + """ + + +def test_get_terraform_resources_from_file(hcl_handler: HclHandler, valid_terraform_code: str, tmp_path: Path): + # Create a temporary Terraform file for testing + tf_file = tmp_path.joinpath("test_file.tf") + tf_file.write_text(valid_terraform_code) + resouces = hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=True, get_providers=True) + modules = hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=True, get_providers=False) + providers = hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=False, get_providers=True) + + modules_filtered = [resource for resource in resouces if isinstance(resource, TerraformModule)] + providers_filtered = [resource for resource in resouces if isinstance(resource, TerraformProvider)] + + assert len(resouces) == 4 + assert len(modules) == 2 + assert len(providers) == 2 + assert len(modules_filtered) == len(modules) + assert len(providers_filtered) == len(providers) + + for resource in resouces: + assert resource._source_file == tf_file.absolute().as_posix() + if resource.name == "test_module": + assert isinstance(resource, TerraformModule) + assert resource.current_version == "2.0.0" + assert resource.source == "test/test_module/test_provider" + assert resource.identifier == "test/test_module/test_provider" + assert resource.base_domain is None + elif resource.name == "test_module2": + assert isinstance(resource, TerraformModule) + assert resource.current_version == "1.0.2" + assert resource.source == "spacelift.io/test/test_module/test_provider" + assert resource.identifier == "test/test_module/test_provider" + assert resource.base_domain == "spacelift.io" + elif resource.name == "test_provider": + assert isinstance(resource, TerraformProvider) + assert resource.current_version == ">1.0.0" + assert resource.source == "test_provider/test_provider" + assert resource.identifier == "test_provider/test_provider" + assert resource.base_domain is None + elif resource.name == "test_provider2": + assert isinstance(resource, TerraformProvider) + assert resource.current_version == "1.0.5" + assert resource.source == "spacelift.io/test_provider/test_provider2" + assert resource.identifier == "test_provider/test_provider2" + assert resource.base_domain == "spacelift.io" + else: + raise Exception(f"Unknown resource '{resource.name}'.") + + +def test_invalid_terraform_code_parse_error(hcl_handler: HclHandler, invalid_terraform_code: str, tmp_path: Path): + # Create a temporary Terraform file for testing + tf_file = tmp_path.joinpath("test_file.tf") + tf_file.write_text(invalid_terraform_code) + with pytest.raises(HclParserException): + hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=True, get_providers=True) + + +def test_bump_resource_version(hcl_handler, valid_terraform_code: str, tmp_path: Path): + # Create a TerraformModule resource for testing + tf_file = tmp_path.joinpath("test_file.tf") + tf_file.write_text(valid_terraform_code) + resouces = hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=True, get_providers=True) + + # bump versions + for resource in resouces: + if resource.name == "test_module": + resource.newest_version = "4.0.1" + elif resource.name == "test_module2": + resource.newest_version = "4.0.2" + + elif resource.name == "test_provider": + resource.newest_version = "4.0.3" + elif resource.name == "test_provider2": + resource.newest_version = "4.0.4" + hcl_handler.bump_resource_version(resource) + + resouces = hcl_handler.get_terraform_resources_from_file(tf_file, get_modules=True, get_providers=True) + # check if versions are bumped + for resource in resouces: + if resource.name == "test_module": + assert resource.current_version == "4.0.1" + elif resource.name == "test_module2": + assert resource.current_version == "4.0.2" + elif resource.name == "test_provider": + assert resource.current_version == ">1.0.0" # should not be bumped since it defines newer version + elif resource.name == "test_provider2": + assert resource.current_version == "4.0.4" + + +def test_get_all_terraform_files(hcl_handler): + # Create a temporary directory with Terraform files for testing + root_dir = Path("test_dir") + root_dir.mkdir() + tf_file1 = root_dir / "file1.tf" + tf_file1.touch() + tf_file2 = root_dir / "file2.tf" + tf_file2.touch() + + # Test getting all Terraform files in the directory + files = hcl_handler.get_all_terraform_files(root_dir) + assert len(files) == 2 + assert tf_file1 in files + assert tf_file2 in files + + # Clean up the temporary directory + tf_file1.unlink() + tf_file2.unlink() + root_dir.rmdir() + + +def test_get_credentials_form_user_rc_file(hcl_handler, tmp_user_home: Path): + # Create a temporary terraformrc file for testing + + # Create an instance of HclHandler with a mock HclEditCli + with patch("pathlib.Path.home", return_value=tmp_user_home): + # test without file + credentials = hcl_handler.get_credentials_form_user_rc_file() + assert len(credentials) == 0 + + # Create a temporary terraformrc file for testing + terraform_rc_file = tmp_user_home.joinpath(".terraformrc") + terraform_rc_file.write_text( + """ + credentials { + test1 = { + token = "token1" + } + test2 = { + token = "token2" + } + } + """ + ) + + # Test getting credentials from the terraformrc file + credentials = hcl_handler.get_credentials_form_user_rc_file() + assert len(credentials) == 2 + assert credentials["test1"] == "token1" + assert credentials["test2"] == "token2" + + # Clean up the temporary file + terraform_rc_file.unlink() From 9b786af87205f573054bdde08e3602bb5f9cfb8c Mon Sep 17 00:00:00 2001 From: Noah Canadea Date: Fri, 1 Dec 2023 14:23:46 +0000 Subject: [PATCH 2/4] feat(dev_container): Add devcontainer extension to recommendations --- .vscode/extensions.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..87749a8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode-remote.remote-containers" + ] +} \ No newline at end of file From 058907100e4ad1bf0f13990325b158d6f543b449 Mon Sep 17 00:00:00 2001 From: Noah Canadea Date: Fri, 1 Dec 2023 14:24:29 +0000 Subject: [PATCH 3/4] doc(README): Add chapter for dev environment and contribution. --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index db64743..7dd3995 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ The CLI works by scanning your .tf files for versioned providers and modules and - [Authentication](#authentication-1) - [.terraformrc file:](#terraformrc-file) - [infrapatch\_credentials.json file:](#infrapatch_credentialsjson-file) + - [Setup Development Environment for InfraPatch](#setup-development-environment-for-infrapatch) + - [Contributing](#contributing) ## GitHub Action @@ -182,3 +184,16 @@ You can also specify the path to the credentials file with the `--credentials-fi infrapatch --credentials-file-path "path/to/credentials/file" update ``` +### Setup Development Environment for InfraPatch + +This repository contains a devcontainer configuration for VSCode. To use it, you need to install the following tools: +* ["Dev Containers VSCode Extension"](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) for VSCode. +* A local Docker installation like [Docker Desktop](https://www.docker.com/products/docker-desktop). + +After installation, you can open the repository in the devcontainer by clicking on the green "Open in Container" button in the bottom left corner of VSCode. +During the first start, the devcontainer will build the container image and install all dependencies. + +### Contributing + +If you have any ideas for improvements or find any bugs, feel free to open an issue or create a pull request. + From 6df899539ce23444a1982934540800be7ee605c8 Mon Sep 17 00:00:00 2001 From: Noah Canadea Date: Tue, 5 Dec 2023 07:18:39 +0000 Subject: [PATCH 4/4] refac(versioned_terraofrm_resource): Remove no longer needed attributes. --- .../test_versioned_terraform_resource.py | 8 - .../models/versioned_terraform_resources.py | 5 - .../core/utils/terraform/hcl_handler.py | 2 +- .../terraform/tests/test_registry_handler.py | 193 ------------------ 4 files changed, 1 insertion(+), 207 deletions(-) delete mode 100644 infrapatch/core/utils/terraform/tests/test_registry_handler.py diff --git a/infrapatch/core/models/tests/test_versioned_terraform_resource.py b/infrapatch/core/models/tests/test_versioned_terraform_resource.py index 4871606..bc40976 100644 --- a/infrapatch/core/models/tests/test_versioned_terraform_resource.py +++ b/infrapatch/core/models/tests/test_versioned_terraform_resource.py @@ -16,13 +16,6 @@ def test_attributes(): assert provider.base_domain is None assert provider.identifier == "test_provider/test_provider" - # test code source - module.code_source = "testing/module/test_module" - provider.code_source = "https://github.com/hashicorp/terraform-provider-test" - - assert module.is_github_hosted() is False - assert provider.is_github_hosted() is True - # test with custom registry module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="testregistry.ch/test/test_module/test_provider") provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="testregistry.ch/test_provider/test_provider") @@ -67,7 +60,6 @@ def test_to_dict(): current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider", - code_source="github.com/hashicorp/terraform-provider-test", ) module_dict = module.to_dict() diff --git a/infrapatch/core/models/versioned_terraform_resources.py b/infrapatch/core/models/versioned_terraform_resources.py index 57b0bac..cc3b5c7 100644 --- a/infrapatch/core/models/versioned_terraform_resources.py +++ b/infrapatch/core/models/versioned_terraform_resources.py @@ -34,11 +34,6 @@ def find(self, resources): filtered_resources = super().find(resources) return [resource for resource in filtered_resources if resource._source == self._source] - def is_github_hosted(self) -> bool: - if self.code_source is None: - return False - return self.code_source.lower().startswith("https://github.com") - @dataclass class TerraformModule(VersionedTerraformResource): diff --git a/infrapatch/core/utils/terraform/hcl_handler.py b/infrapatch/core/utils/terraform/hcl_handler.py index 133ad1f..a0ffaae 100644 --- a/infrapatch/core/utils/terraform/hcl_handler.py +++ b/infrapatch/core/utils/terraform/hcl_handler.py @@ -7,7 +7,7 @@ import pygohcl from infrapatch.core.models.versioned_terraform_resources import TerraformModule, TerraformProvider, VersionedTerraformResource -from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCli, HclEditCliInterface +from infrapatch.core.utils.terraform.hcl_edit_cli import HclEditCliInterface class HclParserException(Exception): diff --git a/infrapatch/core/utils/terraform/tests/test_registry_handler.py b/infrapatch/core/utils/terraform/tests/test_registry_handler.py deleted file mode 100644 index 8999259..0000000 --- a/infrapatch/core/utils/terraform/tests/test_registry_handler.py +++ /dev/null @@ -1,193 +0,0 @@ -import pytest -from unittest.mock import patch, MagicMock - -from infrapatch.core.models.versioned_terraform_resources import TerraformModule, TerraformProvider -from infrapatch.core.utils.terraform.registry_handler import RegistryHandler, ResourceNotFoundException, RegistryMetadataException - - -@pytest.fixture -def registry_handler(): - default_registry_domain = "testregistry.ch" - credentials = {"testregistry.ch": "test_token"} - return RegistryHandler(default_registry_domain, credentials) - - -def test_get_newest_version_module(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_module_version = {"test/test_module/test_provider": "1.0.0"} - - newest_version = registry_handler.get_newest_version(module) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_not_called() - - -def test_get_newest_version_provider(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_provider_version = {"test_provider/test_provider": "1.0.0"} - - newest_version = registry_handler.get_newest_version(provider) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_not_called() - - -def test_get_newest_version_invalid_resource(registry_handler): - resource = MagicMock() - with pytest.raises(Exception, match=r"Resource type '' is not supported."): - registry_handler.get_newest_version(resource) - - -def test_get_newest_version_module_cached(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_module_version = {"test/test_module/test_provider": "1.0.0"} - - newest_version = registry_handler.get_newest_version(module) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_not_called() - - -def test_get_newest_version_provider_cached(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_provider_version = {"test_provider/test_provider": "1.0.0"} - - newest_version = registry_handler.get_newest_version(provider) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_not_called() - - -def test_get_newest_version_module_uncached(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_module_version = {} - - newest_version = registry_handler.get_newest_version(module) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_provider_uncached(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_provider_version = {} - - newest_version = registry_handler.get_newest_version(provider) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_module_no_credentials(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {} - - newest_version = registry_handler.get_newest_version(module) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_provider_no_credentials(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {} - - newest_version = registry_handler.get_newest_version(provider) - - assert newest_version == "1.0.0" - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_module_not_found(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_module_version = {} - - with patch("infrapatch.core.utils.terraform.registry_handler.request.urlopen") as mock_urlopen: - mock_urlopen.return_value.status = 404 - - with pytest.raises(ResourceNotFoundException, match=r"Resource 'test_resource' not found in registry 'testregistry.ch'."): - registry_handler.get_newest_version(module) - - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_provider_not_found(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_provider_version = {} - - with patch("infrapatch.core.utils.terraform.registry_handler.request.urlopen") as mock_urlopen: - mock_urlopen.return_value.status = 404 - - with pytest.raises(ResourceNotFoundException, match=r"Resource 'test_resource' not found in registry 'testregistry.ch'."): - registry_handler.get_newest_version(provider) - - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_module_error(registry_handler): - module = TerraformModule(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test/test_module/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"modules.v1": "https://testregistry.ch/v1/modules"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_module_version = {} - - with patch("infrapatch.core.utils.terraform.registry_handler.request.urlopen") as mock_urlopen: - mock_urlopen.return_value.status = 500 - - with pytest.raises(RegistryMetadataException, match=r"Could not get versions from 'https://testregistry.ch/v1/modules/test/test_module/test_provider/versions'."): - registry_handler.get_newest_version(module) - - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_newest_version_provider_error(registry_handler): - provider = TerraformProvider(name="test_resource", current_version="1.0.0", _source_file="test_file.py", _source="test_provider/test_provider") - registry_handler.get_registry_metadata = MagicMock(return_value={"providers.v1": "https://testregistry.ch/v1/providers"}) - registry_handler.credentials = {"testregistry.ch": "test_token"} - registry_handler.cached_provider_version = {} - - with patch("infrapatch.core.utils.terraform.registry_handler.request.urlopen") as mock_urlopen: - mock_urlopen.return_value.status = 500 - - with pytest.raises(RegistryMetadataException, match=r"Could not get versions from 'https://testregistry.ch/v1/providers/test_provider/test_provider/versions'."): - registry_handler.get_newest_version(provider) - - registry_handler.get_registry_metadata.assert_called_once_with("testregistry.ch") - - -def test_get_registry_metadata_cached(registry_handler): - registry_handler.cached_registry_metadata = {"testregistry.ch": {"modules.v1": "https://testregistry.ch/v1/modules"}} - - metadata = registry_handler.get_registry_metadata("testregistry.ch") - - assert metadata == {"modules.v1": "https://testregistry.ch/v1/modules"} - - -def test_get_registry_metadata_uncached(registry_handler): - registry_handler.cached_registry_metadata = {} - - with patch("infrapatch.core.utils.terraform.registry_handler.request.urlopen") as mock_urlopen: - mock_urlopen.return_value.status = 200 - mock_urlopen.return_value.read.return_value = b'{"modules.v1": "https://testregistry.ch/v1/modules"}' - - metadata = registry_handler.get_registry_metadata("testregistry.ch") - - assert metadata == {"modules.v1": "https://testregistry.ch/v1/modules"} - registry_handler.cached_registry_metadata == {"testregistry.ch": {"modules.v1": "https://testregistry.ch/v1/modules"}}