"""Tests for app.graph module.""" from __future__ import annotations from typing import TYPE_CHECKING from unittest.mock import AsyncMock, MagicMock import pytest from app.graph import build_agent_nodes, build_graph, classify_intent from app.intent import ClassificationResult, IntentTarget if TYPE_CHECKING: from app.registry import AgentRegistry @pytest.mark.unit class TestBuildAgentNodes: def test_creates_correct_number_of_nodes(self, sample_registry: AgentRegistry) -> None: mock_llm = MagicMock() nodes = build_agent_nodes(sample_registry, mock_llm) assert len(nodes) == 3 def test_nodes_are_runnable(self, sample_registry: AgentRegistry) -> None: mock_llm = MagicMock() nodes = build_agent_nodes(sample_registry, mock_llm) for node in nodes: assert hasattr(node, "invoke") or hasattr(node, "ainvoke") @pytest.mark.unit class TestBuildGraph: def test_graph_compiles_with_mock_checkpointer(self, sample_registry: AgentRegistry) -> None: mock_llm = MagicMock() mock_llm.bind_tools = MagicMock(return_value=mock_llm) mock_llm.with_structured_output = MagicMock(return_value=mock_llm) mock_checkpointer = AsyncMock() graph = build_graph(sample_registry, mock_llm, mock_checkpointer) assert graph is not None def test_graph_has_classifier_attached(self, sample_registry: AgentRegistry) -> None: mock_llm = MagicMock() mock_llm.bind_tools = MagicMock(return_value=mock_llm) mock_llm.with_structured_output = MagicMock(return_value=mock_llm) mock_checkpointer = AsyncMock() mock_classifier = MagicMock() graph = build_graph( sample_registry, mock_llm, mock_checkpointer, intent_classifier=mock_classifier ) assert graph.intent_classifier is mock_classifier assert graph.agent_registry is sample_registry def test_graph_without_classifier(self, sample_registry: AgentRegistry) -> None: mock_llm = MagicMock() mock_llm.bind_tools = MagicMock(return_value=mock_llm) mock_llm.with_structured_output = MagicMock(return_value=mock_llm) mock_checkpointer = AsyncMock() graph = build_graph(sample_registry, mock_llm, mock_checkpointer) assert graph.intent_classifier is None @pytest.mark.unit class TestClassifyIntent: @pytest.mark.asyncio async def test_returns_none_without_classifier(self) -> None: graph = MagicMock() graph.intent_classifier = None result = await classify_intent(graph, "hello") assert result is None @pytest.mark.asyncio async def test_calls_classifier(self) -> None: expected = ClassificationResult( intents=(IntentTarget(agent_name="order_lookup", confidence=0.9, reasoning="test"),), ) mock_classifier = AsyncMock() mock_classifier.classify = AsyncMock(return_value=expected) graph = MagicMock() graph.intent_classifier = mock_classifier graph.agent_registry = MagicMock() graph.agent_registry.list_agents = MagicMock(return_value=()) result = await classify_intent(graph, "check order") assert result is not None assert result.intents[0].agent_name == "order_lookup"