"""Tests for app.registry module.""" from __future__ import annotations from typing import TYPE_CHECKING import pytest import yaml from app.registry import AgentConfig, AgentRegistry, PersonalityConfig if TYPE_CHECKING: from pathlib import Path @pytest.mark.unit class TestPersonalityConfig: def test_defaults(self) -> None: p = PersonalityConfig() assert p.tone == "professional and helpful" assert "Hello" in p.greeting assert "human agent" in p.escalation_message def test_custom_values(self) -> None: p = PersonalityConfig(tone="casual", greeting="Hey!", escalation_message="Hold on.") assert p.tone == "casual" def test_immutable(self) -> None: p = PersonalityConfig() with pytest.raises(Exception): p.tone = "new tone" @pytest.mark.unit class TestAgentConfig: def test_valid_config(self) -> None: ac = AgentConfig( name="test", description="A test agent", permission="read", tools=["tool1"], ) assert ac.name == "test" assert ac.permission == "read" def test_empty_name_rejected(self) -> None: with pytest.raises(ValueError, match="must not be empty"): AgentConfig(name=" ", description="d", permission="read", tools=["t"]) def test_empty_tools_rejected(self) -> None: with pytest.raises(ValueError, match="at least one tool"): AgentConfig(name="x", description="d", permission="read", tools=[]) def test_invalid_permission_rejected(self) -> None: with pytest.raises(Exception): AgentConfig(name="x", description="d", permission="admin", tools=["t"]) def test_name_stripped(self) -> None: ac = AgentConfig(name=" test ", description="d", permission="read", tools=["t"]) assert ac.name == "test" def test_immutable(self) -> None: ac = AgentConfig(name="test", description="d", permission="read", tools=["t"]) with pytest.raises(Exception): ac.name = "new" @pytest.mark.unit class TestAgentRegistry: def test_load_valid_yaml(self, sample_yaml_path: Path) -> None: registry = AgentRegistry.load(sample_yaml_path) assert len(registry) == 3 assert registry.get_agent("test_reader").permission == "read" assert registry.get_agent("test_writer").permission == "write" def test_list_agents(self, sample_registry: AgentRegistry) -> None: agents = sample_registry.list_agents() assert len(agents) == 3 names = {a.name for a in agents} assert names == {"test_reader", "test_writer", "test_fallback"} def test_get_agents_by_permission(self, sample_registry: AgentRegistry) -> None: readers = sample_registry.get_agents_by_permission("read") assert len(readers) == 2 writers = sample_registry.get_agents_by_permission("write") assert len(writers) == 1 def test_get_nonexistent_agent(self, sample_registry: AgentRegistry) -> None: with pytest.raises(KeyError, match="not found"): sample_registry.get_agent("nonexistent") def test_personality_defaults_applied(self, sample_registry: AgentRegistry) -> None: agent = sample_registry.get_agent("test_reader") assert agent.personality.tone == "professional and helpful" def test_personality_custom_applied(self, sample_registry: AgentRegistry) -> None: agent = sample_registry.get_agent("test_writer") assert agent.personality.tone == "formal" assert agent.personality.greeting == "Greetings." def test_file_not_found(self) -> None: with pytest.raises(FileNotFoundError): AgentRegistry.load("/nonexistent/path.yaml") def test_empty_file(self, tmp_path: Path) -> None: path = tmp_path / "empty.yaml" path.write_text("", encoding="utf-8") with pytest.raises(ValueError, match="empty"): AgentRegistry.load(path) def test_invalid_yaml_syntax(self, tmp_path: Path) -> None: path = tmp_path / "bad.yaml" path.write_text("agents:\n - name: [invalid\n", encoding="utf-8") with pytest.raises(ValueError, match="Invalid YAML"): AgentRegistry.load(path) def test_missing_agents_key(self, tmp_path: Path) -> None: path = tmp_path / "no_agents.yaml" path.write_text(yaml.dump({"items": []}), encoding="utf-8") with pytest.raises(ValueError, match="agents"): AgentRegistry.load(path) def test_duplicate_agent_names(self, tmp_path: Path) -> None: data = { "agents": [ {"name": "dup", "description": "a", "permission": "read", "tools": ["t1"]}, {"name": "dup", "description": "b", "permission": "read", "tools": ["t2"]}, ] } path = tmp_path / "dups.yaml" path.write_text(yaml.dump(data), encoding="utf-8") with pytest.raises(ValueError, match="Duplicate"): AgentRegistry.load(path) def test_missing_required_fields(self, tmp_path: Path) -> None: data = {"agents": [{"name": "x"}]} path = tmp_path / "missing.yaml" path.write_text(yaml.dump(data), encoding="utf-8") with pytest.raises(ValueError, match="Invalid agent config"): AgentRegistry.load(path) def test_empty_agents_list(self, tmp_path: Path) -> None: data = {"agents": []} path = tmp_path / "empty_list.yaml" path.write_text(yaml.dump(data), encoding="utf-8") with pytest.raises(ValueError, match="non-empty"): AgentRegistry.load(path)