# #region Test.PreviewSessionOps [C:3] [TYPE Module] [SEMANTICS test, translate, preview, session, query] # @BRIEF Tests for preview_session_ops.py — get_preview_session. # @RELATION BINDS_TO -> [preview_session_ops] # @TEST_CONTRACT: get_preview_session -> dict | returns latest session with records # @TEST_EDGE: no_session_found -> raises ValueError 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 import pytest from src.plugins.translate.preview_session_ops import get_preview_session class TestGetPreviewSession: """get_preview_session — fetch latest preview session for a job.""" def _make_session(self, overrides: dict | None = None) -> MagicMock: """Create a mock preview session.""" session = MagicMock() session.id = "session-1" session.job_id = "job-1" session.status = "APPLIED" session.created_by = "user-1" session.created_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc) session.expires_at = datetime(2026, 2, 15, 12, 0, 0, tzinfo=timezone.utc) if overrides: for k, v in overrides.items(): setattr(session, k, v) return session def _make_record(self) -> MagicMock: """Create a mock preview record.""" r = MagicMock() r.id = "rec-1" r.source_sql = "SELECT * FROM foo" r.target_sql = "SELECT * FROM bar" r.source_object_type = "table" r.source_object_id = "obj-1" r.source_object_name = "foo" r.status = "done" r.feedback = None r.languages = [] return r def _make_job(self, target_languages=None, target_dialect=None) -> MagicMock: """Create a mock translation job.""" job = MagicMock() job.id = "job-1" job.target_languages = target_languages if target_languages is not None else ["ru", "de"] job.target_dialect = target_dialect if target_dialect else "clickhouse" return job def _setup_db_mock(self, session, records, job): """Configure db mock chain for the three queries in get_preview_session.""" db = MagicMock() # Query 1: TranslationPreviewSession (filter + order_by + first) db.query.return_value.filter.return_value.order_by.return_value.first.return_value = session # Query 2: TranslationPreviewRecord (filter + all) db.query.return_value.filter.return_value.all.return_value = records # Query 3: TranslationJob (filter + first — no order_by) db.query.return_value.filter.return_value.first.return_value = job return db def test_returns_session_with_records(self): """Returns session dict with serialized records.""" session = self._make_session() record = self._make_record() job = self._make_job() db = self._setup_db_mock(session, [record], job) with patch("src.plugins.translate.preview_session_ops._serialize_preview_record") as mock_ser: mock_ser.return_value = {"id": "rec-1", "source_sql": "SELECT * FROM foo"} result = get_preview_session(db, "job-1") assert result["id"] == "session-1" assert result["job_id"] == "job-1" assert result["status"] == "APPLIED" assert result["created_by"] == "user-1" assert "created_at" in result assert "expires_at" in result assert result["target_languages"] == ["ru", "de"] assert len(result["records"]) == 1 assert result["records"][0]["id"] == "rec-1" def test_no_session_found_raises(self): """No session found raises ValueError.""" db = MagicMock() db.query.return_value.filter.return_value.order_by.return_value.first.return_value = None with pytest.raises(ValueError, match="No preview session found for job 'job-1'"): get_preview_session(db, "job-1") def test_job_not_found_defaults_to_en(self): """Job not found falls back to ['en'] (last fallback).""" session = self._make_session() db = MagicMock() db.query.return_value.filter.return_value.order_by.return_value.first.return_value = session db.query.return_value.filter.return_value.all.return_value = [] # No job found -> first (no order_by) returns None db.query.return_value.filter.return_value.first.return_value = None with patch("src.plugins.translate.preview_session_ops._serialize_preview_record") as mock_ser: result = get_preview_session(db, "job-1") # When job is None -> the fallback is ["en"] assert result["target_languages"] == ["en"] def test_job_no_target_languages_uses_dialect(self): """Job with None target_languages uses target_dialect.""" session = self._make_session() job = self._make_job(target_dialect="postgresql") job.target_languages = None # explicitly override to None db = self._setup_db_mock(session, [], job) with patch("src.plugins.translate.preview_session_ops._serialize_preview_record") as mock_ser: result = get_preview_session(db, "job-1") assert result["target_languages"] == ["postgresql"] def test_target_languages_not_list_normalized(self): """target_languages that is not a list gets wrapped in list.""" session = self._make_session() job = self._make_job(target_languages="ru") # string instead of list db = self._setup_db_mock(session, [], job) with patch("src.plugins.translate.preview_session_ops._serialize_preview_record") as mock_ser: result = get_preview_session(db, "job-1") assert result["target_languages"] == ["ru"] def test_session_expires_at_none(self): """Session without expires_at returns None for that field.""" session = self._make_session({"expires_at": None}) job = self._make_job() db = self._setup_db_mock(session, [], job) with patch("src.plugins.translate.preview_session_ops._serialize_preview_record") as mock_ser: result = get_preview_session(db, "job-1") assert result["expires_at"] is None def test_empty_records(self): """No records returns empty records list.""" session = self._make_session() job = self._make_job() db = self._setup_db_mock(session, [], job) with patch("src.plugins.translate.preview_session_ops._serialize_preview_record"): result = get_preview_session(db, "job-1") assert result["records"] == [] # #endregion Test.PreviewSessionOps