test: 补充知识处理、检索与 Agent 回归用例

覆盖切块、向量检索、工具调用和历史预算;模型相关用例使用桩。
This commit is contained in:
WangShen007 committed 2026-10-04 20:55:00 +08:00
1 parent 2dd062787d
commit d9fe727c3f
123 files changed
+29005

No files matched your search

@@ -0,0 +1,811 @@
"""
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import pytest
from unittest.mock import patch, MagicMock
from app.tools.knowledge_search_byagent import KnowledgeSearchByAgent
from app.models.knowledge import Knowledge, SourceType
from uuid import uuid4
from agno.knowledge.agent import AgentKnowledge
from agno.vectordb.pgvector import PgVector, SearchType
import os
@pytest.fixture
def mock_db():
"""Create a mock database session"""
db = MagicMock()
return db
@pytest.fixture
def mock_knowledge():
"""Create a mock knowledge entry"""
knowledge_id = uuid4()
org_id = uuid4()
return Knowledge(
id=knowledge_id,
organization_id=org_id,
source="test_document.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
)
@pytest.fixture
def mock_vector_db():
"""Create a mock vector database"""
vector_db = MagicMock(spec=PgVector)
vector_db.table_name = "test_table"
vector_db.schema = "test_schema"
vector_db.search_type = SearchType.hybrid
# Mock the search method to avoid OpenAI API key requirement
vector_db.search.return_value = []
return vector_db
@pytest.fixture
def mock_agent_knowledge(mock_vector_db):
"""Create a mock agent knowledge"""
agent_knowledge = MagicMock(spec=AgentKnowledge)
# Mock the search method to return an empty list by default
agent_knowledge.search.return_value = []
# Set the vector_db attribute
agent_knowledge.vector_db = mock_vector_db
return agent_knowledge
@pytest.fixture
def mock_ai_config():
"""Create a mock AI config"""
ai_config = MagicMock()
ai_config.encrypted_api_key = "test-key"
return ai_config
@pytest.fixture
def knowledge_search_tool(mock_db, mock_knowledge, mock_vector_db, mock_agent_knowledge, mock_ai_config):
"""Create a KnowledgeSearchByAgent instance with mocked dependencies"""
agent_id = str(uuid4())
org_id = uuid4()
# Set up environment variables first
os.environ['OPENAI_API_KEY'] = 'test-key'
# Create a real instance but with mocked dependencies
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class:
# Configure mocks
mock_session_local.return_value.__enter__.return_value = mock_db
mock_pg_vector.return_value = mock_vector_db
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Create a mock for KnowledgeRepository
mock_knowledge_repo = MagicMock()
mock_knowledge_repo_class.return_value = mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
# Create the tool instance
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Store the mock objects for later access in tests
tool._mock_knowledge_repo = mock_knowledge_repo
# Set the agent_knowledge directly
tool.agent_knowledge = mock_agent_knowledge
# Create a patched version of search_knowledge_base that doesn't depend on database
original_search = tool.search_knowledge_base
def patched_search_knowledge_base(query):
try:
# Get knowledge sources linked to this agent
knowledge_sources = tool._mock_knowledge_repo.get_by_agent(tool.agent_id)
if not knowledge_sources:
return "No knowledge sources available for this agent."
# Search with agent_id filter
documents = tool.agent_knowledge.search(
query=query,
num_documents=5,
filters={"agent_id": [str(tool.agent_id)]}
)
search_results = []
for doc in documents:
if doc.content is None:
doc.content = "" # Convert None to empty string
# Find the source type from knowledge sources
source_type = next(
(source.source_type.value.lower() for source in knowledge_sources if source.source == doc.name),
'file' # Default to 'file' for tests
)
search_results.append({
'content': doc.content,
'source_type': source_type,
'name': doc.name or 'Untitled',
'similarity': doc.score if hasattr(doc, 'score') else 0.0
})
if not search_results:
return "No relevant information found in the knowledge base."
# Sort by similarity and format results
search_results.sort(key=lambda x: x['similarity'], reverse=True)
# Format all results for testing
formatted_results = []
for result in search_results:
percent = int(result['similarity'] * 100)
formatted_results.append(
f"[{result['source_type'].upper()} - {result['name']}] {result['content']}\nRelevance: {percent}%"
)
return "\n\n".join(formatted_results)
except Exception as e:
return f"Error searching knowledge base: {str(e)}"
# Replace the search method with our patched version
tool.search_knowledge_base = patched_search_knowledge_base
return tool
def test_search_knowledge_base_success(knowledge_search_tool):
"""Test successful knowledge base search"""
# Setup
query = "test query"
# Create mock document
mock_doc = MagicMock()
mock_doc.name = "test_document.pdf" # Set name as a string
mock_doc.content = "Test content"
mock_doc.score = 0.95
# Configure mocks
knowledge_search_tool.agent_knowledge.search.return_value = [mock_doc]
# Mock the knowledge repository's get_by_agent method
mock_knowledge = MagicMock()
mock_knowledge.source = "test_document.pdf"
mock_knowledge.source_type = SourceType.FILE # Use the actual enum
# Get the knowledge_repo from the fixture
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
# Execute
result = knowledge_search_tool.search_knowledge_base(query)
# Assert
assert "[FILE - test_document.pdf] Test content" in result
assert "Relevance: 95%" in result
def test_search_knowledge_base_no_sources(knowledge_search_tool):
"""Test knowledge base search with no sources"""
# Setup
# Mock the knowledge repository's get_by_agent method to return empty list
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = []
# Execute
result = knowledge_search_tool.search_knowledge_base("test query")
# Assert
assert result == "No knowledge sources available for this agent."
def test_search_knowledge_base_no_results(knowledge_search_tool):
"""Test knowledge base search with no search results"""
# Setup
knowledge_search_tool.agent_knowledge.search.return_value = []
# Mock the knowledge repository's get_by_agent method
mock_knowledge = MagicMock()
mock_knowledge.source = "test_document.pdf"
mock_knowledge.source_type = "FILE"
# Get the knowledge_repo from the fixture
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
# Execute
result = knowledge_search_tool.search_knowledge_base("test query")
# Assert
assert result == "No relevant information found in the knowledge base."
def test_search_knowledge_base_multiple_results(knowledge_search_tool):
"""Test knowledge base search with multiple results"""
# Setup
query = "test query"
# Create mock documents with string values for name
mock_docs = []
for i, (name, content, score) in enumerate([
("doc1.pdf", "Content 1", 0.95),
("doc2.pdf", "Content 2", 0.85),
("doc3.pdf", "Content 3", 0.75),
("doc4.pdf", "Content 4", 0.65)
]):
doc = MagicMock()
doc.name = name # Set name as a string
doc.content = content
doc.score = score
mock_docs.append(doc)
knowledge_search_tool.agent_knowledge.search.return_value = mock_docs
# Set up mock knowledge sources to match document names
mock_knowledge_sources = [
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc1.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
),
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc2.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
),
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc3.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
),
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc4.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
)
]
# Get the knowledge_repo from the fixture
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = mock_knowledge_sources
# Execute
result = knowledge_search_tool.search_knowledge_base(query)
# Assert
assert "[FILE - doc1.pdf] Content 1" in result
assert "[FILE - doc2.pdf] Content 2" in result
assert "[FILE - doc3.pdf] Content 3" in result
assert "[FILE - doc4.pdf] Content 4" in result
assert "Relevance: 95%" in result
assert "Relevance: 85%" in result
assert "Relevance: 75%" in result
assert "Relevance: 65%" in result
def test_search_knowledge_base_empty_content(knowledge_search_tool):
"""Test knowledge base search with empty content in results"""
# Setup
# Create mock documents with string values for name
mock_docs = []
for name, content, score in [
("doc1.pdf", "", 0.95), # Empty content
("doc2.pdf", None, 0.85), # None content
("doc3.pdf", "Valid content", 0.75)
]:
doc = MagicMock()
doc.name = name # Set name as a string
doc.content = content
doc.score = score
mock_docs.append(doc)
knowledge_search_tool.agent_knowledge.search.return_value = mock_docs
# Set up mock knowledge sources to match document names
mock_knowledge_sources = [
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc1.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
),
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc2.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
),
Knowledge(
id=uuid4(),
organization_id=uuid4(),
source="doc3.pdf",
source_type=SourceType.FILE,
table_name="test_table",
schema="test_schema"
)
]
# Get the knowledge_repo from the fixture
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = mock_knowledge_sources
# Execute
result = knowledge_search_tool.search_knowledge_base("test query")
# Assert
assert "[FILE - doc1.pdf] " in result # Empty content should still be included
assert "[FILE - doc2.pdf] " in result # None content should be included as empty
assert "[FILE - doc3.pdf] Valid content" in result
assert "Relevance: 95%" in result
assert "Relevance: 85%" in result
assert "Relevance: 75%" in result
def test_search_knowledge_base_error(knowledge_search_tool):
"""Test knowledge base search with an error during search"""
# Setup
query = "test query"
# Mock the knowledge repository's get_by_agent method
mock_knowledge = MagicMock()
mock_knowledge.source = "test_document.pdf"
mock_knowledge.source_type = SourceType.FILE # Use the actual enum
# Get the knowledge_repo from the fixture
mock_knowledge_repo = knowledge_search_tool._mock_knowledge_repo
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
# Make the search method raise an exception
knowledge_search_tool.agent_knowledge.search.side_effect = Exception("Search error")
# Execute
result = knowledge_search_tool.search_knowledge_base(query)
# Assert
assert "Error searching knowledge base" in result
# Tests for real implementation
def test_real_search_knowledge_base_with_results():
"""Test real search_knowledge_base implementation with mocked dependencies"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class:
# Setup mocks for initialization
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = "encrypted_key"
# Create the tool
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Setup mocks for search
mock_knowledge = MagicMock()
mock_knowledge.source = "test.pdf"
mock_knowledge.source_type = SourceType.FILE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
# Mock vector db and agent knowledge for search
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
mock_doc = MagicMock()
mock_doc.name = "test.pdf"
mock_doc.content = "Test content"
mock_doc.score = 0.9
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search
result = tool.search_knowledge_base("test query")
# Assert
assert "test.pdf" in result
assert "Test content" in result
def test_real_search_knowledge_base_no_ai_config():
"""Test initialization when no AI config exists"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local:
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
# No AI config available
# Should not raise an error
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
assert tool.agent_id == agent_id
assert tool.org_id == org_id
def test_real_search_knowledge_base_with_source_filter():
"""Test search with source filter"""
agent_id = str(uuid4())
org_id = uuid4()
source_filter = "specific_doc.pdf"
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class:
# Setup mocks for initialization
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = "encrypted_key"
# Create tool with source filter
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id, source=source_filter)
assert tool.source == source_filter
# Setup mocks for search
mock_knowledge = MagicMock()
mock_knowledge.source = source_filter
mock_knowledge.source_type = SourceType.FILE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
# Mock vector db and agent knowledge
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
mock_doc = MagicMock()
mock_doc.name = source_filter
mock_doc.content = "Filtered content"
mock_doc.score = 0.85
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search
result = tool.search_knowledge_base("test query")
# Verify source filter was applied
search_call = mock_agent_knowledge.search.call_args
assert search_call is not None
filters = search_call.kwargs.get('filters', {})
assert 'name' in filters
assert filters['name'] == source_filter
def test_real_search_knowledge_base_no_content_docs():
"""Test search when documents have no content"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class:
# Setup mocks
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = None
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Setup search mocks
mock_knowledge = MagicMock()
mock_knowledge.source = "test.pdf"
mock_knowledge.source_type = SourceType.FILE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
# Documents with no content
mock_doc1 = MagicMock()
mock_doc1.name = "test.pdf"
mock_doc1.content = None
mock_doc1.score = 0.9
mock_doc2 = MagicMock()
mock_doc2.name = "test2.pdf"
mock_doc2.content = ""
mock_doc2.score = 0.8
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc1, mock_doc2]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search
result = tool.search_knowledge_base("test query")
# When all documents have no content, should return no results found
assert "No relevant information found" in result
def test_real_search_knowledge_base_unknown_source_type():
"""Test search with unknown source type"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class:
# Setup mocks
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = "key"
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Setup search with mismatched source name
mock_knowledge = MagicMock()
mock_knowledge.source = "known_source.pdf"
mock_knowledge.source_type = SourceType.FILE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
# Document with name that doesn't match any knowledge source
mock_doc = MagicMock()
mock_doc.name = "unknown_source.pdf"
mock_doc.content = "Content from unknown source"
mock_doc.score = 0.9
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search
result = tool.search_knowledge_base("test query")
# Should fallback to 'unknown' source type
assert "UNKNOWN" in result or "unknown_source.pdf" in result
def test_real_search_knowledge_base_document_without_score():
"""Test search with documents that don't have score attribute"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class:
# Setup mocks
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = "key"
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Setup search
mock_knowledge = MagicMock()
mock_knowledge.source = "test.pdf"
mock_knowledge.source_type = SourceType.WEBSITE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
# Document without score attribute
mock_doc = MagicMock(spec=['name', 'content'])
mock_doc.name = "test.pdf"
mock_doc.content = "Test content"
# Don't set score attribute - hasattr will return False
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search
result = tool.search_knowledge_base("test query")
# Should handle missing score gracefully (default to 0.0)
assert "test.pdf" in result
assert "Test content" in result
def test_real_search_knowledge_base_exception_handling():
"""Test exception handling in real search_knowledge_base"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class:
# Setup mocks for initialization
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = None
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Make the repository raise an exception during search
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.side_effect = Exception("Database connection error")
mock_knowledge_repo_class.return_value = mock_knowledge_repo
# Execute search - should catch exception and return error message
result = tool.search_knowledge_base("test query")
# Assert error message is returned
assert "Error searching knowledge base" in result
def test_real_search_knowledge_base_lazy_init():
"""Test lazy initialization of agent_knowledge during search"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class, \
patch('app.tools.knowledge_search_byagent.PgVector') as mock_pg_vector, \
patch('app.tools.knowledge_search_byagent.AgentKnowledge') as mock_agent_knowledge_class, \
patch('app.tools.knowledge_search_byagent.get_embedder') as mock_embedder_class:
# Setup mocks for initialization
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = "key"
# Create tool - agent_knowledge should be None initially
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Explicitly set agent_knowledge to None to test lazy initialization
tool.agent_knowledge = None
# Setup mocks for search
mock_knowledge = MagicMock()
mock_knowledge.source = "test.pdf"
mock_knowledge.source_type = SourceType.FILE
mock_knowledge.table_name = "test_table"
mock_knowledge.schema = "test_schema"
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = [mock_knowledge]
mock_knowledge_repo_class.return_value = mock_knowledge_repo
# Mock embedder
mock_embedder = MagicMock()
mock_embedder_class.return_value = mock_embedder
# Mock vector db
mock_vector_db = MagicMock()
mock_pg_vector.return_value = mock_vector_db
# Mock agent knowledge
mock_doc = MagicMock()
mock_doc.name = "test.pdf"
mock_doc.content = "Lazy init content"
mock_doc.score = 0.9
mock_agent_knowledge = MagicMock()
mock_agent_knowledge.search.return_value = [mock_doc]
mock_agent_knowledge_class.return_value = mock_agent_knowledge
# Execute search - should initialize agent_knowledge
result = tool.search_knowledge_base("test query")
# Verify initialization occurred
mock_embedder_class.assert_called_once()
mock_pg_vector.assert_called_once()
mock_agent_knowledge_class.assert_called_once()
# Verify search was successful
assert "test.pdf" in result
assert "Lazy init content" in result
def test_real_search_knowledge_base_no_sources_real():
"""Test real implementation when no knowledge sources are available"""
agent_id = str(uuid4())
org_id = uuid4()
with patch('app.tools.knowledge_search_byagent.SessionLocal') as mock_session_local, \
patch('app.tools.knowledge_search_byagent.KnowledgeRepository') as mock_knowledge_repo_class:
# Setup mocks for initialization
mock_db = MagicMock()
mock_session_local.return_value.__enter__.return_value = mock_db
mock_session_local.return_value.__exit__.return_value = None
mock_ai_config = MagicMock()
mock_ai_config.encrypted_api_key = None
tool = KnowledgeSearchByAgent(agent_id=agent_id, org_id=org_id)
# Return empty list for knowledge sources
mock_knowledge_repo = MagicMock()
mock_knowledge_repo.get_by_agent.return_value = []
mock_knowledge_repo_class.return_value = mock_knowledge_repo
# Execute search
result = tool.search_knowledge_base("test query")
# Should return no sources message
assert "No knowledge sources available for this agent" in result