"""Dashboard analytics: aggregate counts scoped correctly to the requesting user (never leaking another user's data), zero-filled for enum members with no data, and recent signals ordered newest-first.""" from __future__ import annotations import uuid from datetime import UTC, datetime import pytest from app.models.alert import Alert from app.models.company import Company from app.models.detected_change import DetectedChange from app.models.enums import ( ChangeType, MonitoringRunStatus, MonitoringRunTrigger, SeverityLevel, SourceType, ) from app.models.monitoring_run import MonitoringRun from app.models.snapshot import Snapshot from app.models.source import Source from app.services.analytics_service import get_dashboard_analytics async def _make_company_with_activity(db_session, user_id: uuid.UUID) -> Company: company = Company( id=uuid.uuid4(), user_id=user_id, name="Acme Corp", slug=f"acme-{uuid.uuid4().hex[:6]}" ) db_session.add(company) await db_session.flush() source = Source( company_id=company.id, source_type=SourceType.WEBSITE, name="Site", base_url="https://acme.example", ) db_session.add(source) await db_session.flush() run = MonitoringRun( company_id=company.id, trigger_type=MonitoringRunTrigger.MANUAL, status=MonitoringRunStatus.SUCCESSFUL, ) db_session.add(run) await db_session.flush() snapshot = Snapshot( company_id=company.id, source_id=source.id, snapshot_type=source.source_type.value, hash="h1", structured_summary={}, text_summary="", monitoring_run_id=run.id, ) db_session.add(snapshot) await db_session.flush() change = DetectedChange( company_id=company.id, source_id=source.id, monitoring_run_id=run.id, current_snapshot_id=snapshot.id, change_type=ChangeType.LEADERSHIP_CHANGE, raw_diff={}, significance_score=0.6, confidence_score=0.8, severity=SeverityLevel.HIGH, summary="New CEO announced", ) db_session.add(change) await db_session.flush() alert = Alert( company_id=company.id, detected_change_id=change.id, user_id=user_id, title="Leadership change", summary="New CEO announced", why_it_matters="Signals a strategy shift", severity=SeverityLevel.HIGH, confidence=0.8, ) db_session.add(alert) await db_session.commit() return company @pytest.mark.asyncio async def test_analytics_aggregates_counts_for_the_requesting_user(db_session): user_id = uuid.uuid4() await _make_company_with_activity(db_session, user_id) analytics = await get_dashboard_analytics(db_session, user_id) assert analytics.changes_by_type["leadership_change"] == 1 assert analytics.changes_by_type["price_change"] == 0 assert analytics.alerts_by_severity["high"] == 1 assert analytics.alerts_by_severity["critical"] == 0 assert analytics.sources_by_status["active"] == 1 assert len(analytics.recent_signals) == 1 assert analytics.recent_signals[0].company_name == "Acme Corp" assert analytics.recent_signals[0].change_type == "leadership_change" @pytest.mark.asyncio async def test_analytics_does_not_leak_another_users_data(db_session): user_id = uuid.uuid4() other_user_id = uuid.uuid4() await _make_company_with_activity(db_session, other_user_id) analytics = await get_dashboard_analytics(db_session, user_id) assert all(count == 0 for count in analytics.changes_by_type.values()) assert all(count == 0 for count in analytics.alerts_by_severity.values()) assert analytics.recent_signals == [] @pytest.mark.asyncio async def test_analytics_recent_signals_ordered_newest_first(db_session): user_id = uuid.uuid4() company = await _make_company_with_activity(db_session, user_id) source = Source( company_id=company.id, source_type=SourceType.WEBSITE, name="Site 2", base_url="https://acme.example/2", ) db_session.add(source) await db_session.flush() run = MonitoringRun(company_id=company.id, trigger_type=MonitoringRunTrigger.MANUAL) db_session.add(run) await db_session.flush() snapshot = Snapshot( company_id=company.id, source_id=source.id, snapshot_type=source.source_type.value, hash="h2", structured_summary={}, text_summary="", monitoring_run_id=run.id, ) db_session.add(snapshot) await db_session.flush() newer_change = DetectedChange( company_id=company.id, source_id=source.id, monitoring_run_id=run.id, current_snapshot_id=snapshot.id, change_type=ChangeType.PRICE_CHANGE, raw_diff={}, significance_score=0.5, confidence_score=0.7, severity=SeverityLevel.MEDIUM, summary="Price increased", created_at=datetime.now(UTC), ) db_session.add(newer_change) await db_session.commit() analytics = await get_dashboard_analytics(db_session, user_id) assert analytics.recent_signals[0].change_type == "price_change"