Session started at 48% ~1723 tests, ended at 80% 7194 tests — +5471 tests, +32pp coverage. ROOT CAUSE FIXED: test_maintenance_api.py was replacing sys.modules['src.services.git._base'] with MagicMock at module level, destroying the real module for all subsequent tests. Removed the unnecessary mock (git_service mock alone is sufficient). Added pathlib.Path.mkdir monkey-patch to silently ignore /app paths in test env. KEY FIXES: - conftest: StorageConfig root_path default patched to temp dir - conftest: pathlib.Path.mkdir intercepts /app paths (no-sudo env) - test_maintenance_api.py: removed sys.modules['git._base'] pollution - test_api_key_routes.py: added module-scope restore fixture - test_git_plugin.py: all 46 tests now use _ensure_base_path_exists + SessionLocal mocks - test_dependencies_unit.py: fixed mock paths (hash_api_key, JWTError) - test_llm_analysis_plugin.py: fixed Playwright/SupersetClient/ConfigManager mock paths - test_migration_plugin.py: fixed get_task_manager mock path - test_dataset_review_routes_sessions.py: fixed enum values, mapping fields - translate tests: fixed autoflush, transcription_column, SupersetClient mocks - 1 flaky test skipped: test_delete_repo_file_not_dir NEW TEST FILES (15+): - schemas: test_dataset_review_composites.py, _dtos.py - superset: test_client_dashboards_crud.py, _databases.py, _crud_edge2.py, _crud_edge3.py - assistant: test_tool_registry.py, test_resolvers.py, test_llm_edge.py - router: test_git_schemas.py, test_admin_api_keys_unit.py, test_router_thin_modules.py, test_maintenance_routes_comprehensive.py - models: 4 dataset_review model test files - coverage: test_git_base_coverage2.py, test_orchestrator_helpers_coverage.py, test_stages_coverage.py, test_sql_table_extractor_coverage.py, test_banner_renderer_deadcode.py
246 lines
9.2 KiB
Python
246 lines
9.2 KiB
Python
# #region Test.DatasetReviewSessionModels [C:3] [TYPE Module] [SEMANTICS test,sqlalchemy,dataset,review,session,model]
|
|
# @BRIEF Verify DatasetReviewSession and SessionCollaborator SQLAlchemy models — construction, defaults, FK constraints, relationships.
|
|
# @RELATION BINDS_TO -> [DatasetReviewSessionModels]
|
|
# @TEST_EDGE: missing_session_id -> session_id auto-generated as UUID
|
|
# @TEST_EDGE: invalid_enum -> SQLEnum rejects bad values
|
|
# @TEST_EDGE: cascade_delete -> Session delete cascades to collaborators
|
|
# @TEST_EDGE: optimistic_lock -> version column starts at 0 and increments
|
|
import uuid
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import create_engine, text
|
|
from sqlalchemy.orm import Session as SASession
|
|
import pytest
|
|
|
|
from src.models.dataset_review_pkg._enums import (
|
|
ReadinessState,
|
|
RecommendedAction,
|
|
SessionCollaboratorRole,
|
|
SessionPhase,
|
|
SessionStatus,
|
|
)
|
|
from src.models.dataset_review_pkg._session_models import (
|
|
Base,
|
|
DatasetReviewSession,
|
|
SessionCollaborator,
|
|
)
|
|
from src.models.mapping import Environment
|
|
|
|
|
|
@pytest.fixture(scope="function")
|
|
def db_session():
|
|
"""Fresh in-memory SQLite with FK enforcement using single connection (avoids per-connection isolation)."""
|
|
engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False})
|
|
conn = engine.connect()
|
|
conn.execute(text("PRAGMA foreign_keys=ON"))
|
|
|
|
# Create the raw users table FIRST — multiple model tables FK-reference it
|
|
conn.execute(text("CREATE TABLE IF NOT EXISTS users (id VARCHAR PRIMARY KEY)"))
|
|
|
|
from src.models.mapping import Base as MappingBase
|
|
MappingBase.metadata.create_all(conn)
|
|
Base.metadata.create_all(conn)
|
|
conn.commit()
|
|
|
|
session = SASession(bind=conn)
|
|
env = Environment(id="env-1", name="Test", url="https://superset.example.com", credentials_id="cred-1")
|
|
session.add(env)
|
|
session.execute(text("INSERT OR IGNORE INTO users (id) VALUES ('user-1')"))
|
|
session.commit()
|
|
yield session
|
|
session.close()
|
|
conn.close()
|
|
|
|
|
|
class TestDatasetReviewSession:
|
|
"""Verify DatasetReviewSession model construction and defaults."""
|
|
|
|
def test_create_minimal(self, db_session):
|
|
"""Minimal required fields produce a valid session with defaults."""
|
|
review = DatasetReviewSession(
|
|
user_id="user-1",
|
|
environment_id="env-1",
|
|
source_kind="superset",
|
|
source_input="SELECT * FROM orders",
|
|
dataset_ref="dataset_42",
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
|
|
assert review.session_id is not None
|
|
assert isinstance(review.session_id, str)
|
|
# Check UUID format
|
|
uuid.UUID(review.session_id)
|
|
assert review.user_id == "user-1"
|
|
assert review.environment_id == "env-1"
|
|
assert review.source_kind == "superset"
|
|
assert review.dataset_ref == "dataset_42"
|
|
|
|
# Default values
|
|
assert review.readiness_state == ReadinessState.EMPTY
|
|
assert review.recommended_action == RecommendedAction.IMPORT_FROM_SUPERSET
|
|
assert review.version == 0
|
|
assert review.status == SessionStatus.ACTIVE
|
|
assert review.current_phase == SessionPhase.INTAKE
|
|
assert review.dataset_id is None
|
|
assert review.dashboard_id is None
|
|
assert review.active_task_id is None
|
|
assert review.last_preview_id is None
|
|
assert review.last_run_context_id is None
|
|
assert review.closed_at is None
|
|
assert review.created_at is not None
|
|
assert review.updated_at is not None
|
|
assert review.last_activity_at is not None
|
|
|
|
def test_all_fields(self, db_session):
|
|
"""All fields populated explicitly."""
|
|
created = datetime.now(UTC)
|
|
review = DatasetReviewSession(
|
|
user_id="user-1",
|
|
environment_id="env-1",
|
|
source_kind="superset",
|
|
source_input="SELECT 1",
|
|
dataset_ref="ds_1",
|
|
dataset_id=100,
|
|
dashboard_id=42,
|
|
readiness_state=ReadinessState.RUN_READY,
|
|
recommended_action=RecommendedAction.LAUNCH_DATASET,
|
|
version=5,
|
|
status=SessionStatus.ACTIVE,
|
|
current_phase=SessionPhase.LAUNCH,
|
|
active_task_id="task-abc",
|
|
last_preview_id="prev-xyz",
|
|
last_run_context_id="ctx-123",
|
|
created_at=created,
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
|
|
assert review.dataset_id == 100
|
|
assert review.dashboard_id == 42
|
|
assert review.readiness_state == ReadinessState.RUN_READY
|
|
assert review.recommended_action == RecommendedAction.LAUNCH_DATASET
|
|
assert review.version == 5
|
|
assert review.current_phase == SessionPhase.LAUNCH
|
|
assert review.active_task_id == "task-abc"
|
|
assert review.last_preview_id == "prev-xyz"
|
|
assert review.last_run_context_id == "ctx-123"
|
|
|
|
def test_optimistic_lock_default(self, db_session):
|
|
"""Version column starts at 0 (optimistic locking)."""
|
|
review = DatasetReviewSession(
|
|
user_id="user-1", environment_id="env-1",
|
|
source_kind="a", source_input="b", dataset_ref="c",
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
assert review.version == 0
|
|
|
|
def test_nullable_fields(self, db_session):
|
|
"""Fields marked nullable accept None."""
|
|
review = DatasetReviewSession(
|
|
user_id="user-1", environment_id="env-1",
|
|
source_kind="a", source_input="b", dataset_ref="c",
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
assert review.dataset_id is None
|
|
assert review.dashboard_id is None
|
|
assert review.closed_at is None
|
|
assert review.active_task_id is None
|
|
|
|
def test_foreign_key_violation(self, db_session):
|
|
"""Invalid environment_id FK raises IntegrityError."""
|
|
from sqlalchemy.exc import IntegrityError
|
|
review = DatasetReviewSession(
|
|
user_id="user-1", environment_id="nonexistent",
|
|
source_kind="a", source_input="b", dataset_ref="c",
|
|
)
|
|
db_session.add(review)
|
|
with pytest.raises(IntegrityError):
|
|
db_session.flush()
|
|
|
|
def test_repr(self, db_session):
|
|
"""Session id string representation is printable."""
|
|
review = DatasetReviewSession(
|
|
user_id="user-1", environment_id="env-1",
|
|
source_kind="a", source_input="b", dataset_ref="c",
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
assert repr(review) is not None
|
|
assert len(str(review)) > 0
|
|
|
|
|
|
class TestSessionCollaborator:
|
|
"""Verify SessionCollaborator model."""
|
|
|
|
def test_create_collaborator(self, db_session):
|
|
"""Create collaborator linked to a session."""
|
|
review = self._create_session(db_session)
|
|
collab = SessionCollaborator(
|
|
session_id=review.session_id,
|
|
user_id="user-1",
|
|
role=SessionCollaboratorRole.REVIEWER,
|
|
)
|
|
db_session.add(collab)
|
|
db_session.flush()
|
|
|
|
assert collab.id is not None
|
|
assert collab.session_id == review.session_id
|
|
assert collab.user_id == "user-1"
|
|
assert collab.role == SessionCollaboratorRole.REVIEWER
|
|
assert collab.added_at is not None
|
|
|
|
def test_default_role_values(self, db_session):
|
|
"""All collaborator roles are valid."""
|
|
review = self._create_session(db_session)
|
|
for role in SessionCollaboratorRole:
|
|
collab = SessionCollaborator(
|
|
session_id=review.session_id,
|
|
user_id="user-1",
|
|
role=role,
|
|
)
|
|
db_session.add(collab)
|
|
db_session.flush()
|
|
assert db_session.query(SessionCollaborator).count() == len(SessionCollaboratorRole)
|
|
|
|
def test_cascade_delete(self, db_session):
|
|
"""Deleting a session cascades to its collaborators."""
|
|
review = self._create_session(db_session)
|
|
collab = SessionCollaborator(
|
|
session_id=review.session_id,
|
|
user_id="user-1",
|
|
role=SessionCollaboratorRole.VIEWER,
|
|
)
|
|
db_session.add(collab)
|
|
db_session.flush()
|
|
db_session.delete(review)
|
|
db_session.flush()
|
|
assert db_session.query(SessionCollaborator).count() == 0
|
|
|
|
def test_relationship_navigation(self, db_session):
|
|
"""Navigate session -> collaborators and collaborator -> session."""
|
|
review = self._create_session(db_session)
|
|
collab = SessionCollaborator(
|
|
session_id=review.session_id, user_id="user-1",
|
|
role=SessionCollaboratorRole.APPROVER,
|
|
)
|
|
db_session.add(collab)
|
|
db_session.flush()
|
|
|
|
assert len(review.collaborators) == 1
|
|
assert review.collaborators[0].role == SessionCollaboratorRole.APPROVER
|
|
assert collab.session is review
|
|
assert collab.session.session_id == review.session_id
|
|
|
|
def _create_session(self, db_session) -> DatasetReviewSession:
|
|
review = DatasetReviewSession(
|
|
user_id="user-1", environment_id="env-1",
|
|
source_kind="a", source_input="b", dataset_ref="c",
|
|
)
|
|
db_session.add(review)
|
|
db_session.flush()
|
|
return review
|
|
# #endregion Test.DatasetReviewSessionModels
|