mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-17 08:00:13 +01:00
feat(web): Core Refactor Phase A — extract sampling and cache modules; add adaptive TTL + eviction heuristics, Redis PoC, and metrics wiring. Tests added for TTL, eviction, exports, splash-adaptive, card index, and service worker. Docs+roadmap updated.
This commit is contained in:
parent
c4a7fc48ea
commit
a029d430c5
49 changed files with 3889 additions and 701 deletions
54
code/tests/test_sampling_unit.py
Normal file
54
code/tests/test_sampling_unit.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
from code.web.services import sampling
|
||||
from code.web.services import card_index
|
||||
|
||||
|
||||
def setup_module(module): # ensure deterministic env weights
|
||||
os.environ.setdefault("RARITY_W_MYTHIC", "1.2")
|
||||
|
||||
|
||||
def test_rarity_diminishing():
|
||||
# Monkeypatch internal index
|
||||
card_index._CARD_INDEX.clear() # type: ignore
|
||||
theme = "Test Theme"
|
||||
card_index._CARD_INDEX[theme] = [ # type: ignore
|
||||
{"name": "Mythic One", "tags": [theme], "color_identity": "G", "mana_cost": "G", "rarity": "mythic"},
|
||||
{"name": "Mythic Two", "tags": [theme], "color_identity": "G", "mana_cost": "G", "rarity": "mythic"},
|
||||
]
|
||||
def no_build():
|
||||
return None
|
||||
sampling.maybe_build_index = no_build # type: ignore
|
||||
cards = sampling.sample_real_cards_for_theme(theme, 2, None, synergies=[theme], commander=None)
|
||||
rarity_weights = [r for c in cards for r in c["reasons"] if r.startswith("rarity_weight_calibrated")] # type: ignore
|
||||
assert len(rarity_weights) >= 2
|
||||
v1 = float(rarity_weights[0].split(":")[-1])
|
||||
v2 = float(rarity_weights[1].split(":")[-1])
|
||||
assert v1 > v2 # diminishing returns
|
||||
|
||||
|
||||
def test_commander_overlap_monotonic_diminishing():
|
||||
cmd_tags = {"A","B","C","D"}
|
||||
synergy_set = {"A","B","C","D","E"}
|
||||
# Build artificial card tag lists with increasing overlaps
|
||||
bonus1 = sampling.commander_overlap_scale(cmd_tags, ["A"], synergy_set)
|
||||
bonus2 = sampling.commander_overlap_scale(cmd_tags, ["A","B"], synergy_set)
|
||||
bonus3 = sampling.commander_overlap_scale(cmd_tags, ["A","B","C"], synergy_set)
|
||||
assert 0 < bonus1 < bonus2 < bonus3
|
||||
# Diminishing increments: delta shrinks
|
||||
assert (bonus2 - bonus1) > 0
|
||||
assert (bonus3 - bonus2) < (bonus2 - bonus1)
|
||||
|
||||
|
||||
def test_splash_off_color_penalty_applied():
|
||||
card_index._CARD_INDEX.clear() # type: ignore
|
||||
theme = "Splash Theme"
|
||||
# Commander W U B R (4 colors)
|
||||
commander = {"name": "CommanderTest", "tags": [theme], "color_identity": "WUBR", "mana_cost": "", "rarity": "mythic"}
|
||||
# Card with single off-color G (W U B R G)
|
||||
splash_card = {"name": "CardSplash", "tags": [theme], "color_identity": "WUBRG", "mana_cost": "G", "rarity": "rare"}
|
||||
card_index._CARD_INDEX[theme] = [commander, splash_card] # type: ignore
|
||||
sampling.maybe_build_index = lambda: None # type: ignore
|
||||
cards = sampling.sample_real_cards_for_theme(theme, 2, None, synergies=[theme], commander="CommanderTest")
|
||||
splash = next((c for c in cards if c["name"] == "CardSplash"), None)
|
||||
assert splash is not None
|
||||
assert any(r.startswith("splash_off_color_penalty") for r in splash["reasons"]) # type: ignore
|
||||
Loading…
Add table
Add a link
Reference in a new issue