from __future__ import annotations from pathlib import Path import pytest from src.config import DiscoveryConfig from src.discovery import DiscoveryManager from src.metadata import InfraMetadata def _write(path: Path, content: str) -> Path: path.write_text(content, encoding="utf-8") return path def test_infra_metadata_toml_valid(tmp_path: Path) -> None: p = _write( tmp_path / ".infra.toml", """ project = "my-app" [requires] services = ["grafana", "prometheus"] """.lstrip(), ) md = InfraMetadata.from_file(p) assert md.project == "my-app" assert md.requires["services"] == ["grafana", "prometheus"] def test_infra_metadata_yaml_valid_uses_dir_name_when_project_missing(tmp_path: Path) -> None: proj_dir = tmp_path / "some-app" proj_dir.mkdir() p = _write( proj_dir / ".infra.yml", """ requires: services: grafana """.lstrip(), ) md = InfraMetadata.from_file(p) assert md.project == "some-app" assert md.requires["services"] == "grafana" def test_infra_metadata_invalid_top_level(tmp_path: Path) -> None: p = _write(tmp_path / ".infra.yml", "- not-a-mapping\n") with pytest.raises(ValueError, match=r"expected mapping"): InfraMetadata.from_file(p) def test_infra_metadata_invalid_project(tmp_path: Path) -> None: p = _write( tmp_path / ".infra.toml", """ project = "bad name" [requires] services = "grafana" """.lstrip(), ) with pytest.raises(ValueError, match=r"Invalid 'project'"): InfraMetadata.from_file(p) def test_infra_metadata_invalid_requires_type(tmp_path: Path) -> None: p = _write( tmp_path / ".infra.yml", """ project: app requires: not-a-mapping """.lstrip(), ) with pytest.raises(ValueError, match=r"Invalid 'requires'"): InfraMetadata.from_file(p) @pytest.mark.parametrize( "services", [ "[]", "[\"\"]", "[1]", ], ) def test_infra_metadata_invalid_requires_services_list(tmp_path: Path, services: str) -> None: p = _write( tmp_path / ".infra.toml", ( "project = \"app\"\n\n" "[requires]\n" f"services = {services}\n" ), ) with pytest.raises(ValueError, match=r"requires\.services"): InfraMetadata.from_file(p) def test_infra_metadata_invalid_requires_services_type(tmp_path: Path) -> None: p = _write( tmp_path / ".infra.yml", """ project: app requires: services: 123 """.lstrip(), ) with pytest.raises(ValueError, match=r"requires\.services"): InfraMetadata.from_file(p) def test_discover_scan_prefers_toml_over_yaml_in_same_dir(tmp_path: Path, monkeypatch) -> None: base = tmp_path / "apps" proj = base / "proj" (proj / ".git").mkdir(parents=True) (proj / ".git" / "HEAD").write_text("ref: refs/heads/main\n", encoding="utf-8") _write( proj / ".infra.toml", """ project = "proj" [requires] services = ["svc_toml"] """.lstrip(), ) _write( proj / ".infra.yml", """ project: proj requires: services: svc_yaml """.lstrip(), ) cfg = DiscoveryConfig(scan_enabled=True, scan_paths=[base], file_based_enabled=False) mgr = DiscoveryManager(cfg) apps = mgr.discover_all() assert set(apps.keys()) == {"proj"} assert apps["proj"].metadata.requires["services"] == ["svc_toml"]