from datetime import UTC, datetime from leaderboard_analytics.repositories import AnalyticsRepository from leaderboard_analytics.schemas import QueryFilters class CapturingCollection: def __init__(self, rows: list[dict] | None = None) -> None: self.rows = rows or [] self.pipeline: list[dict] | None = None def aggregate(self, pipeline: list[dict]): self.pipeline = pipeline return iter(self.rows) def _filters() -> QueryFilters: return QueryFilters( start_time=datetime(2026, 1, 1, tzinfo=UTC), end_time=datetime(2026, 1, 31, tzinfo=UTC), ) def test_funnel_pipeline_preserves_ordered_step_logic() -> None: collection = CapturingCollection() repository = AnalyticsRepository(collection) # type: ignore[arg-type] repository.funnel(_filters()) assert collection.pipeline is not None assert {"$sort": {"session_id": 1, "event_ts": 1}} in collection.pipeline assert any( "$push" in stage.get("$group", {}).get("events", {}) for stage in collection.pipeline ) assert not any( "$addToSet" in str(stage) and "events" in str(stage) for stage in collection.pipeline ) assert any( "table_download_at" in str(stage) and "$filter_change_at" in str(stage) for stage in collection.pipeline ) def test_new_vs_returning_pipeline_computes_first_seen_before_range_match() -> None: collection = CapturingCollection() repository = AnalyticsRepository(collection) # type: ignore[arg-type] repository.visitors_new_vs_returning(_filters()) assert collection.pipeline is not None window_index = next( i for i, stage in enumerate(collection.pipeline) if "$setWindowFields" in stage ) range_match_index = next( i for i, stage in enumerate(collection.pipeline) if stage.get("$match", {}).get("event_ts") is not None ) assert window_index < range_match_index def test_overview_totals_filters_empty_identifiers() -> None: collection = CapturingCollection([{"pv": 1, "uv": 1, "sessions": 1, "events": 2}]) repository = AnalyticsRepository(collection) # type: ignore[arg-type] totals = repository.overview_totals(_filters()) assert totals == {"pv": 1, "uv": 1, "sessions": 1, "events": 2} assert collection.pipeline is not None pipeline_text = str(collection.pipeline) assert '"$sessions"' in pipeline_text or "'$sessions'" in pipeline_text assert '"$visitors"' in pipeline_text or "'$visitors'" in pipeline_text assert "$$s" in pipeline_text assert "$$v" in pipeline_text