Auto merge dev → main (#1339)
* feat(tally): /tally-claim, /tally-transfer, /tally-wipe commands Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): idle sweep, startup load, and empty-VC expiry Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * style(tally): parenthesize voice-state guard for clarity Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): update live tallies when sessions finish Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(tally): robust winner matching + cleanup of deleted-VC tallies Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tally): /dev-tally to manually attribute a win/loss in your VC Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ Path(os.environ["STORAGE_VOL_PATH"]).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2])) # repo root for `BOT` package
|
||||
|
||||
from BOT.tally import Tally, team_index_for, evaluate, format_status, strip_tag
|
||||
from BOT.tally import Tally, team_index_for, evaluate, format_status, strip_tag, team_identities
|
||||
|
||||
|
||||
def _teams():
|
||||
@@ -111,6 +111,47 @@ def test_persistence_roundtrip(tmp_path_env=None):
|
||||
assert got.mode == "player" and got.target == "bravo" and got.claimed_by == 99
|
||||
|
||||
|
||||
def _teams_resolved_mismatch():
|
||||
# squadron_short was resolved to "DSPLALPHA" but the winning_team_squadron
|
||||
# stripped value is the bare "DSPL".
|
||||
return [
|
||||
{"squadron_short": "DSPLALPHA", "squadron": "DSPL", "squadron_tagged": "-DSPL-",
|
||||
"players": [{"nick": "Alpha"}]},
|
||||
{"squadron_short": "ENEMY", "squadron": "ENEMY", "squadron_tagged": "=ENEMY=",
|
||||
"players": [{"nick": "Echo"}]},
|
||||
]
|
||||
|
||||
def test_evaluate_win_when_winner_matches_bare_squadron_not_resolved_short():
|
||||
t = Tally(guild_id=1, channel_id=2, mode="squadron", target="DSPL", display_target="DSPL")
|
||||
kind = evaluate(t, _teams_resolved_mismatch(), winner_short="DSPL", is_draw=False, session_id="s1")
|
||||
assert kind == "win" # must NOT be scored as a loss
|
||||
assert (t.wins, t.losses) == (1, 0)
|
||||
|
||||
def test_squadron_mode_matches_bare_name_despite_resolved_short():
|
||||
t = Tally(guild_id=1, channel_id=2, mode="squadron", target="DSPL", display_target="DSPL")
|
||||
from BOT.tally import team_index_for
|
||||
assert team_index_for(t, _teams_resolved_mismatch()) == 0
|
||||
|
||||
|
||||
def test_apply_manual_result_win_and_loss():
|
||||
import tempfile
|
||||
import BOT.tally as tal
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
tal.TALLY_PATH = Path(d) / "TALLY.json"
|
||||
tal._REGISTRY.clear()
|
||||
tal.claim(1, 2, "player", "bravo", "Bravo", user_id=7)
|
||||
win = tal.apply_manual_result(1, 2, "win")
|
||||
assert win is not None
|
||||
assert (win.wins, win.losses) == (1, 0)
|
||||
assert win.last_result_kind == "win" and win.last_opponent == "DEV"
|
||||
loss = tal.apply_manual_result(1, 2, "loss")
|
||||
assert loss is not None
|
||||
assert (loss.wins, loss.losses) == (1, 1)
|
||||
assert loss.last_result_kind == "loss" and loss.last_opponent == "DEV"
|
||||
# No active tally in a different VC → None
|
||||
assert tal.apply_manual_result(1, 999, "win") is None
|
||||
|
||||
|
||||
def _run():
|
||||
fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
||||
for fn in fns:
|
||||
|
||||
Reference in New Issue
Block a user