# #region Test.Schemas.DatasetReview [C:2] [TYPE Module] [SEMANTICS test,schema,dataset,review] # @BRIEF Tests for schemas/dataset_review.py and dataset_review_pkg/* — DTOs and composites. # @RELATION BINDS_TO -> [DatasetReviewSchemas] # @TEST_EDGE: missing_field -> optional fields default correctly # @TEST_EDGE: invalid_type -> Pydantic validation rejects bad types # @TEST_EDGE: serialization -> round-trip via model_dump/model_validate from datetime import datetime, timezone import pytest # ── DTOs ──────────────────────────────────────────────────── class TestSessionCollaboratorDto: """SessionCollaboratorDto — from dataset_review_pkg._dtos.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import SessionCollaboratorDto from src.models.dataset_review import SessionCollaboratorRole ts = datetime.now(timezone.utc) dto = SessionCollaboratorDto( user_id="u-1", role=SessionCollaboratorRole.VIEWER, added_at=ts, ) assert dto.user_id == "u-1" assert dto.role == SessionCollaboratorRole.VIEWER def test_serialize_roundtrip(self): from src.schemas.dataset_review_pkg._dtos import SessionCollaboratorDto from src.models.dataset_review import SessionCollaboratorRole ts = datetime.now(timezone.utc) dto = SessionCollaboratorDto(user_id="u-1", role=SessionCollaboratorRole.REVIEWER, added_at=ts) data = dto.model_dump() restored = SessionCollaboratorDto.model_validate(data) assert restored.user_id == "u-1" assert restored.role == "reviewer" def test_from_attributes(self): from src.schemas.dataset_review_pkg._dtos import SessionCollaboratorDto from src.models.dataset_review import SessionCollaboratorRole ts = datetime.now(timezone.utc) class FakeORM: user_id = "u-1" role = SessionCollaboratorRole.APPROVER added_at = ts dto = SessionCollaboratorDto.model_validate(FakeORM()) assert dto.user_id == "u-1" class TestDatasetProfileDto: """DatasetProfileDto — full profile DTO.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import DatasetProfileDto from src.models.dataset_review import BusinessSummarySource, ConfidenceState ts = datetime.now(timezone.utc) dto = DatasetProfileDto( profile_id="p-1", session_id="s-1", dataset_name="sales", business_summary="Sales data", business_summary_source=BusinessSummarySource.IMPORTED, is_sqllab_view=False, confidence_state=ConfidenceState.CONFIRMED, has_blocking_findings=False, has_warning_findings=False, manual_summary_locked=False, created_at=ts, updated_at=ts, ) assert dto.profile_id == "p-1" assert dto.completeness_score is None def test_serialize_roundtrip(self): from src.schemas.dataset_review_pkg._dtos import DatasetProfileDto from src.models.dataset_review import BusinessSummarySource, ConfidenceState ts = datetime.now(timezone.utc) dto = DatasetProfileDto( profile_id="p-1", session_id="s-1", dataset_name="sales", business_summary="Sales data", business_summary_source=BusinessSummarySource.INFERRED, is_sqllab_view=False, confidence_state=ConfidenceState.MIXED, has_blocking_findings=False, has_warning_findings=False, manual_summary_locked=False, created_at=ts, updated_at=ts, ) data = dto.model_dump() restored = DatasetProfileDto.model_validate(data) assert restored.profile_id == "p-1" class TestValidationFindingDto: """ValidationFindingDto — finding with resolution tracking.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import ValidationFindingDto from src.models.dataset_review import FindingArea, FindingSeverity, ResolutionState ts = datetime.now(timezone.utc) dto = ValidationFindingDto( finding_id="f-1", session_id="s-1", area=FindingArea.DATASET_PROFILE, severity=FindingSeverity.BLOCKING, code="DQ001", title="Null values", message="Found null values", resolution_state=ResolutionState.OPEN, created_at=ts, ) assert dto.finding_id == "f-1" assert dto.resolved_at is None def test_serialize_roundtrip(self): from src.schemas.dataset_review_pkg._dtos import ValidationFindingDto from src.models.dataset_review import FindingArea, FindingSeverity, ResolutionState ts = datetime.now(timezone.utc) dto = ValidationFindingDto( finding_id="f-1", session_id="s-1", area=FindingArea.SOURCE_INTAKE, severity=FindingSeverity.WARNING, code="DQ002", title="Duplicates", message="Found duplicates", resolution_state=ResolutionState.RESOLVED, resolved_at=ts, created_at=ts, ) data = dto.model_dump() restored = ValidationFindingDto.model_validate(data) assert restored.code == "DQ002" class TestSemanticSourceDto: """SemanticSourceDto — source with trust level.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import SemanticSourceDto from src.models.dataset_review import SemanticSourceStatus, SemanticSourceType, TrustLevel ts = datetime.now(timezone.utc) dto = SemanticSourceDto( source_id="s-1", session_id="ses-1", source_type=SemanticSourceType.REFERENCE_DATASET, source_ref="ref1", source_version="1.0", display_name="Reference Dataset", trust_level=TrustLevel.TRUSTED, status=SemanticSourceStatus.AVAILABLE, created_at=ts, ) assert dto.source_id == "s-1" assert dto.schema_overlap_score is None def test_serialize_roundtrip(self): from src.schemas.dataset_review_pkg._dtos import SemanticSourceDto from src.models.dataset_review import SemanticSourceStatus, SemanticSourceType, TrustLevel ts = datetime.now(timezone.utc) dto = SemanticSourceDto( source_id="s-1", session_id="ses-1", source_type=SemanticSourceType.CONNECTED_DICTIONARY, source_ref="ref1", source_version="1.0", display_name="Dictionary", trust_level=TrustLevel.RECOMMENDED, status=SemanticSourceStatus.SELECTED, created_at=ts, ) data = dto.model_dump() restored = SemanticSourceDto.model_validate(data) assert restored.trust_level == "recommended" class TestSemanticCandidateDto: """SemanticCandidateDto — candidate with match type.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import SemanticCandidateDto from src.models.dataset_review import CandidateMatchType, CandidateStatus ts = datetime.now(timezone.utc) dto = SemanticCandidateDto( candidate_id="c-1", field_id="f-1", candidate_rank=1, match_type=CandidateMatchType.EXACT, confidence_score=0.95, status=CandidateStatus.ACCEPTED, created_at=ts, ) assert dto.confidence_score == 0.95 assert dto.source_id is None def test_serialize_roundtrip(self): from src.schemas.dataset_review_pkg._dtos import SemanticCandidateDto from src.models.dataset_review import CandidateMatchType, CandidateStatus ts = datetime.now(timezone.utc) dto = SemanticCandidateDto( candidate_id="c-1", field_id="f-1", candidate_rank=1, match_type=CandidateMatchType.FUZZY, confidence_score=0.75, status=CandidateStatus.PROPOSED, created_at=ts, ) data = dto.model_dump() restored = SemanticCandidateDto.model_validate(data) assert restored.status == "proposed" class TestSemanticFieldEntryDto: """SemanticFieldEntryDto — field with nested candidates.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import SemanticFieldEntryDto from src.models.dataset_review import FieldKind, FieldProvenance ts = datetime.now(timezone.utc) dto = SemanticFieldEntryDto( field_id="f-1", session_id="s-1", field_name="amount", field_kind=FieldKind.METRIC, provenance=FieldProvenance.FUZZY_INFERRED, is_locked=False, has_conflict=False, needs_review=False, last_changed_by="admin", created_at=ts, updated_at=ts, ) assert dto.candidates == [] def test_with_candidates(self): from src.schemas.dataset_review_pkg._dtos import SemanticCandidateDto, SemanticFieldEntryDto from src.models.dataset_review import CandidateMatchType, CandidateStatus, FieldKind, FieldProvenance ts = datetime.now(timezone.utc) cand = SemanticCandidateDto( candidate_id="c-1", field_id="f-1", candidate_rank=1, match_type=CandidateMatchType.EXACT, confidence_score=0.99, status=CandidateStatus.ACCEPTED, created_at=ts, ) dto = SemanticFieldEntryDto( field_id="f-1", session_id="s-1", field_name="revenue", field_kind=FieldKind.METRIC, provenance=FieldProvenance.DICTIONARY_EXACT, is_locked=True, has_conflict=False, needs_review=False, last_changed_by="admin", candidates=[cand], created_at=ts, updated_at=ts, ) assert len(dto.candidates) == 1 assert dto.candidates[0].confidence_score == 0.99 class TestImportedFilterDto: """ImportedFilterDto — filter with raw_value and confidence.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import ImportedFilterDto from src.models.dataset_review import FilterConfidenceState, FilterRecoveryStatus, FilterSource ts = datetime.now(timezone.utc) dto = ImportedFilterDto( filter_id="fl-1", session_id="s-1", filter_name="region", raw_value="EMEA", source=FilterSource.SUPERSET_URL, confidence_state=FilterConfidenceState.CONFIRMED, requires_confirmation=False, recovery_status=FilterRecoveryStatus.RECOVERED, created_at=ts, updated_at=ts, ) assert dto.filter_name == "region" assert dto.normalized_value is None def test_with_raw_value_masked(self): from src.schemas.dataset_review_pkg._dtos import ImportedFilterDto from src.models.dataset_review import FilterConfidenceState, FilterRecoveryStatus, FilterSource ts = datetime.now(timezone.utc) dto = ImportedFilterDto( filter_id="fl-1", session_id="s-1", filter_name="region", raw_value="***", raw_value_masked=True, source=FilterSource.SUPERSET_NATIVE, confidence_state=FilterConfidenceState.IMPORTED, requires_confirmation=True, recovery_status=FilterRecoveryStatus.PARTIAL, created_at=ts, updated_at=ts, ) assert dto.raw_value_masked is True assert dto.requires_confirmation is True class TestTemplateVariableDto: """TemplateVariableDto — variable with mapping status.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import TemplateVariableDto from src.models.dataset_review import MappingStatus, VariableKind ts = datetime.now(timezone.utc) dto = TemplateVariableDto( variable_id="v-1", session_id="s-1", variable_name="country", expression_source="{{ country }}", variable_kind=VariableKind.NATIVE_FILTER, is_required=True, mapping_status=MappingStatus.UNMAPPED, created_at=ts, updated_at=ts, ) assert dto.variable_name == "country" assert dto.default_value is None class TestExecutionMappingDto: """ExecutionMappingDto — mapping with approval state.""" def test_minimal(self): from src.schemas.dataset_review_pkg._dtos import ExecutionMappingDto from src.models.dataset_review import ApprovalState, MappingMethod ts = datetime.now(timezone.utc) dto = ExecutionMappingDto( mapping_id="m-1", session_id="s-1", filter_id="f-1", variable_id="v-1", mapping_method=MappingMethod.DIRECT_MATCH, raw_input_value="EMEA", requires_explicit_approval=False, approval_state=ApprovalState.NOT_REQUIRED, created_at=ts, updated_at=ts, ) assert dto.mapping_method == "direct_match" assert dto.effective_value is None # ── Composites ────────────────────────────────────────────── class TestClarificationOptionDto: """ClarificationOptionDto — option for clarification question.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import ClarificationOptionDto dto = ClarificationOptionDto( option_id="o-1", question_id="q-1", label="Yes", value="yes", is_recommended=True, display_order=1, ) assert dto.label == "Yes" assert dto.is_recommended is True class TestClarificationAnswerDto: """ClarificationAnswerDto — answer with feedback.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import ClarificationAnswerDto from src.models.dataset_review import AnswerKind ts = datetime.now(timezone.utc) dto = ClarificationAnswerDto( answer_id="a-1", question_id="q-1", answer_kind=AnswerKind.SELECTED, answered_by_user_id="u-1", created_at=ts, ) assert dto.impact_summary is None class TestClarificationQuestionDto: """ClarificationQuestionDto — question with nested options and answer.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import ClarificationQuestionDto from src.models.dataset_review import QuestionState ts = datetime.now(timezone.utc) dto = ClarificationQuestionDto( question_id="q-1", clarification_session_id="cs-1", topic_ref="topic1", question_text="What?", why_it_matters="Need to know", priority=1, state=QuestionState.OPEN, created_at=ts, updated_at=ts, ) assert dto.options == [] assert dto.answer is None class TestClarificationSessionDto: """ClarificationSessionDto — session with nested questions.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import ClarificationSessionDto from src.models.dataset_review import ClarificationStatus ts = datetime.now(timezone.utc) dto = ClarificationSessionDto( clarification_session_id="cs-1", session_id="s-1", status=ClarificationStatus.ACTIVE, resolved_count=0, remaining_count=5, started_at=ts, updated_at=ts, ) assert dto.questions == [] class TestCompiledPreviewDto: """CompiledPreviewDto — preview with fingerprint.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import CompiledPreviewDto from src.models.dataset_review import PreviewStatus ts = datetime.now(timezone.utc) dto = CompiledPreviewDto( preview_id="p-1", session_id="s-1", preview_status=PreviewStatus.READY, preview_fingerprint="abc123", compiled_by="admin", compiled_at=ts, created_at=ts, ) assert dto.error_code is None class TestDatasetRunContextDto: """DatasetRunContextDto — run context with launch audit.""" def test_minimal(self): from src.schemas.dataset_review_pkg._composites import DatasetRunContextDto from src.models.dataset_review import LaunchStatus ts = datetime.now(timezone.utc) dto = DatasetRunContextDto( run_context_id="rc-1", session_id="s-1", dataset_ref="ds1", environment_id="env-1", preview_id="p-1", sql_lab_session_ref="sql1", effective_filters={}, template_params={}, approved_mapping_ids=[], semantic_decision_refs=[], open_warning_refs=[], launch_status=LaunchStatus.SUCCESS, created_at=ts, ) assert dto.launch_status == "success" class TestSessionSummary: """SessionSummary — lightweight session DTO.""" def test_minimal(self): from src.models.dataset_review import ReadinessState, RecommendedAction, SessionPhase, SessionStatus from src.schemas.dataset_review_pkg._composites import SessionSummary ts = datetime.now(timezone.utc) dto = SessionSummary( session_id="s-1", user_id="u-1", environment_id="env-1", source_kind="dashboard", source_input="42", dataset_ref="ds1", readiness_state=ReadinessState.EMPTY, recommended_action=RecommendedAction.IMPORT_FROM_SUPERSET, status=SessionStatus.ACTIVE, current_phase=SessionPhase.INTAKE, created_at=ts, updated_at=ts, last_activity_at=ts, ) assert dto.session_id == "s-1" class TestSessionDetail: """SessionDetail — full session with nested aggregates.""" def test_minimal(self): from src.models.dataset_review import ReadinessState, RecommendedAction, SessionPhase, SessionStatus from src.schemas.dataset_review_pkg._composites import SessionDetail ts = datetime.now(timezone.utc) dto = SessionDetail( session_id="s-1", user_id="u-1", environment_id="env-1", source_kind="dashboard", source_input="42", dataset_ref="ds1", readiness_state=ReadinessState.EMPTY, recommended_action=RecommendedAction.IMPORT_FROM_SUPERSET, status=SessionStatus.ACTIVE, current_phase=SessionPhase.INTAKE, created_at=ts, updated_at=ts, last_activity_at=ts, ) assert dto.collaborators == [] assert dto.profile is None assert dto.findings == [] assert dto.semantic_sources == [] assert dto.semantic_fields == [] def test_with_nested(self): from src.models.dataset_review import ( BusinessSummarySource, ConfidenceState, ReadinessState, RecommendedAction, SessionCollaboratorRole, SessionPhase, SessionStatus, ) from src.schemas.dataset_review_pkg._composites import SessionDetail from src.schemas.dataset_review_pkg._dtos import DatasetProfileDto, SessionCollaboratorDto ts = datetime.now(timezone.utc) collab = SessionCollaboratorDto(user_id="u-1", role=SessionCollaboratorRole.VIEWER, added_at=ts) profile = DatasetProfileDto( profile_id="p-1", session_id="s-1", dataset_name="sales", business_summary="Sales data", business_summary_source=BusinessSummarySource.IMPORTED, is_sqllab_view=False, confidence_state=ConfidenceState.CONFIRMED, has_blocking_findings=False, has_warning_findings=False, manual_summary_locked=False, created_at=ts, updated_at=ts, ) dto = SessionDetail( session_id="s-1", user_id="u-1", environment_id="env-1", source_kind="dashboard", source_input="42", dataset_ref="ds1", readiness_state=ReadinessState.EMPTY, recommended_action=RecommendedAction.IMPORT_FROM_SUPERSET, status=SessionStatus.ACTIVE, current_phase=SessionPhase.INTAKE, created_at=ts, updated_at=ts, last_activity_at=ts, collaborators=[collab], profile=profile, ) assert len(dto.collaborators) == 1 assert dto.profile is not None assert dto.profile.dataset_name == "sales" def test_facade_reimport(self): """Verify dataset_review.py facade re-exports work.""" from src.schemas.dataset_review import SessionDetail, SessionSummary, DatasetProfileDto assert SessionDetail is not None assert SessionSummary is not None assert DatasetProfileDto is not None # #endregion Test.Schemas.DatasetReview