import unittest from typing import Any, cast from server.metrics import ( MetricsStoreBuilder, MemoryMetricsStore, ) class TestMetricsStoreTemplate(unittest.IsolatedAsyncioTestCase): async def test_memory_backend_returns_local_snapshot(self): manager = MetricsStoreBuilder.build(backend="memory") local = { "games_started": 2, "games_ended": 1, "wins": 1, "losses": 0, "total_moves": 10, "total_turns": 42, "max_turn": 42, "active_games_peak": 2, "games_autocreated": 0, "http_requests_total": 13, "move_response_time_ms_total": 30.0, "move_response_time_ms_max": 6.0, "last_game_start_unix": 1, "last_game_end_unix": 2, "last_move_unix": 3, "games_stuck_removed": 0, "game_state_local_cache_enabled": False, "metrics_backend": "memory", "active_games": 1, "tracked_games": 1, "avg_turns_per_game": 42.0, "win_rate": 1.0, "avg_move_response_ms": 3.0, "oldest_active_game_age_sec": 0, "stale_game_timeout_sec": 180, "active_games_stale": 0, "http_requests_by_endpoint": {"info": 1, "start": 1, "move": 10, "end": 1}, "move_direction_counts": { "up": 4, "down": 2, "left": 2, "right": 2, "unknown": 0, }, } snapshot = await manager.snapshot(local) self.assertEqual(snapshot["games_started"], 2) self.assertEqual(snapshot["metrics_backend"], "memory") await manager.close() async def test_merge_snapshots_aggregates_totals(self): manager = MemoryMetricsStore() merged = manager._merge_snapshots( [ { "games_started": 2, "games_ended": 1, "wins": 1, "losses": 0, "total_moves": 10, "total_turns": 40, "max_turn": 40, "active_games_peak": 2, "games_autocreated": 1, "http_requests_total": 20, "move_response_time_ms_total": 50.0, "move_response_time_ms_max": 8.0, "last_game_start_unix": 10, "last_game_end_unix": 15, "last_move_unix": 16, "games_stuck_removed": 0, "active_games": 1, "tracked_games": 1, "oldest_active_game_age_sec": 5, "stale_game_timeout_sec": 180, "active_games_stale": 0, "game_state_local_cache_enabled": True, "http_requests_by_endpoint": { "info": 1, "start": 1, "move": 17, "end": 1, }, "move_direction_counts": { "up": 5, "down": 2, "left": 1, "right": 2, "unknown": 0, }, }, { "games_started": 1, "games_ended": 1, "wins": 0, "losses": 1, "total_moves": 6, "total_turns": 20, "max_turn": 20, "active_games_peak": 1, "games_autocreated": 0, "http_requests_total": 12, "move_response_time_ms_total": 20.0, "move_response_time_ms_max": 7.0, "last_game_start_unix": 12, "last_game_end_unix": 18, "last_move_unix": 19, "games_stuck_removed": 1, "active_games": 2, "tracked_games": 2, "oldest_active_game_age_sec": 7, "stale_game_timeout_sec": 180, "active_games_stale": 1, "game_state_local_cache_enabled": False, "http_requests_by_endpoint": { "info": 1, "start": 1, "move": 9, "end": 1, }, "move_direction_counts": { "up": 1, "down": 1, "left": 2, "right": 2, "unknown": 0, }, }, ] ) self.assertEqual(merged["games_started"], 3) self.assertEqual(merged["games_ended"], 2) self.assertEqual(merged["wins"], 1) self.assertEqual(merged["losses"], 1) self.assertEqual(merged["total_moves"], 16) self.assertEqual(merged["move_response_time_ms_total"], 70.0) self.assertEqual(merged["http_requests_by_endpoint"]["move"], 26) self.assertEqual(merged["move_direction_counts"]["left"], 3) self.assertEqual(merged["metrics_backend"], "redis") await manager.close() async def test_acquire_startup_cleanup_lock_uses_store_for_redis_backend(self): class FakeStore: def __init__(self): self.calls = [] async def acquire_startup_cleanup_lock(self, lock_key:str, ttl_seconds:int=300): self.calls.append((lock_key, ttl_seconds)) return True async def close(self): return None manager = MetricsStoreBuilder.build(backend="redis", key_prefix="snake:metrics:worker") fake_store = FakeStore() manager.store = cast(Any, fake_store) allowed = await manager.acquire_startup_cleanup_lock(180) self.assertTrue(allowed) self.assertEqual(fake_store.calls, [("snake:metrics:worker:startup_cleanup_lock", 180)]) await manager.close() if __name__ == "__main__": unittest.main()