Initial commit: import from sinmb79/Gov-chat-bot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
Tier B RAG 라우팅 테스트.
|
||||
"""
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from uuid import uuid4
|
||||
from datetime import datetime
|
||||
|
||||
from app.services.routing import ResponseRouter
|
||||
from app.providers.base import SearchResult
|
||||
|
||||
|
||||
def make_router(embedding=None, vectordb=None) -> ResponseRouter:
|
||||
providers = {}
|
||||
if embedding:
|
||||
providers["embedding"] = embedding
|
||||
if vectordb:
|
||||
providers["vectordb"] = vectordb
|
||||
return ResponseRouter(
|
||||
tenant_config={
|
||||
"phone_number": "031-860-2000",
|
||||
"fallback_dept": "민원과",
|
||||
"tenant_name": "동두천시",
|
||||
},
|
||||
providers=providers,
|
||||
)
|
||||
|
||||
|
||||
def make_doc(doc_id: str) -> MagicMock:
|
||||
doc = MagicMock()
|
||||
doc.id = doc_id
|
||||
doc.filename = "안내문.txt"
|
||||
doc.is_active = True
|
||||
doc.published_at = datetime(2026, 3, 1)
|
||||
return doc
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tier_b_returns_when_doc_found():
|
||||
"""문서 RAG 매칭 → Tier B 반환."""
|
||||
doc_id = str(uuid4())
|
||||
doc = make_doc(doc_id)
|
||||
|
||||
embedding = AsyncMock()
|
||||
embedding.embed = AsyncMock(return_value=[[0.1] * 768])
|
||||
|
||||
vectordb = AsyncMock()
|
||||
vectordb.search = AsyncMock(return_value=[
|
||||
SearchResult(text="여권은 민원과에서 발급합니다.", doc_id=f"{doc_id}_0",
|
||||
score=0.75, metadata={"doc_id": doc_id})
|
||||
])
|
||||
|
||||
db = AsyncMock()
|
||||
db.execute = AsyncMock()
|
||||
scalar = MagicMock()
|
||||
scalar.scalar_one_or_none = MagicMock(return_value=doc)
|
||||
db.execute.return_value = scalar
|
||||
|
||||
router = make_router(embedding=embedding, vectordb=vectordb)
|
||||
result = await router.route("tenant-1", "여권 발급 방법", "user-1", db=db)
|
||||
|
||||
assert result.tier == "B"
|
||||
assert result.source == "rag"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tier_b_skipped_when_no_doc_match():
|
||||
"""문서 매칭 없으면 Tier D."""
|
||||
embedding = AsyncMock()
|
||||
embedding.embed = AsyncMock(return_value=[[0.1] * 768])
|
||||
|
||||
vectordb = AsyncMock()
|
||||
vectordb.search = AsyncMock(return_value=[])
|
||||
|
||||
db = AsyncMock()
|
||||
|
||||
router = make_router(embedding=embedding, vectordb=vectordb)
|
||||
result = await router.route("tenant-1", "모르는 질문", "user-1", db=db)
|
||||
|
||||
assert result.tier == "D"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tier_b_result_has_doc_name():
|
||||
"""Tier B 결과에 doc_name 포함."""
|
||||
doc_id = str(uuid4())
|
||||
doc = make_doc(doc_id)
|
||||
|
||||
embedding = AsyncMock()
|
||||
embedding.embed = AsyncMock(return_value=[[0.1] * 768])
|
||||
|
||||
vectordb = AsyncMock()
|
||||
vectordb.search = AsyncMock(return_value=[
|
||||
SearchResult(text="내용", doc_id=f"{doc_id}_0", score=0.72,
|
||||
metadata={"doc_id": doc_id})
|
||||
])
|
||||
|
||||
db = AsyncMock()
|
||||
db.execute = AsyncMock()
|
||||
scalar = MagicMock()
|
||||
scalar.scalar_one_or_none = MagicMock(return_value=doc)
|
||||
db.execute.return_value = scalar
|
||||
|
||||
router = make_router(embedding=embedding, vectordb=vectordb)
|
||||
result = await router.route("tenant-1", "질문", "user-1", db=db)
|
||||
|
||||
assert result.doc_name is not None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tier_a_takes_priority_over_tier_b():
|
||||
"""FAQ 유사도 ≥ 0.85 → Tier A (Tier B 미실행)."""
|
||||
faq_id = str(uuid4())
|
||||
faq = MagicMock()
|
||||
faq.id = faq_id
|
||||
faq.answer = "FAQ 답변"
|
||||
faq.question = "질문"
|
||||
faq.is_active = True
|
||||
faq.hit_count = 0
|
||||
faq.updated_at = None
|
||||
|
||||
embedding = AsyncMock()
|
||||
embedding.embed = AsyncMock(return_value=[[0.1] * 768])
|
||||
|
||||
# FAQ 검색: 높은 유사도
|
||||
vectordb = AsyncMock()
|
||||
vectordb.search = AsyncMock(return_value=[
|
||||
SearchResult(text="Q\nA", doc_id=f"{faq_id}_0", score=0.92,
|
||||
metadata={"faq_id": faq_id})
|
||||
])
|
||||
|
||||
db = AsyncMock()
|
||||
db.execute = AsyncMock()
|
||||
scalar = MagicMock()
|
||||
scalar.scalar_one_or_none = MagicMock(return_value=faq)
|
||||
db.execute.return_value = scalar
|
||||
db.get = AsyncMock(return_value=faq)
|
||||
db.commit = AsyncMock()
|
||||
|
||||
router = make_router(embedding=embedding, vectordb=vectordb)
|
||||
result = await router.route("tenant-1", "질문", "user-1", db=db)
|
||||
|
||||
# FAQ가 먼저 매칭되면 Tier A
|
||||
assert result.tier == "A"
|
||||
Reference in New Issue
Block a user