test(phase3): add comprehensive tests for multi-agent coordination
Protocol tests (16): - AgentRequest/AgentResponse serialization - DelegationIntent and DelegationReason validation - CoordinationResult aggregation - Error type tests Coordination tests (14): - Engine initialization and agent availability - Delegation execution (success, error, timeout) - Multi-intent coordination - Streaming delegation Librarian tests (42): - Library-desk client (all endpoints) - Wiki operations (search, get, create, update) - Smart-create with HybridRAG - Capability registration - Response model validation Total: 72 new tests, all passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Tests for The Librarian agent."""
|
||||
@@ -0,0 +1,126 @@
|
||||
"""
|
||||
Tests for Librarian capability registration.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from src.agents.librarian.capability import (
|
||||
LIBRARIAN_CAPABILITY,
|
||||
get_librarian_capability,
|
||||
register_librarian,
|
||||
unregister_librarian,
|
||||
)
|
||||
from src.core.household_registry import HouseholdCapability
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestLibrarianCapability:
|
||||
"""Tests for the Librarian capability definition."""
|
||||
|
||||
def test_capability_is_household_capability(self):
|
||||
"""Test capability is correct type."""
|
||||
assert isinstance(LIBRARIAN_CAPABILITY, HouseholdCapability)
|
||||
|
||||
def test_capability_name(self):
|
||||
"""Test capability has correct name."""
|
||||
assert LIBRARIAN_CAPABILITY.name == "librarian"
|
||||
|
||||
def test_capability_role(self):
|
||||
"""Test capability has correct role."""
|
||||
assert LIBRARIAN_CAPABILITY.role == "The Librarian"
|
||||
|
||||
def test_capability_category(self):
|
||||
"""Test capability is in research category."""
|
||||
assert LIBRARIAN_CAPABILITY.category == "research"
|
||||
|
||||
def test_capability_domains(self):
|
||||
"""Test capability covers expected domains."""
|
||||
domains = LIBRARIAN_CAPABILITY.domains
|
||||
|
||||
assert "research" in domains
|
||||
assert "knowledge" in domains
|
||||
assert "wiki" in domains
|
||||
assert "search" in domains
|
||||
|
||||
def test_capability_requires_network(self):
|
||||
"""Test capability requires network access."""
|
||||
assert LIBRARIAN_CAPABILITY.requires_network is True
|
||||
|
||||
def test_get_librarian_capability(self):
|
||||
"""Test getter returns same capability."""
|
||||
cap = get_librarian_capability()
|
||||
|
||||
assert cap is LIBRARIAN_CAPABILITY
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestLibrarianRegistration:
|
||||
"""Tests for Librarian registration functions."""
|
||||
|
||||
def test_register_librarian(self):
|
||||
"""Test registering librarian with registry."""
|
||||
mock_registry = MagicMock()
|
||||
mock_registry.__contains__ = MagicMock(return_value=False)
|
||||
|
||||
with patch(
|
||||
"src.agents.librarian.capability.get_household_registry",
|
||||
return_value=mock_registry,
|
||||
):
|
||||
with patch(
|
||||
"src.agents.librarian.capability.get_librarian_agent"
|
||||
) as mock_get_agent:
|
||||
mock_agent = MagicMock()
|
||||
mock_get_agent.return_value = mock_agent
|
||||
|
||||
register_librarian()
|
||||
|
||||
mock_registry.register.assert_called_once()
|
||||
call_kwargs = mock_registry.register.call_args[1]
|
||||
|
||||
assert call_kwargs["name"] == "librarian"
|
||||
assert call_kwargs["capability"] is LIBRARIAN_CAPABILITY
|
||||
assert call_kwargs["agent"] is mock_agent
|
||||
|
||||
def test_register_librarian_already_registered(self):
|
||||
"""Test registering when already registered does nothing."""
|
||||
mock_registry = MagicMock()
|
||||
mock_registry.__contains__ = MagicMock(return_value=True)
|
||||
|
||||
with patch(
|
||||
"src.agents.librarian.capability.get_household_registry",
|
||||
return_value=mock_registry,
|
||||
):
|
||||
register_librarian()
|
||||
|
||||
# Should not call register since already registered
|
||||
mock_registry.register.assert_not_called()
|
||||
|
||||
def test_unregister_librarian(self):
|
||||
"""Test unregistering librarian from registry."""
|
||||
mock_registry = MagicMock()
|
||||
|
||||
with patch(
|
||||
"src.agents.librarian.capability.get_household_registry",
|
||||
return_value=mock_registry,
|
||||
):
|
||||
unregister_librarian()
|
||||
|
||||
mock_registry.unregister.assert_called_once_with("librarian")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestCapabilityDescription:
|
||||
"""Tests for capability description."""
|
||||
|
||||
def test_description_mentions_library_desk(self):
|
||||
"""Test description mentions library-desk API."""
|
||||
assert "library-desk" in LIBRARIAN_CAPABILITY.description.lower()
|
||||
|
||||
def test_description_mentions_search(self):
|
||||
"""Test description mentions search capability."""
|
||||
assert "search" in LIBRARIAN_CAPABILITY.description.lower()
|
||||
|
||||
def test_description_mentions_wiki(self):
|
||||
"""Test description mentions wiki access."""
|
||||
assert "wiki" in LIBRARIAN_CAPABILITY.description.lower()
|
||||
@@ -0,0 +1,598 @@
|
||||
"""
|
||||
Tests for the Library-Desk HTTP client.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
import httpx
|
||||
|
||||
from src.agents.librarian.client import (
|
||||
LibraryDeskClient,
|
||||
HybridRAGResponse,
|
||||
HybridSearchResult,
|
||||
WikiPage,
|
||||
WikiSearchResult,
|
||||
VectorSearchResult,
|
||||
GraphNode,
|
||||
Dossier,
|
||||
SmartCreateResponse,
|
||||
ResearchSummary,
|
||||
EntityLinking,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_httpx_client():
|
||||
"""Create a mock httpx client."""
|
||||
return AsyncMock(spec=httpx.AsyncClient)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client_with_mock(mock_httpx_client):
|
||||
"""Create a LibraryDeskClient with mocked httpx client."""
|
||||
client = LibraryDeskClient(
|
||||
base_url="http://test:8089",
|
||||
api_key="test-key",
|
||||
)
|
||||
client._client = mock_httpx_client
|
||||
return client
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestLibraryDeskClientInit:
|
||||
"""Tests for client initialization."""
|
||||
|
||||
def test_default_initialization(self):
|
||||
"""Test client initializes with defaults from config."""
|
||||
client = LibraryDeskClient()
|
||||
|
||||
assert client.base_url is not None
|
||||
assert client.timeout == 60
|
||||
assert client._client is None
|
||||
|
||||
def test_custom_initialization(self):
|
||||
"""Test client with custom parameters."""
|
||||
client = LibraryDeskClient(
|
||||
base_url="http://custom:9000",
|
||||
api_key="my-api-key",
|
||||
timeout=120,
|
||||
)
|
||||
|
||||
assert client.base_url == "http://custom:9000"
|
||||
assert client.api_key == "my-api-key"
|
||||
assert client.timeout == 120
|
||||
|
||||
def test_ensure_client_not_initialized(self):
|
||||
"""Test _ensure_client raises when not in context."""
|
||||
client = LibraryDeskClient()
|
||||
|
||||
with pytest.raises(RuntimeError) as exc_info:
|
||||
client._ensure_client()
|
||||
|
||||
assert "not initialized" in str(exc_info.value)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestContextManager:
|
||||
"""Tests for async context manager."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_context_manager_creates_client(self):
|
||||
"""Test context manager creates httpx client."""
|
||||
async with LibraryDeskClient(
|
||||
base_url="http://test:8089",
|
||||
api_key="test-key",
|
||||
) as client:
|
||||
assert client._client is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_context_manager_closes_client(self):
|
||||
"""Test context manager closes client on exit."""
|
||||
client = LibraryDeskClient(base_url="http://test:8089")
|
||||
|
||||
async with client:
|
||||
assert client._client is not None
|
||||
|
||||
# After exit, client should be None
|
||||
assert client._client is None
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestHybridSearch:
|
||||
"""Tests for hybrid search."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hybrid_search_success(self, client_with_mock, mock_httpx_client):
|
||||
"""Test successful hybrid search."""
|
||||
# Mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"results": [
|
||||
{
|
||||
"source": "vector",
|
||||
"title": "Docker Guide",
|
||||
"content": "Docker networking basics...",
|
||||
"score": 0.95,
|
||||
"page_id": 123,
|
||||
}
|
||||
],
|
||||
"keywords": ["docker", "networking"],
|
||||
"synonyms": ["container"],
|
||||
"formatted_context": "Context here",
|
||||
"timing": {"total": 1.5},
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
result = await client_with_mock.hybrid_search(
|
||||
query="Docker networking",
|
||||
user="testuser",
|
||||
)
|
||||
|
||||
assert isinstance(result, HybridRAGResponse)
|
||||
assert len(result.results) == 1
|
||||
assert result.results[0].title == "Docker Guide"
|
||||
assert result.results[0].source == "vector"
|
||||
assert "docker" in result.keywords
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hybrid_search_empty_results(
|
||||
self, client_with_mock, mock_httpx_client
|
||||
):
|
||||
"""Test hybrid search with no results."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"results": [],
|
||||
"keywords": [],
|
||||
"formatted_context": "",
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
result = await client_with_mock.hybrid_search("nonexistent query")
|
||||
|
||||
assert len(result.results) == 0
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestWikiOperations:
|
||||
"""Tests for wiki operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_wiki(self, client_with_mock, mock_httpx_client):
|
||||
"""Test wiki search."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"path": "/docs/docker",
|
||||
"title": "Docker Documentation",
|
||||
"description": "Docker docs",
|
||||
}
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
results = await client_with_mock.search_wiki("docker")
|
||||
|
||||
assert len(results) == 1
|
||||
assert isinstance(results[0], WikiSearchResult)
|
||||
assert results[0].title == "Docker Documentation"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_wiki_page(self, client_with_mock, mock_httpx_client):
|
||||
"""Test getting a wiki page."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"id": 123,
|
||||
"path": "/docs/docker",
|
||||
"title": "Docker Guide",
|
||||
"content": "# Docker\n\nFull content here...",
|
||||
"tags": ["docker", "devops"],
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
page = await client_with_mock.get_wiki_page(123)
|
||||
|
||||
assert isinstance(page, WikiPage)
|
||||
assert page.id == 123
|
||||
assert page.title == "Docker Guide"
|
||||
assert "docker" in page.tags
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_wiki_pages(self, client_with_mock, mock_httpx_client):
|
||||
"""Test listing wiki pages."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"pages": [
|
||||
{"id": 1, "path": "/page1", "title": "Page 1"},
|
||||
{"id": 2, "path": "/page2", "title": "Page 2"},
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
pages = await client_with_mock.list_wiki_pages()
|
||||
|
||||
assert len(pages) == 2
|
||||
assert pages[0].title == "Page 1"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_dossiers(self, client_with_mock, mock_httpx_client):
|
||||
"""Test listing dossiers."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"dossiers": [
|
||||
{"name": "docker", "page_count": 10},
|
||||
{"name": "kubernetes", "page_count": 5},
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
dossiers = await client_with_mock.list_dossiers()
|
||||
|
||||
assert len(dossiers) == 2
|
||||
assert isinstance(dossiers[0], Dossier)
|
||||
assert dossiers[0].name == "docker"
|
||||
assert dossiers[0].page_count == 10
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestSemanticSearch:
|
||||
"""Tests for semantic/vector search."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_semantic_search(self, client_with_mock, mock_httpx_client):
|
||||
"""Test semantic search."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"results": [
|
||||
{
|
||||
"page_id": 1,
|
||||
"page_path": "/docs/networking",
|
||||
"page_title": "Networking Guide",
|
||||
"chunk_text": "Container networking...",
|
||||
"score": 0.92,
|
||||
"chunk_index": 0,
|
||||
}
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
results = await client_with_mock.semantic_search("container networking")
|
||||
|
||||
assert len(results) == 1
|
||||
assert isinstance(results[0], VectorSearchResult)
|
||||
assert results[0].score == 0.92
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestGraphOperations:
|
||||
"""Tests for knowledge graph operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_query_graph(self, client_with_mock, mock_httpx_client):
|
||||
"""Test executing a Cypher query."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"records": [
|
||||
{"name": "Docker", "type": "Technology"},
|
||||
{"name": "Kubernetes", "type": "Technology"},
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
records = await client_with_mock.query_graph(
|
||||
"MATCH (n:Technology) RETURN n.name as name, n.type as type"
|
||||
)
|
||||
|
||||
assert len(records) == 2
|
||||
assert records[0]["name"] == "Docker"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_graph_nodes(self, client_with_mock, mock_httpx_client):
|
||||
"""Test listing graph nodes."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node1",
|
||||
"labels": ["Technology"],
|
||||
"properties": {"name": "Docker"},
|
||||
}
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
nodes = await client_with_mock.list_graph_nodes()
|
||||
|
||||
assert len(nodes) == 1
|
||||
assert isinstance(nodes[0], GraphNode)
|
||||
assert nodes[0].id == "node1"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestHealthCheck:
|
||||
"""Tests for health check."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_check_healthy(self, client_with_mock, mock_httpx_client):
|
||||
"""Test health check returns true when healthy."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_httpx_client.get.return_value = mock_response
|
||||
|
||||
result = await client_with_mock.health_check()
|
||||
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_check_unhealthy(self, client_with_mock, mock_httpx_client):
|
||||
"""Test health check returns false on error."""
|
||||
mock_httpx_client.get.side_effect = httpx.ConnectError("Connection refused")
|
||||
|
||||
result = await client_with_mock.health_check()
|
||||
|
||||
assert result is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestResponseModels:
|
||||
"""Tests for response model validation."""
|
||||
|
||||
def test_wiki_page_model(self):
|
||||
"""Test WikiPage model."""
|
||||
page = WikiPage(
|
||||
id=1,
|
||||
path="/test",
|
||||
title="Test Page",
|
||||
content="Content here",
|
||||
tags=["tag1"],
|
||||
)
|
||||
|
||||
assert page.id == 1
|
||||
assert page.title == "Test Page"
|
||||
|
||||
def test_wiki_page_optional_fields(self):
|
||||
"""Test WikiPage with minimal fields."""
|
||||
page = WikiPage(id=1, path="/test", title="Test")
|
||||
|
||||
assert page.content is None
|
||||
assert page.tags == []
|
||||
|
||||
def test_hybrid_search_result_model(self):
|
||||
"""Test HybridSearchResult model."""
|
||||
result = HybridSearchResult(
|
||||
source="vector",
|
||||
title="Title",
|
||||
content="Content",
|
||||
score=0.9,
|
||||
)
|
||||
|
||||
assert result.source == "vector"
|
||||
assert result.url is None
|
||||
assert result.metadata == {}
|
||||
|
||||
def test_vector_search_result_model(self):
|
||||
"""Test VectorSearchResult model."""
|
||||
result = VectorSearchResult(
|
||||
page_id=1,
|
||||
page_path="/doc",
|
||||
page_title="Doc",
|
||||
chunk_text="Text chunk",
|
||||
score=0.85,
|
||||
chunk_index=0,
|
||||
)
|
||||
|
||||
assert result.score == 0.85
|
||||
assert result.chunk_index == 0
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestUpdateWikiPage:
|
||||
"""Tests for update_wiki_page method."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_wiki_page_content(self, client_with_mock, mock_httpx_client):
|
||||
"""Test updating wiki page content."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"id": 42,
|
||||
"path": "/docs/test",
|
||||
"title": "Test Page",
|
||||
"content": "# Updated\n\nNew content",
|
||||
"tags": ["test"],
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.put.return_value = mock_response
|
||||
|
||||
page = await client_with_mock.update_wiki_page(
|
||||
page_id=42,
|
||||
content="# Updated\n\nNew content",
|
||||
)
|
||||
|
||||
assert isinstance(page, WikiPage)
|
||||
assert page.id == 42
|
||||
assert "Updated" in page.content
|
||||
mock_httpx_client.put.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_wiki_page_tags_only(self, client_with_mock, mock_httpx_client):
|
||||
"""Test updating only tags (partial update)."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"id": 42,
|
||||
"path": "/docs/test",
|
||||
"title": "Test Page",
|
||||
"tags": ["projects", "devops"],
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.put.return_value = mock_response
|
||||
|
||||
page = await client_with_mock.update_wiki_page(
|
||||
page_id=42,
|
||||
tags=["projects", "devops"],
|
||||
)
|
||||
|
||||
assert page.tags == ["projects", "devops"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_wiki_page_multiple_fields(
|
||||
self, client_with_mock, mock_httpx_client
|
||||
):
|
||||
"""Test updating multiple fields at once."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"id": 42,
|
||||
"path": "/docs/test",
|
||||
"title": "New Title",
|
||||
"description": "New description",
|
||||
"tags": ["updated"],
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.put.return_value = mock_response
|
||||
|
||||
page = await client_with_mock.update_wiki_page(
|
||||
page_id=42,
|
||||
title="New Title",
|
||||
description="New description",
|
||||
tags=["updated"],
|
||||
)
|
||||
|
||||
assert page.title == "New Title"
|
||||
assert page.description == "New description"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestSmartCreateWikiPage:
|
||||
"""Tests for smart_create_wiki_page method."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_smart_create_basic(self, client_with_mock, mock_httpx_client):
|
||||
"""Test basic smart create."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"page": {
|
||||
"id": 123,
|
||||
"path": "/users/test/technology/docker-compose",
|
||||
"title": "Docker Compose",
|
||||
"content": "# Docker Compose\n\nContent...",
|
||||
"tags": ["technology", "devops"],
|
||||
},
|
||||
"research_summary": {
|
||||
"wiki_results": 3,
|
||||
"web_results": 8,
|
||||
"graph_entities": 5,
|
||||
"keywords_extracted": 12,
|
||||
"timing_ms": 4500,
|
||||
},
|
||||
"sources_used": 11,
|
||||
"search_id": "uuid-123",
|
||||
"entity_linking": {
|
||||
"forward_links": 5,
|
||||
"backward_links": 3,
|
||||
"pages_updated": 2,
|
||||
},
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
result = await client_with_mock.smart_create_wiki_page(
|
||||
topic="Docker Compose",
|
||||
tags=["technology", "devops"],
|
||||
)
|
||||
|
||||
assert isinstance(result, SmartCreateResponse)
|
||||
assert result.page.id == 123
|
||||
assert result.page.title == "Docker Compose"
|
||||
assert result.sources_used == 11
|
||||
assert result.research_summary.wiki_results == 3
|
||||
assert result.research_summary.web_results == 8
|
||||
assert result.entity_linking.forward_links == 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_smart_create_with_options(self, client_with_mock, mock_httpx_client):
|
||||
"""Test smart create with custom options."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"page": {
|
||||
"id": 456,
|
||||
"path": "/custom/path",
|
||||
"title": "Custom Topic",
|
||||
"tags": ["custom"],
|
||||
},
|
||||
"research_summary": {
|
||||
"wiki_results": 5,
|
||||
"web_results": 0, # Web disabled
|
||||
"timing_ms": 2000,
|
||||
},
|
||||
"sources_used": 5,
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
mock_httpx_client.post.return_value = mock_response
|
||||
|
||||
result = await client_with_mock.smart_create_wiki_page(
|
||||
topic="Custom Topic",
|
||||
tags=["custom"],
|
||||
path="/custom/path",
|
||||
include_web_research=False,
|
||||
)
|
||||
|
||||
assert result.page.path == "/custom/path"
|
||||
assert result.research_summary.web_results == 0
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestNewResponseModels:
|
||||
"""Tests for new response models."""
|
||||
|
||||
def test_research_summary_model(self):
|
||||
"""Test ResearchSummary model."""
|
||||
summary = ResearchSummary(
|
||||
wiki_results=3,
|
||||
web_results=5,
|
||||
graph_entities=2,
|
||||
keywords_extracted=10,
|
||||
timing_ms=3000,
|
||||
)
|
||||
|
||||
assert summary.wiki_results == 3
|
||||
assert summary.timing_ms == 3000
|
||||
|
||||
def test_research_summary_defaults(self):
|
||||
"""Test ResearchSummary default values."""
|
||||
summary = ResearchSummary()
|
||||
|
||||
assert summary.wiki_results == 0
|
||||
assert summary.timing_ms == 0
|
||||
|
||||
def test_entity_linking_model(self):
|
||||
"""Test EntityLinking model."""
|
||||
linking = EntityLinking(
|
||||
forward_links=5,
|
||||
backward_links=3,
|
||||
pages_updated=2,
|
||||
)
|
||||
|
||||
assert linking.forward_links == 5
|
||||
assert linking.pages_updated == 2
|
||||
|
||||
def test_smart_create_response_model(self):
|
||||
"""Test SmartCreateResponse model."""
|
||||
page = WikiPage(id=1, path="/test", title="Test")
|
||||
response = SmartCreateResponse(
|
||||
page=page,
|
||||
sources_used=10,
|
||||
search_id="uuid-456",
|
||||
)
|
||||
|
||||
assert response.page.id == 1
|
||||
assert response.sources_used == 10
|
||||
assert response.search_id == "uuid-456"
|
||||
Reference in New Issue
Block a user