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