Files
ss-tools/backend/tests/api/test_assistant_dataset_review_extended.py

261 lines
11 KiB
Python

# #region Test.Assistant.DatasetReviewExtended [C:2] [TYPE Module] [SEMANTICS test,assistant,dataset,review,edge]
# @BRIEF Edge-case coverage for _dataset_review.py and _dataset_review_dispatch.py uncovered lines.
# @RELATION BINDS_TO -> [AssistantDatasetReview]
# @RELATION BINDS_TO -> [AssistantDatasetReviewDispatch]
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from datetime import UTC, datetime
from unittest.mock import MagicMock, AsyncMock, patch
import pytest
from fastapi import HTTPException
class TestMatchDatasetReviewFieldExtended:
"""_match_dataset_review_field — field_name and verbose_name matching paths."""
def test_match_by_field_name(self):
from src.api.routes.assistant._dataset_review import _match_dataset_review_field
ctx = {"semantic_fields": [{"field_id": "fld-1", "field_name": "region", "verbose_name": "Region"}]}
result = _match_dataset_review_field(ctx, "update the region field please")
assert result is not None
assert result["field_name"] == "region"
def test_match_by_verbose_name(self):
from src.api.routes.assistant._dataset_review import _match_dataset_review_field
ctx = {"semantic_fields": [{"field_id": "fld-1", "field_name": "rgn", "verbose_name": "Region Name"}]}
result = _match_dataset_review_field(ctx, "change Region Name")
assert result is not None
assert result["verbose_name"] == "Region Name"
def test_match_by_field_id_lowercase_in_message(self):
"""Match field when field_id appears in message text."""
from src.api.routes.assistant._dataset_review import _match_dataset_review_field
ctx = {"semantic_fields": [{"field_id": "FLD-001", "field_name": "sales", "verbose_name": "Sales"}]}
# field_id "fld-001" does not appear in message — should try field_name/verbose
result = _match_dataset_review_field(ctx, "about sales data")
assert result is not None
assert result["field_name"] == "sales"
class TestPlanDatasetReviewIntentExtended:
"""_plan_dataset_review_intent — uncovered branches."""
@pytest.fixture
def context(self):
return {
"session_id": "sess-1",
"version": 5,
"readiness_state": "REVIEW_READY",
"recommended_action": "REVIEW_DOCUMENTATION",
"mappings": [],
"findings": [{"code": "TEST"}],
"imported_filters": [{"filter_name": "region"}],
"preview": {"preview_status": "missing"},
"semantic_fields": [
{
"field_id": "fld-1",
"field_name": "region",
"verbose_name": "Region",
"candidates": [{"candidate_id": "cand-1"}],
}
],
}
def test_set_field_semantics_accept_candidate(self, context):
"""Accept first candidate via message keywords."""
from src.api.routes.assistant._dataset_review import _plan_dataset_review_intent
result = _plan_dataset_review_intent(
'apply field semantics target:field=fld-1 accept candidate',
context,
)
assert result is not None
assert result["operation"] == "dataset_review_set_field_semantics"
assert result["entities"]["candidate_id"] == "cand-1"
def test_set_field_semantics_with_label(self, context):
"""Set verbose_name via 'label' keyword (matches regex alternation)."""
from src.api.routes.assistant._dataset_review import _plan_dataset_review_intent
result = _plan_dataset_review_intent(
'set field semantics target:field=fld-1 label="Country"',
context,
)
assert result is not None
assert result["entities"]["verbose_name"] == "Country"
def test_set_field_semantics_with_description(self, context):
"""Set description via 'desc' keyword."""
from src.api.routes.assistant._dataset_review import _plan_dataset_review_intent
result = _plan_dataset_review_intent(
'set field semantics target:field=fld-1 desc="The country field"',
context,
)
assert result is not None
assert result["entities"]["description"] == "The country field"
def test_set_field_semantics_no_attributes_returns_none(self, context):
"""No candidate, verbose_name, description, or display_format returns None."""
from src.api.routes.assistant._dataset_review import _plan_dataset_review_intent
result = _plan_dataset_review_intent(
'set field semantics target:field=fld-1',
context,
)
assert result is None
def test_set_field_semantics_with_lock(self, context):
"""Lock field when message contains lock keywords."""
from src.api.routes.assistant._dataset_review import _plan_dataset_review_intent
result = _plan_dataset_review_intent(
'update semantic field target:field=fld-1 lock it desc="Locked"',
context,
)
assert result is not None
assert result["entities"]["lock_field"] is True
class TestDispatchDatasetReviewIntentExtended:
"""_dispatch_dataset_review_intent — uncovered branches."""
@pytest.fixture
def user(self):
from src.schemas.auth import User
return User(id="user-1", username="test", email="test@x.com", is_active=True,
auth_source="internal", created_at=datetime.now(UTC),
last_login=datetime.now(UTC), roles=[])
@pytest.fixture
def config_manager(self):
cm = MagicMock()
cm.get_environments = MagicMock(return_value=[])
return cm
def _make_session_mock(self, **overrides):
from src.models.dataset_review import ReadinessState, RecommendedAction, SessionStatus, SessionPhase
s = MagicMock()
s.session_id = overrides.get("session_id", "sess-1")
s.version = overrides.get("version", 5)
s.user_id = overrides.get("user_id", "user-1")
s.readiness_state = overrides.get("readiness_state",
MagicMock(value=ReadinessState.MAPPING_REVIEW_NEEDED.value))
s.recommended_action = overrides.get("recommended_action",
MagicMock(value=RecommendedAction.REVIEW_DOCUMENTATION.value))
s.execution_mappings = overrides.get("execution_mappings", [])
s.semantic_fields = overrides.get("semantic_fields", [])
s.last_activity_at = None
return s
def _make_mapping_mock(self, **overrides):
from src.models.dataset_review import ApprovalState
m = MagicMock()
m.mapping_id = overrides.get("mapping_id", "m-1")
m.approval_state = overrides.get("approval_state", ApprovalState.PENDING)
m.requires_explicit_approval = overrides.get("requires_explicit_approval", True)
return m
def _make_field_mock(self, **overrides):
f = MagicMock()
f.field_id = overrides.get("field_id", "fld-1")
f.field_name = overrides.get("field_name", "region")
return f
def test_approve_mappings_updates_readiness_state(self, user, config_manager):
"""When MAPPING_REVIEW_NEEDED, recommended_action changes to GENERATE_SQL_PREVIEW."""
from src.api.routes.assistant._dataset_review_dispatch import _dispatch_dataset_review_intent
from src.models.dataset_review import ApprovalState
mapping = self._make_mapping_mock(mapping_id="m-1", approval_state=ApprovalState.PENDING,
requires_explicit_approval=True)
session = self._make_session_mock(execution_mappings=[mapping], user_id="user-1")
intent = {
"operation": "dataset_review_approve_mappings",
"entities": {
"dataset_review_session_id": "sess-1",
"session_version": 5,
"mapping_ids": ["m-1"],
},
}
db = MagicMock()
with patch("src.api.routes.assistant._dataset_review_dispatch.DatasetReviewSessionRepository") as mock_repo_cls:
mock_repo = MagicMock()
mock_repo_cls.return_value = mock_repo
mock_repo.load_session_detail.return_value = session
mock_repo.require_session_version.return_value = None
mock_repo.bump_session_version.return_value = None
mock_repo.db = MagicMock()
mock_repo.event_logger = MagicMock()
text, task_id, actions = (
__import__("asyncio").run(_dispatch_dataset_review_intent(intent, user, config_manager, db))
)
assert "Approved" in text
def test_set_field_semantics_value_error(self, user, config_manager):
"""ValueError from _update_semantic_field_state becomes 400."""
from src.api.routes.assistant._dataset_review_dispatch import _dispatch_dataset_review_intent
field = self._make_field_mock()
session = self._make_session_mock(semantic_fields=[field], user_id="user-1")
intent = {
"operation": "dataset_review_set_field_semantics",
"entities": {
"dataset_review_session_id": "sess-1",
"session_version": 5,
"field_id": "fld-1",
"lock_field": True,
},
}
db = MagicMock()
with patch("src.api.routes.assistant._dataset_review_dispatch.DatasetReviewSessionRepository") as mock_repo_cls:
mock_repo = MagicMock()
mock_repo_cls.return_value = mock_repo
mock_repo.load_session_detail.return_value = session
mock_repo.require_session_version.return_value = None
with patch("src.api.routes.assistant._dataset_review_dispatch._update_semantic_field_state",
side_effect=ValueError("Invalid field value")):
with pytest.raises(HTTPException) as exc:
__import__("asyncio").run(
_dispatch_dataset_review_intent(intent, user, config_manager, db)
)
assert exc.value.status_code == 400
assert "Invalid" in str(exc.value.detail)
def test_set_field_semantics_field_not_found(self, user, config_manager):
"""Field not found in session raises 404."""
from src.api.routes.assistant._dataset_review_dispatch import _dispatch_dataset_review_intent
session = self._make_session_mock(semantic_fields=[], user_id="user-1")
intent = {
"operation": "dataset_review_set_field_semantics",
"entities": {
"dataset_review_session_id": "sess-1",
"session_version": 5,
"field_id": "nonexistent",
},
}
db = MagicMock()
with patch("src.api.routes.assistant._dataset_review_dispatch.DatasetReviewSessionRepository") as mock_repo_cls:
mock_repo = MagicMock()
mock_repo_cls.return_value = mock_repo
mock_repo.load_session_detail.return_value = session
mock_repo.require_session_version.return_value = None
with pytest.raises(HTTPException) as exc:
__import__("asyncio").run(
_dispatch_dataset_review_intent(intent, user, config_manager, db)
)
assert exc.value.status_code == 404
# #endregion Test.Assistant.DatasetReviewExtended