Spaces:
Sleeping
Sleeping
| 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: | |
| def test_database_mode_when_db_enabled(self): | |
| handler = _make_handler() | |
| assert handler._decide_execution(QueryMode.DATABASE, "anything") == "database" | |
| 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" | |
| def test_hybrid_with_db_signals(self): | |
| handler = _make_handler() | |
| assert handler._decide_execution(QueryMode.HYBRID, "26000046 statü") == "database" | |
| def test_hybrid_without_db_signals(self): | |
| handler = _make_handler() | |
| assert handler._decide_execution(QueryMode.HYBRID, "merhaba nasılsın") == "documents" | |
| 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: | |
| 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: | |
| 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: | |
| 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: | |
| 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 | |
| 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 | |
| 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: | |
| 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 | |
| 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 == [] | |
| 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 | |