diff options
Diffstat (limited to 'internal/game/payout_evidence_regression_test.go')
| -rw-r--r-- | internal/game/payout_evidence_regression_test.go | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/internal/game/payout_evidence_regression_test.go b/internal/game/payout_evidence_regression_test.go new file mode 100644 index 0000000..b58bf9d --- /dev/null +++ b/internal/game/payout_evidence_regression_test.go @@ -0,0 +1,76 @@ +package game + +// Regression: when the economy journal is unavailable, a payout paid +// through the character file must record the round's stake-resolution +// evidence in that same file. Otherwise a crash before the games' own +// ResolveStakes call leaves the wager records open with checkpoint +// balances that no longer match, and recovery refunds chip stakes the +// paid settlement had already consumed — duplicating the round's value. + +import ( + "testing" + + "thehouseoficarus/internal/player" +) + +func TestUnjournalablePayoutCrashDoesNotRefundChips(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice") + p := h.seedCharacter("alice", "alice") + h.g.RunOnOwner(func() { + p.Credits = 100 + p.SetInvSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 10}) + w := casinoWallet{g: h.g, p: p} + if _, err := w.Wager(10); err != nil { + t.Error(err) + return + } + path := h.g.Economy.path + h.g.Economy.path = h.dir // persistent append failure + if err := w.PayCredits(20); err != nil { + t.Errorf("fallback payout failed: %v", err) + return + } + h.g.Economy.path = path + // Process loss BEFORE the game's own ResolveStakes call: the + // fallback payout itself must be the durable resolution. + h.g.recoverEconomy() + loaded, err := h.g.AccountStore.LoadCharacter("alice") + if err != nil { + t.Error(err) + return + } + if loaded.Credits != 120 || countChips(loaded) != 0 { + t.Errorf("recovered credits=%d chips=%d; want 120 credits and no chips (the paid settlement consumed the stake)", loaded.Credits, countChips(loaded)) + } + if open := h.g.Economy.mustLoadOpen(t); len(open) != 0 { + t.Errorf("records left open after recovery: %+v", open) + } + }) +} + +// The stakes-open count is a proxy for "recovery did not silently refund": +// with the fix the wager record settles through the file-side evidence. +func TestUnjournalablePayoutEvidenceRecorded(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice") + p := h.seedCharacter("alice", "alice") + h.g.RunOnOwner(func() { + p.Credits = 100 + w := casinoWallet{g: h.g, p: p} + if _, err := w.Wager(10); err != nil { + t.Error(err) + return + } + path := h.g.Economy.path + h.g.Economy.path = h.dir + if err := w.PayCredits(20); err != nil { + t.Errorf("fallback payout failed: %v", err) + return + } + h.g.Economy.path = path + if len(p.ResolvedStakes) == 0 { + t.Error("fallback payout recorded no stake-resolution evidence; a crash before ResolveStakes would double-refund chips") + } + }) +} |
