169 lines
5.0 KiB
Python
169 lines
5.0 KiB
Python
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()
|