diff --git a/resource_classes/azure/databases.py b/resource_classes/azure/databases.py index e8e50a3..eae93ef 100644 --- a/resource_classes/azure/databases.py +++ b/resource_classes/azure/databases.py @@ -3,7 +3,7 @@ class _Databases(_Azure): _type = "databases" - _icon_dir = "resource_images/azure/database" + _icon_dir = "resource_images/azure/databases" class AzureCosmosDb(_Databases): diff --git a/resource_classes/azure/web.py b/resource_classes/azure/web.py index 749bc33..9337af5 100644 --- a/resource_classes/azure/web.py +++ b/resource_classes/azure/web.py @@ -99,3 +99,9 @@ class StaticApps(_Web): azurerm_search_service = Search azurerm_signalr_service = Signalr azurerm_static_site = StaticApps +azurerm_static_web_app = StaticApps +azurerm_cdn_frontdoor_profile = FrontDoorAndCDNProfiles +azurerm_cdn_frontdoor_endpoint = FrontDoorAndCDNProfiles +azurerm_cdn_frontdoor_origin_group = FrontDoorAndCDNProfiles +azurerm_cdn_frontdoor_origin = FrontDoorAndCDNProfiles +azurerm_cdn_frontdoor_route = FrontDoorAndCDNProfiles diff --git a/tests/test_azure_icon_dirs.py b/tests/test_azure_icon_dirs.py new file mode 100644 index 0000000..c600a56 --- /dev/null +++ b/tests/test_azure_icon_dirs.py @@ -0,0 +1,49 @@ +""" +Regression tests that every Azure resource class points at an icon that exists. + +``resource_classes/azure/databases.py`` declared ``resource_images/azure/database`` +while its icons ship under ``resource_images/azure/databases``, so 41 of its 45 +classes resolved to a missing file. Nothing warned about it: the alias resolved, +so the renderer believed it had an icon and drew an empty node. + +``modules.drawing`` loads every module in the package into one namespace, so the +alphabetically last module wins. That made ``databases.py`` override the working +aliases in ``database.py``, and broke azurerm_redis_cache and +azurerm_postgresql_flexible_server for anyone using them. +""" + +import importlib +import inspect +import pkgutil +from pathlib import Path + +import pytest + +import resource_classes.azure as azure_classes + +REPO_ROOT = Path(azure_classes.__file__).parents[2] + + +def _icon_classes(): + """Every class in resource_classes.azure that declares an icon.""" + found = [] + package_path = Path(azure_classes.__file__).parent + for _, module_name, _ in pkgutil.iter_modules([str(package_path)]): + module = importlib.import_module(f"resource_classes.azure.{module_name}") + for name, obj in vars(module).items(): + if inspect.isclass(obj) and getattr(obj, "_icon", None): + found.append(pytest.param(obj, id=f"{module_name}.{name}")) + return found + + +ICON_CLASSES = _icon_classes() + + +def test_azure_icon_classes_were_discovered(): + assert ICON_CLASSES, "no icon classes found; the package layout changed" + + +@pytest.mark.parametrize("icon_class", ICON_CLASSES) +def test_icon_file_exists(icon_class): + icon = REPO_ROOT / icon_class._icon_dir / icon_class._icon + assert icon.is_file(), f"{icon_class.__name__} points at missing icon {icon}" diff --git a/tests/test_azure_web_aliases.py b/tests/test_azure_web_aliases.py new file mode 100644 index 0000000..8257160 --- /dev/null +++ b/tests/test_azure_web_aliases.py @@ -0,0 +1,60 @@ +""" +Regression tests for Azure web resource aliases. + +Both icons these aliases reach already ship with Terravision, but the module +could not resolve either resource: + +- ``azurerm_static_site`` was renamed ``azurerm_static_web_app`` in the AzureRM + provider, and only the old name was aliased. +- ``azurerm_cdn_frontdoor_*`` was never aliased, so ``FrontDoorAndCDNProfiles`` + was unreachable. Classic CDN is not an alternative: creating new + ``azurerm_cdn_profile`` resources has been blocked since 1 October 2025. + +Without the aliases both render as generic unconnected nodes. +""" + +import pytest + +from resource_classes.azure import web + + +class TestStaticWebAppAlias: + def test_current_resource_name_is_aliased(self): + assert hasattr(web, "azurerm_static_web_app") + + def test_current_name_resolves_to_static_apps_icon(self): + assert web.azurerm_static_web_app is web.StaticApps + + def test_renamed_resource_keeps_the_legacy_alias(self): + assert web.azurerm_static_site is web.StaticApps + + +class TestFrontDoorAliases: + FRONT_DOOR_RESOURCES = [ + "azurerm_cdn_frontdoor_profile", + "azurerm_cdn_frontdoor_endpoint", + "azurerm_cdn_frontdoor_origin_group", + "azurerm_cdn_frontdoor_origin", + "azurerm_cdn_frontdoor_route", + ] + + @pytest.mark.parametrize("resource", FRONT_DOOR_RESOURCES) + def test_resource_is_aliased(self, resource): + assert hasattr(web, resource) + + @pytest.mark.parametrize("resource", FRONT_DOOR_RESOURCES) + def test_resource_resolves_to_front_door_icon(self, resource): + assert getattr(web, resource) is web.FrontDoorAndCDNProfiles + + +class TestAliasedIconsExist: + """An alias pointing at a class whose icon file is missing still renders blank.""" + + @pytest.mark.parametrize( + "icon_class", [web.StaticApps, web.FrontDoorAndCDNProfiles] + ) + def test_icon_file_is_present(self, icon_class): + from pathlib import Path + + icon = Path(web.__file__).parents[2] / icon_class._icon_dir / icon_class._icon + assert icon.is_file(), f"missing icon file: {icon}"