"""record_hand idempotency + straddle parse coverage. The chat turn can execute twice — the SSE stream and the blocking fallback both run server-side (two 'chat request' lines, 1s apart) — which double-logged the same hand once logging became guaranteed. A system-of-record must record an event once.""" from __future__ import annotations import importlib import pytest @pytest.fixture def poker(tmp_path, monkeypatch): monkeypatch.setenv("LYRA_DB_PATH", str(tmp_path / "test.db")) from lyra import llm monkeypatch.setattr(llm, "embed", lambda texts: [[0.1, 0.2, 0.3] for _ in texts]) import lyra.memory as memory importlib.reload(memory) import lyra.poker as poker importlib.reload(poker) return poker _PARSED = { "game": "NLH", "hero_pos": "SB", "hero_cards": ["Ah", "Kh"], "board": ["Kd", "9d", "4c", "2s"], "players": [], "actions": [], "result": {"hero_net": -200, "pot": 400}, } def test_record_hand_is_idempotent_across_double_execution(poker, monkeypatch): sid = poker.start_session(venue="Borgata", stakes="1/3", buy_in=400) monkeypatch.setattr(poker, "parse_hand", lambda *a, **k: dict(_PARSED)) first = poker.record_hand("i have AhKh in the SB, btn straddle, ...") second = poker.record_hand("i have AhKh in the SB, btn straddle, ...") # the duplicate turn assert first["id"] == second["id"] assert second.get("deduped") is True assert len(poker.list_hands(sid)) == 1 # ledger holds ONE, not two def test_record_hand_does_not_dedupe_a_genuinely_different_hand(poker, monkeypatch): sid = poker.start_session(venue="Borgata", stakes="1/3", buy_in=400) monkeypatch.setattr(poker, "parse_hand", lambda *a, **k: dict(_PARSED)) poker.record_hand("hand one") other = dict(_PARSED, hero_cards=["Qs", "Qd"], board=["Qh", "7c", "2s"]) monkeypatch.setattr(poker, "parse_hand", lambda *a, **k: dict(other)) poker.record_hand("a different hand entirely") assert len(poker.list_hands(sid)) == 2 # distinct hands both land def test_dedupe_handles_boardless_hand(poker, monkeypatch): # NULL-safe match: a preflop-only hand (no board) still dedupes. sid = poker.start_session(venue="Borgata", buy_in=400) preflop = {"game": "NLH", "hero_pos": "BTN", "hero_cards": ["As", "Ks"], "board": [], "players": [], "actions": [], "result": {"hero_net": 30}} monkeypatch.setattr(poker, "parse_hand", lambda *a, **k: dict(preflop)) a = poker.record_hand("AKs btn, i open everyone folds") b = poker.record_hand("AKs btn, i open everyone folds") assert a["id"] == b["id"] and len(poker.list_hands(sid)) == 1 def test_parse_prompt_records_straddles(): from lyra import poker as pk p = pk._HAND_PARSE_PROMPT.lower() assert "straddle" in p and "button straddle" in p assert "acts last preflop" in p or "act last preflop" in p