"""Unit tests for STRATZ matchup ranking + OpenDota cross classification. Run from repo root: python -m unittest shared.tests.test_matchup_cross -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 shared.matchup_cross import ( # noqa: E402 classify_cross_source, enrich_entry_cross, enrich_hero_matchups_cross, ) 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) class CrossClassifyTests(unittest.TestCase): def test_agree_conflict_weak(self) -> None: self.assertEqual( classify_cross_source( stratz_signed=5.0, odota_adv_val=0.04, odota_games=200 ), "agree", ) self.assertEqual( classify_cross_source( stratz_signed=5.0, odota_adv_val=-0.04, odota_games=200 ), "conflict", ) self.assertEqual( classify_cross_source( stratz_signed=5.0, odota_adv_val=0.04, odota_games=10 ), "weak", ) def test_countered_uses_restored_sign(self) -> None: # Display advantage for countered is +8 (negated); original vs was -8. by_odota = { "1": { "2": {"games": 200, "wins": 80}, # AM wr 40% vs peer } } # Baseline for hero 1 ≈ 0.4 from only this matchup. from shared.matchup_cross import build_baseline baseline = build_baseline(by_odota) entry = enrich_entry_cross( { "hero_id": 2, "games": 100, "wins": 40, "advantage": 8.0, "wr": 0.6, }, hero_id=1, kind="countered", by_odota=by_odota, baseline=baseline, min_games=80, ) # odota_adv = 0.4 - 0.4 = 0 → weak self.assertEqual(entry["cross"]["status"], "weak") def test_enrich_hero_keeps_stale_flag(self) -> None: cell = { "counters": [ { "hero_id": 94, "games": 100, "wins": 60, "advantage": 3.0, "wr": 0.6, } ], "countered": [], "synergies": [], "fetched_at": "t0", "stale": True, } out = enrich_hero_matchups_cross( cell, hero_id=1, by_odota={}, baseline={}, ) self.assertTrue(out["stale"]) self.assertEqual(out["cross"] if False else out["counters"][0]["cross"]["status"], "weak") if __name__ == "__main__": unittest.main()