585 lines
23 KiB
Python
585 lines
23 KiB
Python
# #region Test.DatasetReview.SessionLifecycle [C:3] [TYPE Module] [SEMANTICS test,dataset,review,sessions,lifecycle]
|
|
# @BRIEF Session lifecycle tests for dataset review routes — covers list, update, delete, export, batch, mapping & feedback.
|
|
# @RELATION BINDS_TO -> [DatasetReviewRoutes]
|
|
# @TEST_EDGE: list_sessions_pagination -> paginated listing returns correct items/total/has_next
|
|
# @TEST_EDGE: update_session_pause -> PATCH session status to PAUSED sets recommended_action=RESUME_SESSION
|
|
# @TEST_EDGE: update_session_archive -> PATCH session to ARCHIVED clears active_task_id
|
|
# @TEST_EDGE: delete_session_hard -> DELETE hard_delete removes from DB
|
|
# @TEST_EDGE: delete_session_archive -> DELETE without hard_delete archives
|
|
# @TEST_EDGE: export_validation_markdown -> export validation in markdown format
|
|
# @TEST_EDGE: batch_approve_semantic -> batch approve semantic fields
|
|
# @TEST_EDGE: update_execution_mapping_full -> full execution mapping update
|
|
# @TEST_EDGE: approve_execution_mapping_with_note -> approve mapping with note
|
|
# @TEST_EDGE: batch_approve_mappings -> batch approve mappings
|
|
# @TEST_EDGE: record_field_feedback -> persist field feedback
|
|
# @TEST_EDGE: record_clarification_feedback -> persist clarification feedback
|
|
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,
|
|
MappingMethod,
|
|
PreviewStatus,
|
|
ReadinessState,
|
|
RecommendedAction,
|
|
SessionStatus,
|
|
FieldProvenance,
|
|
)
|
|
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,
|
|
StartSessionCommand,
|
|
StartSessionResult,
|
|
)
|
|
from src.services.dataset_review.repositories.session_repository import (
|
|
DatasetReviewSessionVersionConflictError,
|
|
)
|
|
|
|
client = TestClient(app)
|
|
|
|
# ── Shared fixture: cleanup ───────────────────────────────────
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clean_overrides():
|
|
app.dependency_overrides.clear()
|
|
yield
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
# ── Builders ──────────────────────────────────────────────────
|
|
|
|
|
|
def _make_user():
|
|
permissions = [
|
|
SimpleNamespace(resource="dataset:session", action="READ"),
|
|
SimpleNamespace(resource="dataset:session", action="MANAGE"),
|
|
SimpleNamespace(resource="dataset:execution:launch", action="EXECUTE"),
|
|
]
|
|
role = SimpleNamespace(name="DatasetReviewOperator", permissions=permissions)
|
|
return SimpleNamespace(id="user-1", username="tester", roles=[role])
|
|
|
|
|
|
def _make_config_manager(ff_auto=True, ff_clarification=True, ff_execution=True):
|
|
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 = ff_auto
|
|
config.settings.ff_dataset_clarification = ff_clarification
|
|
config.settings.ff_dataset_execution = ff_execution
|
|
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 = "review"
|
|
s.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 = []
|
|
s.run_contexts = []
|
|
return s
|
|
|
|
|
|
def _make_field(field_id="field-1", **kw):
|
|
f = MagicMock()
|
|
f.field_id = field_id
|
|
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
|
|
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 = None
|
|
m.approval_state = kw.get("approval_state", ApprovalState.PENDING)
|
|
m.approved_by_user_id = None
|
|
m.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
|
|
return m
|
|
|
|
|
|
def _make_clarification_session(**kw):
|
|
cs = MagicMock()
|
|
cs.questions = kw.get("questions", [])
|
|
return cs
|
|
|
|
|
|
def _make_question(question_id="q-1", **kw):
|
|
q = MagicMock()
|
|
q.question_id = question_id
|
|
q.answer = kw.get("answer", None)
|
|
return q
|
|
|
|
|
|
def _make_answer(**kw):
|
|
a = MagicMock()
|
|
a.user_feedback = kw.get("user_feedback", None)
|
|
return a
|
|
|
|
|
|
def _setup_deps(repository=None, orchestrator=None, clarification_engine=None,
|
|
session=None, config_manager=None, user=None):
|
|
user = user or _make_user()
|
|
cm = config_manager or _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.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}
|
|
|
|
|
|
# ── list_sessions ─────────────────────────────────────────────
|
|
|
|
|
|
class TestListSessions:
|
|
"""GET /api/dataset-orchestration/sessions"""
|
|
|
|
# #region test_list_sessions_paginated [C:2] [TYPE Function]
|
|
def test_list_sessions_paginated(self):
|
|
s1 = _make_session(session_id="sess-1")
|
|
s2 = _make_session(session_id="sess-2")
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [s1, s2]
|
|
_setup_deps(repository=repo)
|
|
|
|
resp = client.get("/api/dataset-orchestration/sessions?page=1&page_size=1")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["total"] == 2
|
|
assert len(data["items"]) == 1
|
|
assert data["page"] == 1
|
|
assert data["page_size"] == 1
|
|
assert data["has_next"] is True
|
|
|
|
# #endregion test_list_sessions_paginated
|
|
|
|
# #region test_list_sessions_last_page [C:2] [TYPE Function]
|
|
def test_list_sessions_last_page(self):
|
|
s1 = _make_session(session_id="sess-1")
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [s1]
|
|
_setup_deps(repository=repo)
|
|
|
|
resp = client.get("/api/dataset-orchestration/sessions?page=1&page_size=20")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["total"] == 1
|
|
assert data["has_next"] is False
|
|
# #endregion test_list_sessions_last_page
|
|
|
|
|
|
# ── update_session ────────────────────────────────────────────
|
|
|
|
|
|
class TestUpdateSession:
|
|
"""PATCH /api/dataset-orchestration/sessions/{session_id}"""
|
|
|
|
# #region test_update_session_pause [C:2] [TYPE Function]
|
|
def test_update_session_pause(self):
|
|
session = _make_session(version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
repo.load_session_detail.return_value = session
|
|
repo.bump_session_version = MagicMock()
|
|
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",
|
|
json={"status": "paused"},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data is not None
|
|
# #endregion test_update_session_pause
|
|
|
|
# #region test_update_session_archive [C:2] [TYPE Function]
|
|
def test_update_session_archive(self):
|
|
session = _make_session(version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
repo.load_session_detail.return_value = session
|
|
repo.bump_session_version = MagicMock()
|
|
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",
|
|
json={"status": "archived"},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
# #endregion test_update_session_archive
|
|
|
|
|
|
# ── delete_session ────────────────────────────────────────────
|
|
|
|
|
|
class TestDeleteSession:
|
|
"""DELETE /api/dataset-orchestration/sessions/{session_id}"""
|
|
|
|
# #region test_delete_session_hard [C:2] [TYPE Function]
|
|
def test_delete_session_hard(self):
|
|
session = _make_session(version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
repo.load_session_detail.return_value = session
|
|
repo.require_session_version = MagicMock(return_value=session)
|
|
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?hard_delete=true",
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 204
|
|
# #endregion test_delete_session_hard
|
|
|
|
|
|
# ── export_validation ─────────────────────────────────────────
|
|
|
|
|
|
class TestExportValidation:
|
|
"""GET /api/dataset-orchestration/sessions/{session_id}/exports/validation"""
|
|
|
|
# #region test_export_validation_json [C:2] [TYPE Function]
|
|
def test_export_validation_json(self):
|
|
session = _make_session()
|
|
_setup_deps(session=session)
|
|
|
|
resp = client.get(
|
|
"/api/dataset-orchestration/sessions/sess-1/exports/validation?format=json"
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["artifact_type"] == "validation_report"
|
|
# #endregion test_export_validation_json
|
|
|
|
# #region test_export_validation_markdown [C:2] [TYPE Function]
|
|
def test_export_validation_markdown(self):
|
|
session = _make_session()
|
|
_setup_deps(session=session)
|
|
|
|
resp = client.get(
|
|
"/api/dataset-orchestration/sessions/sess-1/exports/validation?format=markdown"
|
|
)
|
|
assert resp.status_code == 200
|
|
# #endregion test_export_validation_markdown
|
|
|
|
|
|
# ── batch_approve_semantic_fields ─────────────────────────────
|
|
|
|
|
|
class TestBatchApproveSemantic:
|
|
"""POST /api/dataset-orchestration/sessions/{session_id}/fields/semantic/approve-batch"""
|
|
|
|
# #region test_batch_approve_semantic [C:2] [TYPE Function]
|
|
def test_batch_approve_semantic(self):
|
|
field1 = _make_field(field_id="field-1")
|
|
field2 = _make_field(field_id="field-2")
|
|
session = _make_session(semantic_fields=[field1, field2], version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
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": "field-1", "candidate_id": "cand-1", "lock_field": True},
|
|
{"field_id": "field-2", "candidate_id": "cand-2", "lock_field": False},
|
|
]
|
|
},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert isinstance(data, list)
|
|
# #endregion test_batch_approve_semantic
|
|
|
|
|
|
# ── update_execution_mapping ──────────────────────────────────
|
|
|
|
|
|
class TestUpdateExecutionMapping:
|
|
"""PATCH /api/dataset-orchestration/sessions/{session_id}/mappings/{mapping_id}"""
|
|
|
|
# #region test_update_execution_mapping [C:2] [TYPE Function]
|
|
def test_update_execution_mapping(self):
|
|
mapping = _make_mapping(mapping_id="map-1")
|
|
session = _make_session(execution_mappings=[mapping], version=1,
|
|
readiness_state=ReadinessState.MAPPING_REVIEW_NEEDED)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
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_value",
|
|
"mapping_method": "manual_override",
|
|
"transformation_note": "Updated by user",
|
|
},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["mapping_id"] == "map-1"
|
|
# #endregion test_update_execution_mapping
|
|
|
|
|
|
# ── approve_execution_mapping ─────────────────────────────────
|
|
|
|
|
|
class TestApproveExecutionMapping:
|
|
"""POST /api/dataset-orchestration/sessions/{session_id}/mappings/{mapping_id}/approve"""
|
|
|
|
# #region test_approve_mapping_with_note [C:2] [TYPE Function]
|
|
def test_approve_mapping_with_note(self):
|
|
mapping = _make_mapping(mapping_id="map-1")
|
|
session = _make_session(execution_mappings=[mapping], version=1,
|
|
readiness_state=ReadinessState.MAPPING_REVIEW_NEEDED)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
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": "Looks good"},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
# #endregion test_approve_mapping_with_note
|
|
|
|
|
|
# ── approve_batch_execution_mappings ──────────────────────────
|
|
|
|
|
|
class TestBatchApproveMappings:
|
|
"""POST /api/dataset-orchestration/sessions/{session_id}/mappings/approve-batch"""
|
|
|
|
# #region test_batch_approve_mappings [C:2] [TYPE Function]
|
|
def test_batch_approve_mappings(self):
|
|
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.list_sessions_for_user.return_value = [session]
|
|
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 isinstance(data, list)
|
|
assert len(data) == 2
|
|
# #endregion test_batch_approve_mappings
|
|
|
|
|
|
# ── record_field_feedback ─────────────────────────────────────
|
|
|
|
|
|
class TestRecordFieldFeedback:
|
|
"""POST /api/dataset-orchestration/sessions/{session_id}/fields/{field_id}/feedback"""
|
|
|
|
# #region test_field_feedback_thumbs_up [C:2] [TYPE Function]
|
|
def test_field_feedback_thumbs_up(self):
|
|
field = _make_field(field_id="field-1")
|
|
session = _make_session(semantic_fields=[field], version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
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/feedback",
|
|
json={"feedback": "thumbs_up"},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["feedback"] == "thumbs_up"
|
|
assert data["target_id"] == "field-1"
|
|
# #endregion test_field_feedback_thumbs_up
|
|
|
|
|
|
# ── record_clarification_feedback ─────────────────────────────
|
|
|
|
|
|
class TestRecordClarificationFeedback:
|
|
"""POST /api/dataset-orchestration/sessions/{session_id}/clarification/questions/{question_id}/feedback"""
|
|
|
|
# #region test_clarification_feedback_success [C:2] [TYPE Function]
|
|
def test_clarification_feedback_success(self):
|
|
answer = _make_answer()
|
|
question = _make_question(question_id="q-1", answer=answer)
|
|
cs = _make_clarification_session(questions=[question])
|
|
session = _make_session(clarification_sessions=[cs], version=1)
|
|
repo = MagicMock()
|
|
repo.list_sessions_for_user.return_value = [session]
|
|
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/clarification/questions/q-1/feedback",
|
|
json={"feedback": "thumbs_down"},
|
|
headers={"X-Session-Version": "1"},
|
|
)
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["feedback"] == "thumbs_down"
|
|
# #endregion test_clarification_feedback_success
|
|
# #endregion Test.DatasetReview.SessionLifecycle
|