"""Unit tests for STRATZ matchup ranking + cache helpers. Run from repo root: python -m unittest shared.tests.test_stratz_matchups -v """ from __future__ import annotations import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from web.fetch_stratz_meta import ( # noqa: E402 annotate_matchup_cell, build_matchup_file_payload, load_previous_matchups, rank_matchup_lists, write_json_atomic, ) class RankMatchupListsTests(unittest.TestCase): def test_counters_sorted_by_advantage_not_raw_wr(self) -> None: # Wraith King: wr < 0.5 but positive relative advantage — still a counter. vs = [ { "hero_id": 42, "games": 3286, "wins": 1597, "advantage": 4.444, "wr": 0.486, }, { "hero_id": 94, "games": 1070, "wins": 649, "advantage": 12.994, "wr": 0.607, }, { "hero_id": 34, "games": 1171, "wins": 723, "advantage": 7.318, "wr": 0.617, }, ] ranked = rank_matchup_lists(vs, [], take=3) self.assertEqual( [e["hero_id"] for e in ranked["counters"]], [94, 34, 42], ) wk = ranked["counters"][2] self.assertLess(wk["wr"], 0.5) self.assertGreater(wk["advantage"], 0) def test_countered_mirrors_lowest_advantage(self) -> None: vs = [ {"hero_id": 1, "games": 100, "wins": 60, "advantage": 5.0, "wr": 0.6}, {"hero_id": 2, "games": 100, "wins": 40, "advantage": -8.0, "wr": 0.4}, {"hero_id": 3, "games": 50, "wins": 20, "advantage": -3.0, "wr": 0.4}, ] ranked = rank_matchup_lists(vs, [], take=2) fear = ranked["countered"] self.assertEqual([e["hero_id"] for e in fear], [2, 3]) self.assertAlmostEqual(fear[0]["advantage"], 8.0) self.assertAlmostEqual(fear[0]["wr"], 0.6) def test_synergies_sorted_desc(self) -> None: with_rows = [ {"hero_id": 10, "games": 10, "wins": 5, "synergy": 1.0, "wr": 0.5}, {"hero_id": 11, "games": 10, "wins": 6, "synergy": 4.5, "wr": 0.6}, ] ranked = rank_matchup_lists([], with_rows, take=2) self.assertEqual([e["hero_id"] for e in ranked["synergies"]], [11, 10]) class MatchupCacheHelpersTests(unittest.TestCase): def test_annotate_and_payload_metadata(self) -> None: cell = annotate_matchup_cell( {"counters": [{"hero_id": 1}], "countered": [], "synergies": []}, fetched_at="2026-07-28T00:00:00+00:00", stale=True, ) self.assertTrue(cell["stale"]) self.assertEqual(cell["fetched_at"], "2026-07-28T00:00:00+00:00") payload = build_matchup_file_payload( {"antimage": cell}, take=12, match_limit=50, started_at="2026-07-28T00:00:00+00:00", finished_at="2026-07-28T01:00:00+00:00", stats={"heroes": 1, "ok": 0, "failed": 1, "stale_kept": 1}, ) self.assertEqual(payload["scope"]["kind"], "global_aggregate") self.assertIn("global", payload["scope"]["kind"]) self.assertTrue(payload["scope"]["label_zh"]) self.assertEqual(payload["stats"]["stale_kept"], 1) def test_atomic_write_and_resume_load(self) -> None: with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "stratz_matchup_tops.json" payload = build_matchup_file_payload( { "antimage": annotate_matchup_cell( {"counters": [], "countered": [], "synergies": []}, fetched_at="t0", ) }, take=12, match_limit=50, started_at="t0", finished_at="t0", ) write_json_atomic(path, payload) loaded = load_previous_matchups(path) self.assertIn("antimage", loaded) if __name__ == "__main__": unittest.main()