"""Unit tests for catalog loading and validation.""" import textwrap from pathlib import Path import pytest from svcforge_core.domain.catalog import CatalogError, load_catalog from svcforge_core.domain.models import CatalogEntry VALID_YAML = textwrap.dedent(""" services: redis: chart: bitnamilegacy/redis chart_version: 20.6.2 sizes: small: replicas: 1 resources: requests: {cpu: 100m, memory: 256Mi} medium: replicas: 3 resources: requests: {cpu: 500m, memory: 1Gi} postgres: chart: bitnamilegacy/postgresql chart_version: 16.4.5 sizes: small: replicas: 1 resources: requests: {cpu: 250m, memory: 512Mi} """) MISSING_CHART_VERSION_YAML = textwrap.dedent(""" services: redis: chart: bitnamilegacy/redis sizes: small: replicas: 1 resources: {} """) ZERO_REPLICAS_YAML = textwrap.dedent(""" services: redis: chart: bitnamilegacy/redis chart_version: 20.6.2 sizes: small: replicas: 0 resources: {} """) def _write(tmp_path: Path, body: str) -> Path: path = tmp_path / "catalog.yaml" path.write_text(body) return path def test_valid_yaml_loads_to_catalog_entries(tmp_path: Path) -> None: catalog = load_catalog(_write(tmp_path, VALID_YAML)) assert set(catalog) == {"redis", "postgres"} assert all(isinstance(entry, CatalogEntry) for entry in catalog.values()) redis = catalog["redis"] assert redis.service_type == "redis" assert redis.chart_version == "20.6.2" assert set(redis.sizes) == {"small", "medium"} assert redis.sizes["medium"].replicas == 3 def test_missing_chart_version_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError) as excinfo: load_catalog(_write(tmp_path, MISSING_CHART_VERSION_YAML)) assert excinfo.value.key == "redis" assert "redis" in str(excinfo.value) def test_zero_replicas_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError) as excinfo: load_catalog(_write(tmp_path, ZERO_REPLICAS_YAML)) assert excinfo.value.key == "redis" def test_missing_file_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError, match="cannot read catalog"): load_catalog(tmp_path / "nope.yaml") def test_unparseable_yaml_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError, match="not valid YAML"): load_catalog(_write(tmp_path, "services: [unclosed\n")) def test_scalar_root_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError, match="must be a mapping"): load_catalog(_write(tmp_path, "just-a-string\n")) def test_non_mapping_services_raises_catalog_error(tmp_path: Path) -> None: with pytest.raises(CatalogError, match="'services' must be a mapping"): load_catalog(_write(tmp_path, "services:\n - redis\n")) def test_non_mapping_entry_raises_catalog_error_naming_the_key(tmp_path: Path) -> None: with pytest.raises(CatalogError) as excinfo: load_catalog(_write(tmp_path, "services:\n redis: just-a-string\n")) assert excinfo.value.key == "redis" assert "must be a mapping" in str(excinfo.value) def test_non_string_field_key_raises_catalog_error_naming_the_key(tmp_path: Path) -> None: """A non-string YAML key inside an entry breaks `**body`; it must surface as CatalogError.""" body = textwrap.dedent(""" services: redis: 1: oops chart: bitnamilegacy/redis chart_version: 20.6.2 sizes: {} """) with pytest.raises(CatalogError) as excinfo: load_catalog(_write(tmp_path, body)) assert excinfo.value.key == "redis" def test_bare_mapping_without_services_key_is_accepted(tmp_path: Path) -> None: """The top-level `services:` wrapper is optional; a bare service_type mapping also loads.""" body = textwrap.dedent(""" redis: chart: bitnamilegacy/redis chart_version: 20.6.2 sizes: small: replicas: 1 resources: {} """) catalog = load_catalog(_write(tmp_path, body)) assert set(catalog) == {"redis"} def test_repo_catalog_yaml_is_valid() -> None: catalog = load_catalog(Path(__file__).parents[2] / "catalog.yaml") assert set(catalog) == {"elasticsearch", "redis", "postgres"} for entry in catalog.values(): assert set(entry.sizes) == {"small", "medium"}