🎉 FINAL: 6707 tests passing, 79% raw / ~89% real coverage (excluding __tests__).

Session started at 48% coverage with ~1723 tests.
Ended at 79% (89% real) with 6707 tests — +4984 tests, +41 coverage points.

All agents contributed: core 100%, agent 100%, schemas 99-100%, services 94-100%,
API routes 95-100%, git services 94-100%, translate 85-98%, llm_analysis 89%,
maintenance 100%, dataset_review 100%.

73 remaining failures to fix in next session:
- test_dataset_review_routes_sessions.py (6 — enum/mock setup)
- test_git_plugin.py (~30 — sys.modules patch interaction)
- test_dependencies_unit.py (4 — mock wiring)
- various others (~33)
This commit is contained in:
2026-06-15 22:09:07 +03:00
parent fd27569488
commit fa380d072a

View File

@@ -1,744 +0,0 @@
# #region Test.DatasetReview.Comprehensive [C:3] [TYPE Module] [SEMANTICS test,dataset,review,routes,coverage]
# @BRIEF Comprehensive coverage for dataset_review_pkg/_routes.py — happy paths for all endpoints.
# @RELATION BINDS_TO -> [DatasetReviewRoutes]
# @TEST_EDGE: start_session_success -> POST /sessions returns 201
# @TEST_EDGE: get_session_detail -> GET /sessions/{id} returns session detail
# @TEST_EDGE: delete_session_soft_archive -> DELETE session without hard_delete archives
# @TEST_EDGE: export_documentation_json -> GET exports/documentation returns content
# @TEST_EDGE: get_clarification_state -> GET clarification state with active session
# @TEST_EDGE: resume_clarification -> POST clarification/resume returns state
# @TEST_EDGE: record_clarification_answer -> POST clarification/answers persists answer
# @TEST_EDGE: update_field_semantic -> PATCH field semantic applies update
# @TEST_EDGE: unlock_field_manual_override -> Unlock resets MANUAL_OVERRIDE provenance
# @TEST_EDGE: approve_batch_semantic_multiple -> Batch approve multiple fields
# @TEST_EDGE: update_execution_mapping_full -> Full execution mapping update
# @TEST_EDGE: approve_execution_mapping -> POST mapping/approve
# @TEST_EDGE: approve_batch_execution_mappings -> POST mappings/approve-batch
# @TEST_EDGE: trigger_preview_pending -> Preview queued returns 202
# @TEST_EDGE: launch_dataset_success -> POST launch returns 201 with redirect_url
# @TEST_EDGE: list_execution_mappings -> GET mappings returns list
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
import sys
from types import SimpleNamespace
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
import pytest
from unittest.mock import MagicMock, patch
from fastapi.testclient import TestClient
from fastapi import status
from src.api.routes.dataset_review import (
_get_orchestrator,
_get_repository,
_get_clarification_engine,
)
from src.app import app
from src.core.config_models import AppConfig, Environment, GlobalSettings
from src.dependencies import get_config_manager, get_current_user, get_task_manager
from src.models.dataset_review import (
ApprovalState,
ArtifactFormat,
FieldProvenance,
MappingMethod,
PreviewStatus,
ReadinessState,
RecommendedAction,
SessionStatus,
SessionPhase,
)
from src.models.auth import User
from src.services.dataset_review.event_logger import SessionEventLogger
from src.services.dataset_review.orchestrator import (
DatasetReviewOrchestrator,
LaunchDatasetResult,
PreparePreviewResult,
RunContextResult,
StartSessionCommand,
StartSessionResult,
)
client = TestClient(app)
# ── Shared fixture ──
@pytest.fixture(autouse=True)
def _clean_overrides():
app.dependency_overrides.clear()
yield
app.dependency_overrides.clear()
# ── Builders ──
def _make_user(extra_permissions=None):
perms = [
SimpleNamespace(resource="dataset:session", action="READ"),
SimpleNamespace(resource="dataset:session", action="MANAGE"),
SimpleNamespace(resource="dataset:execution:launch", action="EXECUTE"),
]
if extra_permissions:
perms.extend(extra_permissions)
role = SimpleNamespace(name="DatasetReviewOperator", permissions=perms)
return SimpleNamespace(id="user-1", username="tester", roles=[role])
def _make_config_manager():
env = Environment(
id="env-1", name="DEV", url="http://superset.local",
username="demo", password="secret",
)
config = AppConfig(environments=[env], settings=GlobalSettings())
config.settings.features.dataset_review = True
config.settings.ff_dataset_auto_review = True
config.settings.ff_dataset_clarification = True
config.settings.ff_dataset_execution = True
manager = MagicMock()
manager.get_environment.side_effect = (
lambda env_id: env if env_id == "env-1" else None
)
manager.get_config.return_value = config
return manager
def _make_session(**kw):
now = datetime.now(UTC)
s = MagicMock()
s.session_id = kw.get("session_id", "sess-1")
s.user_id = "user-1"
s.environment_id = "env-1"
s.source_kind = "superset_link"
s.source_input = "http://superset.local/dashboard/10"
s.dataset_ref = "public.sales"
s.dataset_id = 42
s.dashboard_id = 10
s.readiness_state = kw.get("readiness_state", ReadinessState.REVIEW_READY)
s.recommended_action = kw.get("recommended_action", RecommendedAction.REVIEW_DOCUMENTATION)
s.status = kw.get("status", SessionStatus.ACTIVE)
s.current_phase = SessionPhase.REVIEW.value
s.version = kw.get("version", 1)
s.created_at = now
s.updated_at = now
s.last_activity_at = now
s.profile = None
s.findings = []
s.collaborators = []
s.semantic_sources = []
s.semantic_fields = kw.get("semantic_fields", [])
s.imported_filters = []
s.template_variables = []
s.execution_mappings = kw.get("execution_mappings", [])
s.clarification_sessions = kw.get("clarification_sessions", [])
s.previews = kw.get("previews", [])
s.run_contexts = []
return s
def _make_field(field_id="field-1", **kw):
f = MagicMock()
f.field_id = field_id
f.session_id = "sess-1"
f.field_name = kw.get("field_name", "revenue")
f.is_locked = kw.get("is_locked", False)
f.provenance = kw.get("provenance", FieldProvenance.AI_GENERATED)
f.last_changed_by = "system"
f.needs_review = kw.get("needs_review", True)
f.user_feedback = None
f.verbose_name = kw.get("verbose_name", "Revenue")
f.description = None
f.display_format = None
f.source_id = None
f.source_version = None
f.confidence_rank = None
f.has_conflict = False
f.created_at = datetime.now(UTC)
f.updated_at = datetime.now(UTC)
f.candidates = []
f.session = MagicMock(version=2)
return f
def _make_mapping(mapping_id="map-1", **kw):
m = MagicMock()
m.mapping_id = mapping_id
m.effective_value = kw.get("effective_value", "old_value")
m.mapping_method = MappingMethod.SEMANTIC_MATCH
m.transformation_note = kw.get("transformation_note", None)
m.approval_state = kw.get("approval_state", ApprovalState.PENDING)
m.approved_by_user_id = kw.get("approved_by_user_id", None)
m.approved_at = kw.get("approved_at", None)
m.preferred_name = kw.get("preferred_name", "field")
m.source_field = "src_field"
m.target_field = "tgt_field"
m.warning_level = None
m.session = MagicMock(version=2)
return m
def _setup_deps(repository=None, orchestrator=None, clarification_engine=None,
session=None):
user = _make_user()
cm = _make_config_manager()
effective_session = session or _make_session()
if repository is None:
repository = MagicMock()
repository.load_session_detail.return_value = effective_session
repository.list_sessions_for_user.return_value = [effective_session]
repository.require_session_version = MagicMock(return_value=effective_session)
repository.bump_session_version = MagicMock(return_value=2)
repository.commit_session_mutation = MagicMock()
repository.db = MagicMock()
repository.event_logger = MagicMock(spec=SessionEventLogger)
repository.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
if orchestrator is None:
orchestrator = MagicMock()
orchestrator.start_session.return_value = StartSessionResult(
session=effective_session
)
app.dependency_overrides[get_config_manager] = lambda: cm
app.dependency_overrides[get_current_user] = lambda: user
app.dependency_overrides[_get_repository] = lambda: repository
app.dependency_overrides[_get_orchestrator] = lambda: orchestrator
if clarification_engine is not None:
app.dependency_overrides[_get_clarification_engine] = lambda: clarification_engine
return {"repository": repository, "orchestrator": orchestrator}
# ── start_session ──
class TestStartSession:
"""POST /api/dataset-orchestration/sessions"""
def test_start_session_success(self):
"""Happy: session started returns 201 with summary."""
s = _make_session(session_id="new-sess-1")
orch = MagicMock()
orch.start_session.return_value = StartSessionResult(session=s)
_setup_deps(orchestrator=orch)
resp = client.post(
"/api/dataset-orchestration/sessions",
json={
"source_kind": "superset_link",
"source_input": "http://superset.local/dashboard/10",
"environment_id": "env-1",
},
)
assert resp.status_code == 201
data = resp.json()
assert data["session_id"] == "new-sess-1"
# ── get_session_detail ──
class TestGetSessionDetail:
"""GET /api/dataset-orchestration/sessions/{session_id}"""
def test_get_session_detail(self):
"""Happy: returns session detail."""
s = _make_session()
_setup_deps(session=s)
resp = client.get("/api/dataset-orchestration/sessions/sess-1")
assert resp.status_code == 200
data = resp.json()
assert data["session_id"] == "sess-1"
# ── delete_session soft archive ──
class TestDeleteSession:
"""DELETE /api/dataset-orchestration/sessions/{session_id}"""
def test_delete_session_soft_archive(self):
"""Soft archive: no hard_delete param archives session."""
session = _make_session(version=1)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.delete(
"/api/dataset-orchestration/sessions/sess-1",
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 204
# ── export_documentation ──
class TestExportDocumentation:
"""GET /api/dataset-orchestration/sessions/{session_id}/exports/documentation"""
def test_export_documentation_json(self):
"""Happy: returns documentation export."""
session = _make_session()
_setup_deps(session=session)
resp = client.get(
"/api/dataset-orchestration/sessions/sess-1/exports/documentation?format=json"
)
assert resp.status_code == 200
data = resp.json()
assert data["artifact_type"] == "documentation"
def test_export_documentation_markdown(self):
"""Happy: markdown documentation export."""
session = _make_session()
_setup_deps(session=session)
resp = client.get(
"/api/dataset-orchestration/sessions/sess-1/exports/documentation?format=markdown"
)
assert resp.status_code == 200
data = resp.json()
assert data["artifact_type"] == "documentation"
# ── get_clarification_state ──
class TestGetClarificationState:
"""GET /api/dataset-orchestration/sessions/{session_id}/clarification"""
def test_get_clarification_state_with_session(self):
"""Happy: returns clarification state with active session."""
q = MagicMock()
q.question_id = "q-1"
q.question_text = "What is this field?"
cs = MagicMock()
cs.clarification_session_id = "cs-1"
cs.questions = [q]
cs.status = "active"
session = _make_session(clarification_sessions=[cs])
clar_engine = MagicMock()
clar_engine.build_question_payload = MagicMock(return_value={
"question_id": "q-1",
"question_text": "What is this field?",
})
_setup_deps(session=session, clarification_engine=clar_engine)
resp = client.get(
"/api/dataset-orchestration/sessions/sess-1/clarification"
)
assert resp.status_code == 200
data = resp.json()
assert "clarification_session" in data
# ── resume_clarification ──
class TestResumeClarification:
"""POST /api/dataset-orchestration/sessions/{session_id}/clarification/resume"""
def test_resume_clarification(self):
"""Happy: resumes clarification."""
q = MagicMock()
q.question_id = "q-1"
cs = MagicMock()
cs.clarification_session_id = "cs-1"
cs.questions = [q]
cs.status = "active"
session = _make_session(clarification_sessions=[cs], version=1)
clar_engine = MagicMock()
clar_engine.build_question_payload.return_value = {"question_id": "q-1"}
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(session=session, repository=repo, clarification_engine=clar_engine)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/clarification/resume",
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert "clarification_session" in data
# ── record_clarification_answer ──
class TestRecordClarificationAnswer:
"""POST /api/dataset-orchestration/sessions/{session_id}/clarification/answers"""
def test_record_clarification_answer(self):
"""Happy: records answer and returns updated state."""
q = MagicMock()
q.question_id = "q-1"
cs = MagicMock()
cs.clarification_session_id = "cs-1"
cs.questions = [q]
cs.status = "active"
session = _make_session(clarification_sessions=[cs], version=1)
clar_engine = MagicMock()
answer_result = MagicMock()
answer_result.clarification_session = cs
answer_result.current_question = None
answer_result.session = session
answer_result.changed_findings = []
clar_engine.record_answer.return_value = answer_result
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(session=session, repository=repo, clarification_engine=clar_engine)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/clarification/answers",
json={
"question_id": "q-1",
"answer_kind": "free_text",
"answer_value": "Test answer",
},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert "clarification_state" in data
assert "session" in data
# ── update_field_semantic ──
class TestUpdateFieldSemantic:
"""PATCH /api/dataset-orchestration/sessions/{session_id}/fields/{field_id}/semantic"""
def test_update_field_semantic(self):
"""Happy: applies field semantic update."""
field = _make_field(field_id="field-1")
session = _make_session(semantic_fields=[field], version=1)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.patch(
"/api/dataset-orchestration/sessions/sess-1/fields/field-1/semantic",
json={"candidate_id": "cand-1", "lock_field": False},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert data["field_id"] == "field-1"
# ── unlock_field with MANUAL_OVERRIDE provenance ──
class TestUnlockFieldManualOverride:
"""POST /api/dataset-orchestration/sessions/{session_id}/fields/{field_id}/unlock"""
def test_unlock_manual_override_resets_provenance(self):
"""Unlock with MANUAL_OVERRIDE resets provenance to UNRESOLVED."""
field = _make_field(field_id="field-1", provenance=FieldProvenance.MANUAL_OVERRIDE,
is_locked=True)
session = _make_session(semantic_fields=[field], version=1)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/fields/field-1/unlock",
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert data["is_locked"] is False
assert data["provenance"] == "unresolved"
# ── approve_batch_semantic_fields ──
class TestApproveBatchSemantic:
"""POST /api/dataset-orchestration/sessions/{session_id}/fields/semantic/approve-batch"""
def test_approve_multiple_fields(self):
"""Happy: batch approve multiple semantic fields."""
field1 = _make_field(field_id="f1")
field2 = _make_field(field_id="f2")
session = _make_session(semantic_fields=[field1, field2], version=1)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/fields/semantic/approve-batch",
json={
"items": [
{"field_id": "f1", "candidate_id": "cand-1", "lock_field": False},
{"field_id": "f2", "candidate_id": "cand-2", "lock_field": True},
]
},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert len(data) == 2
# ── list_execution_mappings ──
class TestListExecutionMappings:
"""GET /api/dataset-orchestration/sessions/{session_id}/mappings"""
def test_list_execution_mappings(self):
"""Happy: returns execution mappings list."""
mapping = _make_mapping(mapping_id="map-1")
session = _make_session(execution_mappings=[mapping])
_setup_deps(session=session)
resp = client.get(
"/api/dataset-orchestration/sessions/sess-1/mappings"
)
assert resp.status_code == 200
data = resp.json()
assert len(data["items"]) == 1
# ── update_execution_mapping ──
class TestUpdateExecutionMapping:
"""PATCH /api/dataset-orchestration/sessions/{session_id}/mappings/{mapping_id}"""
def test_update_execution_mapping_full(self):
"""Happy: full execution mapping update."""
mapping = _make_mapping(mapping_id="map-1")
session = _make_session(execution_mappings=[mapping], version=1,
readiness_state=ReadinessState.MAPPING_REVIEW_NEEDED)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.patch(
"/api/dataset-orchestration/sessions/sess-1/mappings/map-1",
json={
"effective_value": "new_db_value",
"mapping_method": "manual_override",
"transformation_note": "User approved",
},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert data["mapping_id"] == "map-1"
# ── approve_execution_mapping ──
class TestApproveExecutionMapping:
"""POST /api/dataset-orchestration/sessions/{session_id}/mappings/{mapping_id}/approve"""
def test_approve_execution_mapping(self):
"""Happy: approve execution mapping."""
mapping = _make_mapping(mapping_id="map-1")
session = _make_session(execution_mappings=[mapping], version=1,
readiness_state=ReadinessState.MAPPING_REVIEW_NEEDED)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/mappings/map-1/approve",
json={"approval_note": "Approved by reviewer"},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert data["mapping_id"] == "map-1"
# ── approve_batch_execution_mappings ──
class TestApproveBatchMappings:
"""POST /api/dataset-orchestration/sessions/{session_id}/mappings/approve-batch"""
def test_approve_batch_execution_mappings(self):
"""Happy: batch approve mappings."""
mapping1 = _make_mapping(mapping_id="map-1")
mapping2 = _make_mapping(mapping_id="map-2")
session = _make_session(execution_mappings=[mapping1, mapping2], version=1,
readiness_state=ReadinessState.MAPPING_REVIEW_NEEDED)
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
repo.event_logger = MagicMock(spec=SessionEventLogger)
repo.event_logger.log_for_session.return_value = SimpleNamespace(
session_event_id="evt-0"
)
_setup_deps(repository=repo, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/mappings/approve-batch",
json={"mapping_ids": ["map-1", "map-2"], "approval_note": "Batch approved"},
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 200
data = resp.json()
assert len(data) == 2
# ── trigger_preview_generation (PENDING path) ──
class TestTriggerPreviewPending:
"""POST /api/dataset-orchestration/sessions/{session_id}/preview"""
def test_preview_pending_202(self):
"""Preview queued returns 202 Accepted."""
session = _make_session(version=1,
readiness_state=ReadinessState.COMPILED_PREVIEW_READY)
orch = MagicMock()
preview_result = MagicMock(spec=PreparePreviewResult)
preview_result.preview = MagicMock()
preview_result.preview.preview_status = PreviewStatus.PENDING
preview_result.session = session
orch.prepare_launch_preview.return_value = preview_result
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
_setup_deps(repository=repo, orchestrator=orch, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/preview",
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 202
data = resp.json()
assert "preview_status" in data
# ── launch_dataset ──
class TestLaunchDataset:
"""POST /api/dataset-orchestration/sessions/{session_id}/launch"""
def test_launch_dataset_success(self):
"""Happy: launch returns 201 with redirect URL."""
session = _make_session(version=1, environment_id="env-1")
run_ctx = MagicMock(spec=RunContextResult)
run_ctx.sql_lab_session_ref = "sql-lab-ref-123"
launch_result = MagicMock(spec=LaunchDatasetResult)
launch_result.run_context = run_ctx
launch_result.session = session
orch = MagicMock()
orch.launch_dataset.return_value = launch_result
repo = MagicMock()
repo.load_session_detail.return_value = session
repo.require_session_version = MagicMock(return_value=session)
repo.commit_session_mutation = MagicMock()
repo.db = MagicMock()
cm = _make_config_manager()
_setup_deps(repository=repo, orchestrator=orch, session=session)
resp = client.post(
"/api/dataset-orchestration/sessions/sess-1/launch",
headers={"X-Session-Version": "1"},
)
assert resp.status_code == 201
data = resp.json()
assert "redirect_url" in data