"""Tests for the in-memory tool manifest registry.""" from __future__ import annotations from pathlib import Path import pytest from pydantic import ValidationError from app.tools.models import PortConfig, ToolManifest, TraefikConfig from app.tools.registry import ToolRegistry # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture def registry() -> ToolRegistry: return ToolRegistry() @pytest.fixture def sample_manifest() -> ToolManifest: return ToolManifest( id="test-tool", name="Test Tool", image="test:latest", ports=[PortConfig(container_port=8080, primary=True)], traefik=TraefikConfig(enabled=False), ) # --------------------------------------------------------------------------- # Register / get round-trip # --------------------------------------------------------------------------- def test_register_and_get(registry: ToolRegistry, sample_manifest: ToolManifest) -> None: registry.register(sample_manifest) retrieved = registry.get("test-tool") assert retrieved is not None assert retrieved.id == "test-tool" def test_get_missing_returns_none(registry: ToolRegistry) -> None: assert registry.get("missing") is None # --------------------------------------------------------------------------- # List # --------------------------------------------------------------------------- def test_list_returns_all(registry: ToolRegistry) -> None: m1 = ToolManifest( id="tool-a", name="Tool A", image="a:latest", ports=[PortConfig(container_port=8080, primary=True)], traefik=TraefikConfig(enabled=False), ) m2 = ToolManifest( id="tool-b", name="Tool B", image="b:latest", ports=[PortConfig(container_port=3000, primary=True)], traefik=TraefikConfig(enabled=False), ) registry.register(m1) registry.register(m2) assert len(registry.list()) == 2 ids = {m.id for m in registry.list()} assert ids == {"tool-a", "tool-b"} # --------------------------------------------------------------------------- # Overwrite behavior # --------------------------------------------------------------------------- def test_register_overwrites_existing( registry: ToolRegistry, sample_manifest: ToolManifest, ) -> None: registry.register(sample_manifest) updated = ToolManifest( id="test-tool", name="Updated Tool", image="updated:latest", ports=[PortConfig(container_port=8080, primary=True)], traefik=TraefikConfig(enabled=False), ) registry.register(updated) retrieved = registry.get("test-tool") assert retrieved is not None assert retrieved.name == "Updated Tool" # --------------------------------------------------------------------------- # Remove # --------------------------------------------------------------------------- def test_remove_returns_manifest(registry: ToolRegistry, sample_manifest: ToolManifest) -> None: registry.register(sample_manifest) removed = registry.remove("test-tool") assert removed is not None assert removed.id == "test-tool" assert registry.get("test-tool") is None def test_remove_missing_returns_none(registry: ToolRegistry) -> None: assert registry.remove("missing") is None # --------------------------------------------------------------------------- # Load file # --------------------------------------------------------------------------- def test_load_valid_yaml_file(registry: ToolRegistry, tmp_path: Path) -> None: yaml_path = tmp_path / "my-tool.yml" yaml_path.write_text( """ id: my-tool name: My Tool image: my-tool:latest ports: - container_port: 8080 primary: true traefik: enabled: false """, encoding="utf-8", ) manifest = registry.load_file(yaml_path) assert manifest.id == "my-tool" assert manifest.name == "My Tool" assert manifest.ports[0].container_port == 8080 def test_load_invalid_yaml_raises(registry: ToolRegistry, tmp_path: Path) -> None: yaml_path = tmp_path / "bad-tool.yml" yaml_path.write_text( """ id: BAD ID name: Bad Tool image: bad:latest """, encoding="utf-8", ) with pytest.raises(ValidationError): registry.load_file(yaml_path) # --------------------------------------------------------------------------- # Load builtin manifests # --------------------------------------------------------------------------- def test_load_builtin_manifests(registry: ToolRegistry) -> None: registry.load_builtin_manifests() # Built-in manifests from Step 4 may not exist yet in isolation, # but the method should not raise regardless of directory contents. assert isinstance(registry.list(), list)