# #region Test.OrchestratorQuery [C:3] [TYPE Module] [SEMANTICS test, translate, query, records, history] # @BRIEF Tests for orchestrator_query.py — get_run_records, get_run_history. # @RELATION BINDS_TO -> [orchestrator_query] # @TEST_CONTRACT: get_run_records -> dict | paginated records with optional deduplicate # @TEST_CONTRACT: get_run_history -> tuple[int, list[dict]] | paginated run history # @TEST_EDGE: deduplicate_empty_source_hash # @TEST_EDGE: status_filter import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src")) import os os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing") os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:") os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:") from datetime import datetime, timezone from unittest.mock import MagicMock, patch, ANY import pytest from src.plugins.translate.orchestrator_query import get_run_records, get_run_history class TestGetRunRecords: """get_run_records — paginated run records.""" def _make_record(self, rec_id: str, **kwargs): rec = MagicMock() rec.id = rec_id rec.batch_id = "batch-1" rec.source_sql = "SELECT * FROM t" rec.target_sql = "SELECT * FROM t_translated" rec.source_object_type = "table" rec.source_object_id = "123" rec.source_object_name = "my_table" rec.source_data = {"col": "val"} rec.source_hash = f"hash_{rec_id}" rec.status = "translated" rec.error_message = None rec.created_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc) rec.languages = [] for k, v in kwargs.items(): setattr(rec, k, v) return rec def test_basic_query(self): """Returns paginated records with metadata.""" db = MagicMock() # Mock chain: db.query().options().filter() options_mock = MagicMock() filter_mock = MagicMock() options_filter_mock = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value = options_filter_mock options_filter_mock.count.return_value = 2 options_filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ self._make_record("r1"), self._make_record("r2"), ] result = get_run_records(db, "run-1") assert result["total"] == 2 assert len(result["items"]) == 2 assert result["page"] == 1 assert result["page_size"] == 50 assert result["status_filter"] is None assert result["deduplicate"] is False def test_with_status_filter(self): """Status filter applied to query.""" db = MagicMock() options_mock = MagicMock() filter_mock1 = MagicMock() filter_mock2 = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value = filter_mock1 filter_mock1.filter.return_value = filter_mock2 filter_mock2.count.return_value = 1 filter_mock2.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ self._make_record("r1", status="failed"), ] result = get_run_records(db, "run-1", status_filter="failed") assert result["total"] == 1 assert result["status_filter"] == "failed" def test_deduplicate_applied(self): """Deduplicate flag applies exclusion logic.""" db = MagicMock() options_mock = MagicMock() filter_mock1 = MagicMock() filter_mock2 = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value = filter_mock1 filter_mock1.filter.return_value = filter_mock2 filter_mock2.count.return_value = 1 filter_mock2.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [ self._make_record("r1", source_hash="abc123"), ] result = get_run_records(db, "run-1", deduplicate=True) assert result["deduplicate"] is True assert len(result["items"]) == 1 def test_empty_records(self): """No records returns empty list.""" db = MagicMock() options_mock = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value.count.return_value = 0 options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [] result = get_run_records(db, "run-1") assert result["total"] == 0 assert result["items"] == [] def test_record_languages_included(self): """Language entries included in record dict.""" db = MagicMock() lang = MagicMock() lang.language_code = "ru" lang.translated_value = "привет" lang.final_value = None lang.source_language_detected = "en" lang.status = "translated" lang.needs_review = False rec = self._make_record("r1", languages=[lang]) options_mock = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value.count.return_value = 1 options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec] result = get_run_records(db, "run-1") assert len(result["items"][0]["languages"]) == 1 assert result["items"][0]["languages"][0]["language_code"] == "ru" assert result["items"][0]["languages"][0]["final_value"] == "привет" def test_record_without_created_at(self): """Record without created_at returns None for isoformat.""" db = MagicMock() rec = self._make_record("r1", created_at=None) options_mock = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value.count.return_value = 1 options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec] result = get_run_records(db, "run-1") assert result["items"][0]["created_at"] is None def test_pagination_params(self): """Pagination params affect offset and limit.""" db = MagicMock() rec = self._make_record("r1") options_mock = MagicMock() db.query.return_value.options.return_value = options_mock options_mock.filter.return_value.count.return_value = 10 options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec] result = get_run_records(db, "run-1", page=2, page_size=10) assert result["page"] == 2 assert result["page_size"] == 10 class TestGetRunHistory: """get_run_history — paginated run history for a job.""" def _make_run(self, run_id: str, **kwargs): run = MagicMock() run.id = run_id run.job_id = "job-1" run.status = "COMPLETED" run.trigger_type = "manual" run.started_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc) run.completed_at = datetime(2026, 1, 15, 13, 0, 0, tzinfo=timezone.utc) run.error_message = None run.total_records = 100 run.successful_records = 95 run.failed_records = 3 run.skipped_records = 2 run.cache_hits = 5 run.insert_status = "success" run.insert_method = "cte_insert" run.connection_snapshot = {"db": "test"} run.superset_execution_id = "q-1" run.created_by = "user" run.created_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc) for k, v in kwargs.items(): setattr(run, k, v) return run def test_basic_history(self): """Returns total count and run list.""" db = MagicMock() runs = [self._make_run("r1"), self._make_run("r2")] filter_mock = MagicMock() db.query.return_value.filter.return_value = filter_mock filter_mock.count.return_value = 2 filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = runs total, items = get_run_history(db, "job-1") assert total == 2 assert len(items) == 2 assert items[0]["id"] == "r1" assert items[0]["status"] == "COMPLETED" def test_empty_history(self): """No runs returns total=0 and empty list.""" db = MagicMock() filter_mock = MagicMock() db.query.return_value.filter.return_value = filter_mock filter_mock.count.return_value = 0 filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [] total, items = get_run_history(db, "job-1") assert total == 0 assert items == [] def test_pagination_params(self): """Pagination affects offset and limit.""" db = MagicMock() runs = [self._make_run("r1")] filter_mock = MagicMock() db.query.return_value.filter.return_value = filter_mock filter_mock.count.return_value = 10 filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = runs total, items = get_run_history(db, "job-1", page=3, page_size=5) assert total == 10 assert len(items) == 1 def test_run_without_dates(self): """Run without started_at/completed_at returns None.""" db = MagicMock() run = self._make_run("r1", started_at=None, completed_at=None) filter_mock = MagicMock() db.query.return_value.filter.return_value = filter_mock filter_mock.count.return_value = 1 filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [run] total, items = get_run_history(db, "job-1") assert items[0]["started_at"] is None assert items[0]["completed_at"] is None def test_all_numeric_fields_default_to_zero(self): """Numeric fields default to 0 when None.""" db = MagicMock() run = self._make_run( "r1", total_records=None, successful_records=None, failed_records=None, skipped_records=None, cache_hits=None, ) filter_mock = MagicMock() db.query.return_value.filter.return_value = filter_mock filter_mock.count.return_value = 1 filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [run] total, items = get_run_history(db, "job-1") assert items[0]["total_records"] == 0 assert items[0]["successful_records"] == 0 assert items[0]["failed_records"] == 0 assert items[0]["skipped_records"] == 0 assert items[0]["cache_hits"] == 0 # #endregion Test.OrchestratorQuery