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

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

No files matched your search

@@ -0,0 +1,205 @@
"""
Copyright 2024-2026 ChatterMate
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 MagicMock, patch
import numpy as np
from agno.document import Document
from agno.vectordb.pgvector.pgvector import PgVector
from app.knowledge.optimized_pgvector import OptimizedPgVector
class TestOptimizedPgVector:
"""Test cases for OptimizedPgVector"""
@pytest.fixture
def mock_embedder(self):
"""Create a mock embedder"""
mock = MagicMock()
mock.get_embedding.return_value = np.array([0.1, 0.2, 0.3])
mock.dimensions = 3
return mock
@pytest.fixture
def mock_session(self):
"""Create a mock database session"""
mock = MagicMock()
# Configure session to return a context manager
mock.return_value.__enter__.return_value = mock
return mock
@patch('sqlalchemy.engine.create_engine')
def test_init(self, mock_create_engine, mock_embedder):
"""Test initialization"""
# Arrange
mock_engine = MagicMock()
mock_create_engine.return_value = mock_engine
# Act
db = OptimizedPgVector(
table_name="test_table",
db_url="postgresql+psycopg://user:pass@localhost/testdb",
embedder=mock_embedder
)
# Assert
assert db.table_name == "test_table"
assert db.embedder == mock_embedder
assert db.dimensions == 3
@patch('sqlalchemy.engine.create_engine')
def test_upsert_with_pre_embedded_documents(self, mock_create_engine, mock_embedder, mock_session):
"""Test upserting pre-embedded documents"""
# Arrange
mock_engine = MagicMock()
mock_create_engine.return_value = mock_engine
db = OptimizedPgVector(
table_name="test_table",
db_url="postgresql+psycopg://user:pass@localhost/testdb",
embedder=mock_embedder
)
db.Session = mock_session
db._clean_content = lambda x: x # Mock the content cleaning function
# Create a document with an existing embedding
pre_embedded_doc = Document(
id="test1",
name="Test Document 1",
content="This is a test document",
embedding=np.array([0.5, 0.6, 0.7])
)
# Create a document without an embedding
unembedded_doc = Document(
id="test2",
name="Test Document 2",
content="This is another test document"
)
# Mock the document's embed method
with patch.object(Document, 'embed') as mock_embed:
# Act
with patch('sqlalchemy.dialects.postgresql.insert') as mock_insert:
mock_insert.return_value.on_conflict_do_update.return_value = "upsert_stmt"
db.upsert([pre_embedded_doc, unembedded_doc])
# Assert
# The document's embed method should be called for the unembedded document
mock_embed.assert_called_once_with(embedder=mock_embedder)
# Both documents should be passed to the upsert statement
assert mock_session.execute.call_count == 1
@patch('sqlalchemy.engine.create_engine')
def test_insert_with_pre_embedded_documents(self, mock_create_engine, mock_embedder, mock_session):
"""Test inserting pre-embedded documents"""
# Arrange
mock_engine = MagicMock()
mock_create_engine.return_value = mock_engine
db = OptimizedPgVector(
table_name="test_table",
db_url="postgresql+psycopg://user:pass@localhost/testdb",
embedder=mock_embedder
)
db.Session = mock_session
db._clean_content = lambda x: x # Mock the content cleaning function
# Create a document with an existing embedding
pre_embedded_doc = Document(
id="test1",
name="Test Document 1",
content="This is a test document",
embedding=np.array([0.5, 0.6, 0.7])
)
# Create a document without an embedding
unembedded_doc = Document(
id="test2",
name="Test Document 2",
content="This is another test document"
)
# Mock the document's embed method
with patch.object(Document, 'embed') as mock_embed:
# Act
with patch('sqlalchemy.dialects.postgresql.insert') as mock_insert:
db.insert([pre_embedded_doc, unembedded_doc])
# Assert
# The document's embed method should be called for the unembedded document
mock_embed.assert_called_once_with(embedder=mock_embedder)
# Both documents should be passed to the insert statement
assert mock_session.execute.call_count == 1
if __name__ == "__main__":
pytest.main(["-v", "test_optimized_pgvector.py"])
class _PgError(Exception):
"""Stand-in for a psycopg error, which carries the SQLSTATE as .sqlstate."""
def __init__(self, sqlstate):
super().__init__(sqlstate)
self.sqlstate = sqlstate
class TestConcurrentTableCreate:
"""The per-org vector table is created by whichever queue item gets there first.
agno's PgVector.create() does table_exists() and then CREATE TABLE, which is
not atomic. The processor runs two items at once, so an organisation whose
first two sources are queued together had both workers create the table and
the loser died with
`duplicate key value violates unique constraint "pg_type_typname_nsp_index"`,
losing that source (production queue item 372, goldmaidsteam.com).
"""
def _vector_db(self):
return OptimizedPgVector.__new__(OptimizedPgVector)
@pytest.mark.parametrize("sqlstate", ["42P07", "23505"])
def test_losing_the_create_race_is_not_an_error(self, sqlstate):
db = self._vector_db()
db.table_name = "d_org"
db.schema = "ai"
with patch.object(PgVector, "create", side_effect=_PgError(sqlstate)):
db.create() # the other worker created it; that is the table we wanted
def test_a_real_create_failure_still_raises(self):
db = self._vector_db()
db.table_name = "d_org"
db.schema = "ai"
with patch.object(PgVector, "create", side_effect=_PgError("42501")):
with pytest.raises(_PgError):
db.create()
def test_duplicate_detected_through_sqlalchemy_wrapping(self):
"""SQLAlchemy wraps the driver error; the SQLSTATE lives on .orig."""
wrapped = RuntimeError("duplicate")
wrapped.orig = _PgError("23505")
with patch.object(PgVector, "create", side_effect=wrapped):
db = self._vector_db()
db.table_name = "d_org"
db.schema = "ai"
db.create()