Skip to content

Commit 8453040

Browse files
committed
Testing for qdrant service
1 parent 4ecfca9 commit 8453040

File tree

4 files changed

+140
-6
lines changed

4 files changed

+140
-6
lines changed

backend/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Added support for queries without source data in vector database
1313
- Graceful failure of triple export when no chunks are found
14+
- Tested Qdrant vector database service
1415

1516
### Changed
1617

backend/src/app/schemas/query_api.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import Any, List, Optional, Union
44

5-
from pydantic import BaseModel
5+
from pydantic import BaseModel, ConfigDict
66

77
from app.models.query_core import Chunk, FormatType, Rule
88

@@ -23,10 +23,7 @@ class QueryRequestSchema(BaseModel):
2323
document_id: str
2424
prompt: QueryPromptSchema
2525

26-
class Config:
27-
"""Pydantic configuration."""
28-
29-
extra = "allow"
26+
model_config = ConfigDict(extra="allow")
3027

3128

3229
class VectorResponseSchema(BaseModel):

backend/src/app/services/vector_db/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def get_embeddings(
9090
async def prepare_chunks(
9191
self, document_id: str, chunks: List[Document]
9292
) -> List[Dict[str, Any]]:
93-
"""Prepare chunks for insertion into the Milvus database."""
93+
"""Prepare chunks for insertion into the vector database."""
9494
logger.info(f"Preparing {len(chunks)} chunks")
9595

9696
# Clean the chunks
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
from unittest.mock import Mock, patch
2+
3+
import pytest
4+
5+
from app.schemas.query_api import VectorResponseSchema
6+
from app.services.vector_db.qdrant_service import QdrantService
7+
8+
9+
@pytest.fixture
10+
def mock_qdrant_client():
11+
with patch("app.services.vector_db.qdrant_service.QdrantClient") as mock:
12+
client = Mock()
13+
# Set up mock responses
14+
client.collection_exists.return_value = True
15+
client.upsert.return_value = None
16+
17+
# Use a simple Mock instead of Qdrant models
18+
response_mock = Mock()
19+
response_mock.points = [
20+
Mock(
21+
payload={
22+
"text": "test text",
23+
"page_number": 1,
24+
"chunk_number": 1,
25+
"document_id": "test_doc",
26+
}
27+
)
28+
]
29+
client.query_points.return_value = response_mock
30+
31+
client.delete.return_value = None
32+
mock.return_value = client
33+
yield client
34+
35+
36+
@pytest.fixture
37+
def qdrant_service(
38+
mock_embeddings_service,
39+
mock_llm_service,
40+
test_settings,
41+
mock_qdrant_client,
42+
):
43+
service = QdrantService(
44+
embedding_service=mock_embeddings_service,
45+
llm_service=mock_llm_service,
46+
settings=test_settings,
47+
)
48+
# Override the client with our mock
49+
service.client = mock_qdrant_client
50+
return service
51+
52+
53+
@pytest.mark.asyncio
54+
async def test_ensure_collection_exists(qdrant_service):
55+
await qdrant_service.ensure_collection_exists()
56+
assert qdrant_service.client.collection_exists.called
57+
58+
59+
@pytest.mark.asyncio
60+
async def test_upsert_vectors(qdrant_service):
61+
vectors = [
62+
{
63+
"id": "1",
64+
"vector": [0.1, 0.2],
65+
"text": "test",
66+
"page_number": 1,
67+
"chunk_number": 1,
68+
"document_id": "doc1",
69+
}
70+
]
71+
72+
result = await qdrant_service.upsert_vectors(vectors)
73+
74+
assert "message" in result
75+
assert qdrant_service.client.upsert.called
76+
77+
78+
@pytest.mark.asyncio
79+
async def test_vector_search(qdrant_service, mock_embeddings_service):
80+
mock_embeddings_service.get_embeddings.return_value = [0.1, 0.2]
81+
82+
result = await qdrant_service.vector_search(["test query"], "test_doc")
83+
84+
assert isinstance(result, VectorResponseSchema)
85+
assert result.message == "Query processed successfully."
86+
assert qdrant_service.client.query_points.called
87+
88+
89+
@pytest.mark.asyncio
90+
async def test_hybrid_search(qdrant_service, mock_embeddings_service):
91+
# Mock the embedding service response
92+
mock_embeddings_service.get_embeddings.return_value = [0.1, 0.2]
93+
94+
# Mock the extract_keywords method directly on the qdrant_service
95+
# since it's a method of QdrantService, not CompletionService
96+
with patch.object(
97+
qdrant_service,
98+
"extract_keywords",
99+
return_value=["keyword1", "keyword2"],
100+
):
101+
result = await qdrant_service.hybrid_search(
102+
"test query", "test_doc", []
103+
)
104+
105+
assert isinstance(result, VectorResponseSchema)
106+
assert result.message == "Query processed successfully."
107+
assert qdrant_service.client.query_points.called
108+
109+
110+
@pytest.mark.asyncio
111+
async def test_decomposed_search(qdrant_service, mock_llm_service):
112+
mock_llm_service.decompose_query.return_value = {
113+
"sub-queries": ["query1", "query2"]
114+
}
115+
116+
result = await qdrant_service.decomposed_search(
117+
"test query", "test_doc", []
118+
)
119+
120+
assert "sub_queries" in result
121+
assert "chunks" in result
122+
123+
124+
@pytest.mark.asyncio
125+
async def test_delete_document(qdrant_service):
126+
result = await qdrant_service.delete_document("test_doc")
127+
128+
assert result["status"] == "success"
129+
assert result["message"] == "Document deleted successfully."
130+
assert qdrant_service.client.delete.called
131+
132+
133+
@pytest.mark.asyncio
134+
async def test_keyword_search_not_implemented(qdrant_service):
135+
with pytest.raises(NotImplementedError):
136+
await qdrant_service.keyword_search("query", "doc_id", ["keyword"])

0 commit comments

Comments
 (0)