package game // Disconnect-forfeit regressions: a player who disconnects mid-round // forfeits the committed stake, so startup recovery must NOT refund it; a // stake left outstanding by a genuine crash still is. import ( "testing" "thehouseoficarus/internal/casino" ) // TestDisconnectForfeitsMachineStake: disconnecting mid-spin forfeits the // stake (the machine frees the seat immediately); recovery must not refund // it on the next startup. func TestDisconnectForfeitsMachineStake(t *testing.T) { h := newHarness(t) h.seedAccount("alice", "pw", false) p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = 100 h.g.Casino.JoinMachine(1, casino.MachineConfig{ID: "slots", Game: casino.GameSlots, SpinTicks: 50}, "alice") h.g.Casino.SetMachineBet(1, "slots", "alice", 10, casinoWallet{g: h.g, p: p}) // The disconnect path forfeits the in-flight spin. h.g.Casino.MarkDisconnected("alice") }) h.barrier() restarted := harnessFromDir(t, h.dir) loaded, err := restarted.g.AccountStore.LoadCharacter("alice") if err != nil { t.Fatal(err) } if loaded.Credits != 90 { t.Fatalf("credits after forfeited spin = %d, want 90 (disconnect forfeits)", loaded.Credits) } } // TestDisconnectForfeitsBlackjackStake: same policy at a table: the seat // frees up and the stake is not refunded by recovery. func TestDisconnectForfeitsBlackjackStake(t *testing.T) { h := newHarness(t) h.seedAccount("alice", "pw", false) p := h.seedCharacter("alice", "alice") h.g.RunOnOwner(func() { p.Credits = 100 h.g.Casino.Join(1, casino.TableConfig{ID: "bj", Game: casino.GameBlackjack, MaxPlayers: 2}, "alice") h.g.Casino.Bet(1, "bj", "alice", "", 10, casinoWallet{g: h.g, p: p}) h.g.Casino.MarkDisconnected("alice") }) h.barrier() restarted := harnessFromDir(t, h.dir) loaded, err := restarted.g.AccountStore.LoadCharacter("alice") if err != nil { t.Fatal(err) } if loaded.Credits != 90 { t.Fatalf("credits after forfeited hand = %d, want 90 (disconnect forfeits)", loaded.Credits) } }