# #region Test.Api.TranslatePreviewRoutes [C:3] [TYPE Module] [SEMANTICS test,translate,preview,api] # @BRIEF Tests for _preview_routes.py — preview translation and preview records. # @RELATION BINDS_TO -> [TranslatePreviewRoutesModule] # @TEST_EDGE: preview_validation_error -> 400 # @TEST_EDGE: preview_service_error -> 502 # @TEST_EDGE: preview_session_not_found -> 404 import os os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:") os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:") os.environ.setdefault("SECRET_KEY", "test-secret-key-for-tests") import sys from pathlib import Path from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import FastAPI from fastapi.testclient import TestClient _src = str(Path(__file__).resolve().parent.parent.parent / "src") if _src not in sys.path: sys.path.insert(0, _src) def _make_client(overrides: dict | None = None) -> TestClient: from src.api.routes.translate._preview_routes import router from src.dependencies import get_current_user, get_config_manager, get_db, has_permission, require_feature from src.schemas.auth import User, RoleSchema app = FastAPI() app.include_router(router) mock_user = User( id="user-1", username="admin", email="admin@x.com", auth_source="LOCAL", created_at=__import__("datetime").datetime.now(), roles=[RoleSchema(id="r1", name="Admin", description="", permissions=[])], ) defaults = { get_current_user: lambda: mock_user, get_config_manager: lambda: MagicMock(), get_db: lambda: MagicMock(), has_permission: lambda *a, **kw: lambda: None, require_feature: lambda *a, **kw: lambda: None, } if overrides: defaults.update(overrides) for dep, mock_fn in defaults.items(): app.dependency_overrides[dep] = mock_fn return TestClient(app) # ── preview_translation ── class TestPreviewTranslation: """POST /api/translate/jobs/{job_id}/preview""" def test_preview_success(self): mock_preview = AsyncMock() mock_preview.preview_rows.return_value = { "session_id": "preview-1", "rows": [{"id": "row-1", "source_sql": "SELECT 1"}], "cost_estimation": {"total_tokens": 100}, } with patch("src.api.routes.translate._preview_routes.TranslationPreview", return_value=mock_preview): client = _make_client() resp = client.post("/api/translate/jobs/job-1/preview", json={ "sample_size": 5, }) assert resp.status_code == 201 data = resp.json() assert data["session_id"] == "preview-1" def test_preview_no_payload_defaults(self): """When payload is None, PreviewRequest() defaults are used.""" mock_preview = AsyncMock() mock_preview.preview_rows.return_value = { "session_id": "preview-2", "rows": [], "cost_estimation": {"total_tokens": 0}, } with patch("src.api.routes.translate._preview_routes.TranslationPreview", return_value=mock_preview): client = _make_client() resp = client.post("/api/translate/jobs/job-1/preview") assert resp.status_code == 201 def test_preview_validation_error_400(self): mock_preview = AsyncMock() mock_preview.preview_rows.side_effect = ValueError("Invalid job_id") with patch("src.api.routes.translate._preview_routes.TranslationPreview", return_value=mock_preview): client = _make_client() resp = client.post("/api/translate/jobs/job-1/preview", json={}) assert resp.status_code == 400 assert "Invalid job_id" in resp.text def test_preview_service_error_502(self): mock_preview = AsyncMock() mock_preview.preview_rows.side_effect = RuntimeError("LLM provider failed") with patch("src.api.routes.translate._preview_routes.TranslationPreview", return_value=mock_preview): client = _make_client() resp = client.post("/api/translate/jobs/job-1/preview", json={}) assert resp.status_code == 502 assert "Preview failed" in resp.text # ── get_preview_records ── class TestGetPreviewRecords: """GET /api/translate/preview/{session_id}/records""" def test_records_success(self): from src.schemas.translate import PreviewRow mock_db = MagicMock() mock_session = MagicMock() mock_session.id = "preview-1" mock_record = MagicMock() mock_record.id = "rec-1" mock_record.source_sql = "SELECT 1" mock_record.target_sql = "SELECT 1" mock_record.source_object_type = "table" mock_record.source_object_id = "obj-1" mock_record.source_object_name = "test" mock_record.status = "pending" mock_record.feedback = None mock_db.query.return_value.filter.return_value.first.return_value = mock_session mock_db.query.return_value.filter.return_value.all.return_value = [mock_record] from src.dependencies import get_db client = _make_client({get_db: lambda: mock_db}) resp = client.get("/api/translate/preview/preview-1/records") assert resp.status_code == 200 data = resp.json() assert len(data) == 1 assert data[0]["id"] == "rec-1" def test_records_session_not_found_404(self): mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None from src.dependencies import get_db client = _make_client({get_db: lambda: mock_db}) resp = client.get("/api/translate/preview/preview-missing/records") assert resp.status_code == 404 assert "Preview session" in resp.text def test_records_db_error_400(self): mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.side_effect = Exception("DB error") from src.dependencies import get_db client = _make_client({get_db: lambda: mock_db}) resp = client.get("/api/translate/preview/preview-1/records") assert resp.status_code == 400 # #endregion Test.Api.TranslatePreviewRoutes