test: 7 agents — plugins ~158 tests, extractor 98-100%, dashboard routes 99-100%, git services 94-100%, services 94-100%, dataset_review 100%. Fix test_api_key_routes.py sys.modules pollution

This commit is contained in:
2026-06-15 18:30:05 +03:00
parent 3de67c258a
commit 51d90f58c1
40 changed files with 9629 additions and 4 deletions

View File

@@ -0,0 +1,277 @@
# #region Test.RunSourceFetcher [C:3] [TYPE Module] [SEMANTICS test, translate, source, fetch]
# @BRIEF Tests for _run_source.py — fetch_source_rows, _extract_chart_data_rows.
# @RELATION BINDS_TO -> [_run_source]
# @TEST_EDGE: superset_datasource_fetch -> fetch_source_rows returns rows from Superset API
# @TEST_EDGE: empty_datasource -> fallback to preview session
# @TEST_EDGE: no_session -> returns empty list
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from datetime import UTC, datetime
from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session, sessionmaker
from src.models.translate import (
Base, TranslationJob, TranslationPreviewSession, TranslationPreviewRecord,
)
from src.plugins.translate._run_source import _extract_chart_data_rows
def make_session():
engine = create_engine("sqlite:///:memory:")
@event.listens_for(engine, "connect")
def _set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
Base.metadata.create_all(bind=engine)
session = sessionmaker(bind=engine)()
return session, engine
# #region TestExtractChartDataRows [C:1] [TYPE Class]
class TestExtractChartDataRows:
"""_extract_chart_data_rows — extract data rows from Superset chart data API response."""
def test_result_list_with_data(self):
"""result is a list with item containing data key."""
response = {"result": [{"data": [{"x": 1}, {"x": 2}]}]}
assert _extract_chart_data_rows(response) == [{"x": 1}, {"x": 2}]
def test_result_dict_with_data(self):
"""result is a dict with data key."""
response = {"result": {"data": [{"x": 1}]}}
assert _extract_chart_data_rows(response) == [{"x": 1}]
def test_fallback_to_response_data(self):
"""Fallback to response.data."""
response = {"data": [{"x": 1}]}
assert _extract_chart_data_rows(response) == [{"x": 1}]
def test_result_list_no_data(self):
"""When result is a list with no data, return it."""
response = {"result": [{"x": 1}]}
assert _extract_chart_data_rows(response) == [{"x": 1}]
def test_empty_result_list(self):
"""Empty list returns empty list."""
assert _extract_chart_data_rows({"result": []}) == []
def test_empty_response(self):
"""No recognizable fields returns empty list."""
assert _extract_chart_data_rows({"foo": "bar"}) == []
# #endregion TestExtractChartDataRows
# #region TestFetchSourceRows [C:3] [TYPE Class]
class TestFetchSourceRows:
"""fetch_source_rows — fetch source rows from Superset datasource or preview."""
@pytest.mark.asyncio
async def test_no_job_returns_empty(self):
"""No matching job -> empty list."""
session, engine = make_session()
try:
config_manager = MagicMock()
from src.plugins.translate._run_source import fetch_source_rows
result = await fetch_source_rows(session, config_manager, "nonexistent-job", "run-1")
assert result == []
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_job_without_datasource_preview_session(self):
"""Job with no datasource, but has preview session."""
session, engine = make_session()
try:
job = TranslationJob(id="job-1", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=None)
session.add(job)
sess = TranslationPreviewSession(id="ps-1", job_id="job-1", status="APPLIED",
created_at=datetime.now(UTC))
session.add(sess)
rec = TranslationPreviewRecord(id="rec-1", session_id="ps-1", status="APPROVED",
source_object_id="row1", source_sql="hello",
source_object_name="Row 1")
session.add(rec)
session.commit()
config_manager = MagicMock()
from src.plugins.translate._run_source import fetch_source_rows
result = await fetch_source_rows(session, config_manager, "job-1", "run-1")
assert len(result) == 1
assert result[0]["source_text"] == "hello"
# approved_translation should be the target_sql (None here since status=APPROVED but target_sql not set)
assert result[0]["approved_translation"] is None
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_no_preview_session_returns_empty(self):
"""Job without datasource, no preview session -> empty list."""
session, engine = make_session()
try:
job = TranslationJob(id="job-2", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=None)
session.add(job)
session.commit()
config_manager = MagicMock()
from src.plugins.translate._run_source import fetch_source_rows
result = await fetch_source_rows(session, config_manager, "job-2", "run-2")
assert result == []
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_superset_datasource_success(self):
"""Job with datasource, successful fetch from Superset."""
session, engine = make_session()
try:
job = TranslationJob(id="job-3", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=42, environment_id="env-1",
translation_column="text")
session.add(job)
session.commit()
config_manager = MagicMock()
config_manager.get_environments.return_value = [
MagicMock(id="env-1")
]
mock_client = AsyncMock()
mock_client.get_dataset_detail.return_value = {"id": 42}
mock_client.build_dataset_preview_query_context.return_value = {
"queries": [{}], "form_data": {}
}
mock_client.network.request.return_value = {
"result": [{"data": [{"text": "hello"}, {"text": "world"}]}]
}
from src.plugins.translate._run_source import fetch_source_rows
with patch('src.plugins.translate._run_source.get_superset_client',
AsyncMock(return_value=mock_client)):
result = await fetch_source_rows(session, config_manager, "job-3", "run-3")
assert len(result) == 2
assert result[0]["source_text"] == "hello"
assert result[1]["source_text"] == "world"
assert result[0]["source_data"] == {"text": "hello"}
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_superset_fetch_falls_back_to_preview(self):
"""Superset fetch fails, falls back to preview session."""
session, engine = make_session()
try:
job = TranslationJob(id="job-4", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=42, environment_id="env-1",
translation_column="text")
session.add(job)
sess = TranslationPreviewSession(id="ps-4", job_id="job-4", status="APPLIED",
created_at=datetime.now(UTC))
session.add(sess)
rec = TranslationPreviewRecord(id="rec-4", session_id="ps-4", status="PENDING",
source_object_id="row1", source_sql="fallback text",
source_object_name="Row 1")
session.add(rec)
session.commit()
config_manager = MagicMock()
config_manager.get_environments.return_value = [
MagicMock(id="env-1")
]
mock_client = AsyncMock()
mock_client.get_dataset_detail.side_effect = Exception("API error")
from src.plugins.translate._run_source import fetch_source_rows
with patch('src.plugins.translate._run_source.get_superset_client',
AsyncMock(return_value=mock_client)):
result = await fetch_source_rows(session, config_manager, "job-4", "run-4")
assert len(result) == 1
assert result[0]["source_text"] == "fallback text"
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_superset_empty_rows_falls_back(self):
"""Superset returns empty rows, falls back to preview."""
session, engine = make_session()
try:
job = TranslationJob(id="job-5", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=42, environment_id="env-1",
translation_column="text")
session.add(job)
sess = TranslationPreviewSession(id="ps-5", job_id="job-5", status="APPLIED",
created_at=datetime.now(UTC))
session.add(sess)
rec = TranslationPreviewRecord(id="rec-5", session_id="ps-5", status="APPROVED",
source_object_id="row1", source_sql="fb text",
source_object_name="Row 1")
session.add(rec)
session.commit()
config_manager = MagicMock()
config_manager.get_environments.return_value = [
MagicMock(id="env-1")
]
mock_client = AsyncMock()
mock_client.get_dataset_detail.return_value = {"id": 42}
mock_client.build_dataset_preview_query_context.return_value = {
"queries": [{}], "form_data": {}
}
mock_client.network.request.return_value = {"result": [{"data": []}]}
from src.plugins.translate._run_source import fetch_source_rows
with patch('src.plugins.translate._run_source.get_superset_client',
AsyncMock(return_value=mock_client)):
result = await fetch_source_rows(session, config_manager, "job-5", "run-5")
assert len(result) == 1
assert result[0]["source_text"] == "fb text"
finally:
session.close()
Base.metadata.drop_all(bind=engine)
@pytest.mark.asyncio
async def test_superset_no_env_config_falls_back(self):
"""No matching environment config -> falls back to preview."""
session, engine = make_session()
try:
job = TranslationJob(id="job-6", name="Test", status="ACTIVE",
source_dialect="en", target_dialect="fr",
source_datasource_id=42, environment_id="nonexistent",
translation_column="text")
session.add(job)
session.commit()
config_manager = MagicMock()
config_manager.get_environments.return_value = [
MagicMock(id="other-env")
]
from src.plugins.translate._run_source import fetch_source_rows
result = await fetch_source_rows(session, config_manager, "job-6", "run-6")
# No preview session, falls back to empty
assert result == []
finally:
session.close()
Base.metadata.drop_all(bind=engine)
# #endregion TestFetchSourceRows
# #endregion Test.RunSourceFetcher