acb / tests /test_query_handler.py
Kagan Tek
merge
79d4fd5
Raw
History Blame Contribute Delete
15.4 kB
import pytest
import time
from unittest.mock import MagicMock, patch
from integration.query_handler import QueryHandler, QueryMode, QueryResult, get_query_handler, reset_query_handler
from agent import IntentType
def _make_handler(mode=QueryMode.AUTO):
return QueryHandler(default_mode=mode)
class TestQueryResult:
def test_success_when_no_error(self):
r = QueryResult(query="test", response="ok", mode=QueryMode.DOCUMENTS)
assert r.success is True
def test_failure_when_error(self):
r = QueryResult(query="test", response="fail", mode=QueryMode.DOCUMENTS, error="boom")
assert r.success is False
def test_to_dict_contains_all_keys(self):
r = QueryResult(query="q", response="r", mode=QueryMode.DATABASE, sql_query="SELECT 1")
d = r.to_dict()
assert d["query"] == "q"
assert d["mode"] == "database"
assert d["sql_query"] == "SELECT 1"
assert d["success"] is True
class TestDecideExecution:
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_database_mode_when_db_enabled(self):
handler = _make_handler()
assert handler._decide_execution(QueryMode.DATABASE, "anything") == "database"
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": False})
def test_database_mode_falls_to_documents_when_disabled(self):
handler = _make_handler()
assert handler._decide_execution(QueryMode.DATABASE, "anything") == "documents"
def test_documents_mode_always_documents(self):
handler = _make_handler()
assert handler._decide_execution(QueryMode.DOCUMENTS, "anything") == "documents"
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_hybrid_with_db_signals(self):
handler = _make_handler()
assert handler._decide_execution(QueryMode.HYBRID, "26000046 statü") == "database"
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_hybrid_without_db_signals(self):
handler = _make_handler()
assert handler._decide_execution(QueryMode.HYBRID, "merhaba nasılsın") == "documents"
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
@patch("integration.query_handler.get_intent_router")
def test_auto_delegates_to_router(self, mock_router_fn):
decision = MagicMock()
decision.execution_mode = "database"
router = MagicMock()
router.route.return_value = decision
mock_router_fn.return_value = router
handler = _make_handler()
result = handler._decide_execution(QueryMode.AUTO, "26000046 teklif")
assert result == "database"
router.route.assert_called_once()
class TestExecuteDatabase:
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.query_and_format")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_returns_db_result(self, mock_metrics, mock_qf, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("26000046 statü", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
mock_qf.return_value = {
"success": True,
"response": "26000046 teklifinin statüsü: Teklif.",
"sql": "SELECT * FROM v_CurrentCreditFileStatus WHERE CreditFileId = 26000046",
"rows": [{"CreditFileId": 26000046, "StatusName": "Teklif"}],
}
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("26000046 statü", mode=QueryMode.DATABASE)
assert result.mode == QueryMode.DATABASE
assert result.sql_query is not None
assert result.db_results is not None
assert "Teklif" in result.response
assert result.intent == IntentType.DATABASE_QUERY.value
class TestExecuteDocuments:
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.get_retrieval_engine")
@patch("integration.query_handler.get_llm_generator")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": False})
def test_returns_document_result(self, mock_metrics, mock_gen, mock_engine, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("Atlas nedir", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
engine = MagicMock()
engine.retrieve.return_value = ([{"text": "Atlas bir sistemdir", "metadata": {}, "source": "doc.pdf", "score": 0.9}], {})
engine.build_context.return_value = "Atlas bir sistemdir"
mock_engine.return_value = engine
generator = MagicMock()
generator.generate.return_value = "Atlas, kredi süreçlerini yöneten bir sistemdir."
mock_gen.return_value = generator
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("Atlas nedir", mode=QueryMode.DOCUMENTS)
assert result.mode == QueryMode.DOCUMENTS
assert result.intent == IntentType.DOCUMENT_QUERY.value
assert len(result.sources) == 1
assert result.error is None
class TestExecuteConversational:
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.get_intent_router")
@patch("integration.query_handler.get_llm_generator")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": False})
def test_greeting_goes_conversational(self, mock_metrics, mock_gen, mock_router_fn, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("merhaba", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
decision = MagicMock()
decision.execution_mode = "conversational"
router = MagicMock()
router.route.return_value = decision
mock_router_fn.return_value = router
generator = MagicMock()
generator.generate.return_value = "Merhaba! Size nasıl yardımcı olabilirim?"
mock_gen.return_value = generator
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("merhaba", mode=QueryMode.AUTO)
assert result.intent == IntentType.CONVERSATIONAL.value
assert "Merhaba" in result.response
class TestHybridMode:
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.query_and_format")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_hybrid_routes_to_db_with_signals(self, mock_metrics, mock_qf, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("26000046 statü", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
mock_qf.return_value = {
"success": True,
"response": "26000046 teklifinin statüsü: Sözleşme ve Ekleri Hazır.",
"sql": "SELECT ...",
"rows": [{"CreditFileId": 26000046, "StatusName": "Sözleşme ve Ekleri Hazır"}],
}
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("26000046 statü", mode=QueryMode.HYBRID)
assert result.mode == QueryMode.DATABASE
assert "Sözleşme ve Ekleri Hazır" in result.response
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.get_retrieval_engine")
@patch("integration.query_handler.get_llm_generator")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_hybrid_falls_to_docs_without_signals(self, mock_metrics, mock_gen, mock_engine, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("Atlas nedir", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
engine = MagicMock()
engine.retrieve.return_value = ([], {})
engine.build_context.return_value = ""
mock_engine.return_value = engine
generator = MagicMock()
generator.generate.return_value = "Atlas bir sistemdir."
mock_gen.return_value = generator
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("Atlas nedir", mode=QueryMode.HYBRID)
assert result.mode == QueryMode.DOCUMENTS
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.query_and_format")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_hybrid_no_rows_returns_not_found(self, mock_metrics, mock_qf, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("99999999 statü", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
mock_qf.return_value = {
"success": True,
"response": "99999999 numaralı teklif için veritabanında kayıt bulunamadı.",
"sql": "SELECT ...",
"rows": [],
}
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("99999999 statü", mode=QueryMode.HYBRID)
assert result.db_results is not None
assert len(result.db_results) == 0
assert "bulunamadı" in result.response
class TestErrorHandling:
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.query_and_format")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_db_error_surfaced(self, mock_metrics, mock_qf, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("26000046 statü", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
mock_qf.return_value = {
"success": False,
"response": "Hata oluştu",
"sql": "SELECT ...",
"rows": None,
"error": "connection refused",
}
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("26000046 statü", mode=QueryMode.DATABASE)
assert result.error is not None
assert "connection refused" in result.error
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.get_retrieval_engine")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": False})
def test_document_retrieval_error(self, mock_metrics, mock_engine, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("test", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
engine = MagicMock()
engine.retrieve.side_effect = Exception("retrieval failed")
mock_engine.return_value = engine
mock_metrics.return_value = MagicMock()
handler = _make_handler()
result = handler.execute("test", mode=QueryMode.DOCUMENTS)
assert result.error is not None
assert "retrieval failed" in result.error
class TestQueryHistory:
def test_starts_empty(self):
handler = _make_handler()
assert handler.query_history == []
@patch("integration.query_handler.get_context_manager")
@patch("integration.query_handler.get_query_rewriter")
@patch("integration.query_handler.query_and_format")
@patch("integration.query_handler.get_metrics")
@patch("integration.query_handler.DATABASE_CONFIG", {"enabled": True})
def test_records_after_execute(self, mock_metrics, mock_qf, mock_rewriter, mock_ctx):
mock_rewriter_inst = MagicMock()
mock_rewriter_inst.rewrite.return_value = ("26000046 statü", {"method": "none"})
mock_rewriter.return_value = mock_rewriter_inst
ctx = MagicMock()
ctx.get_active_credit_file_id.return_value = None
mock_ctx.return_value = ctx
mock_qf.return_value = {"success": True, "response": "ok", "sql": "SELECT 1", "rows": []}
mock_metrics.return_value = MagicMock()
handler = _make_handler()
handler.execute("26000046 statü", mode=QueryMode.DATABASE)
assert len(handler.query_history) == 1
def test_clear_history(self):
handler = _make_handler()
handler._query_history.append(QueryResult(query="q", response="r", mode=QueryMode.DOCUMENTS))
handler.clear_history()
assert len(handler.query_history) == 0
class TestSingleton:
def setup_method(self):
reset_query_handler()
def teardown_method(self):
reset_query_handler()
def test_returns_same_instance(self):
h1 = get_query_handler()
h2 = get_query_handler()
assert h1 is h2
def test_reset_creates_new(self):
h1 = get_query_handler()
reset_query_handler()
h2 = get_query_handler()
assert h1 is not h2
def test_mode_update(self):
h1 = get_query_handler(default_mode=QueryMode.DOCUMENTS)
assert h1.default_mode == QueryMode.DOCUMENTS
get_query_handler(default_mode=QueryMode.HYBRID)
assert h1.default_mode == QueryMode.HYBRID