from datetime import UTC, datetime from pathlib import Path from leaderboard_analytics.schemas import QueryFilters from leaderboard_analytics.services import AnalyticsService class FakeRepository: def overview_timeseries(self, filters: QueryFilters) -> list[dict]: return [ {"period": "2026-01-01", "pv": 2, "uv": 1, "session_count": 1, "event_count": 3}, {"period": "2026-01-02", "pv": 1, "uv": 1, "session_count": 1, "event_count": 2}, ] def overview_totals(self, filters: QueryFilters) -> dict: return {"pv": 3, "uv": 1, "sessions": 1, "events": 5} class LocationRepository: def __init__(self, rows: list[dict]) -> None: self.rows = rows def visitor_ip_counts(self, filters: QueryFilters) -> list[dict]: return self.rows class FakeGeoIpResolver: def __init__(self, countries: dict[str, tuple[str, str]]) -> None: self.countries = countries def resolve_country(self, ip_address: str) -> tuple[str, str]: return self.countries[ip_address] def test_overview_uses_full_range_distinct_totals() -> None: service = AnalyticsService(FakeRepository()) # type: ignore[arg-type] filters = QueryFilters( start_time=datetime(2026, 1, 1, tzinfo=UTC), end_time=datetime(2026, 1, 2, tzinfo=UTC), ) frame, totals = service.get_overview(filters) assert list(frame["period"]) == ["2026-01-01", "2026-01-02"] assert totals == { "pv": 3, "uv": 1, "sessions": 1, "events": 5, "events_per_session": 5.0, "sessions_per_visitor": 1.0, } def test_visitor_locations_groups_pv_and_ip_count_by_country() -> None: repository = LocationRepository( [ {"ip": "8.8.8.8", "pv": 3}, {"ip": "8.8.4.4", "pv": 2}, {"ip": "1.1.1.1", "pv": 4}, ] ) resolver = FakeGeoIpResolver( { "8.8.8.8": ("US", "United States"), "8.8.4.4": ("US", "United States"), "1.1.1.1": ("AU", "Australia"), } ) service = AnalyticsService( repository, # type: ignore[arg-type] geoip_resolver=resolver, # type: ignore[arg-type] ) frame = service.get_visitor_locations( QueryFilters( start_time=datetime(2026, 1, 1, tzinfo=UTC), end_time=datetime(2026, 1, 2, tzinfo=UTC), ) ) assert frame.to_dict("records") == [ {"country_code": "US", "country_name": "United States", "pv": 5, "ip_count": 2}, {"country_code": "AU", "country_name": "Australia", "pv": 4, "ip_count": 1}, ] def test_visitor_locations_groups_unresolved_ips_as_unknown() -> None: repository = LocationRepository( [ {"ip": "10.0.0.1", "pv": 2}, {"ip": "not-an-ip", "pv": 1}, {"ip": "8.8.8.8", "pv": 3}, ] ) service = AnalyticsService( repository, # type: ignore[arg-type] geoip_database_path=Path("missing-geolite2-country.mmdb"), ) frame = service.get_visitor_locations( QueryFilters( start_time=datetime(2026, 1, 1, tzinfo=UTC), end_time=datetime(2026, 1, 2, tzinfo=UTC), ) ) assert frame.to_dict("records") == [ {"country_code": "Unknown", "country_name": "Unknown", "pv": 6, "ip_count": 3} ]