diff options
Diffstat (limited to 'internal/game/economy_recovery_regression_test.go')
| -rw-r--r-- | internal/game/economy_recovery_regression_test.go | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/internal/game/economy_recovery_regression_test.go b/internal/game/economy_recovery_regression_test.go index 738dece..12df39b 100644 --- a/internal/game/economy_recovery_regression_test.go +++ b/internal/game/economy_recovery_regression_test.go @@ -282,3 +282,80 @@ func TestRecoveryPreservesAmbiguousPayout(t *testing.T) { t.Fatalf("ambiguous payout must stay open for operator inspection: %+v", open) } } + +// TestRecoveryKeepsUnpaidSiblingsOpenAtCreditCap: two identical open payout +// records where re-applying the second would exceed the economy cap must +// settle only the sibling that was actually paid. The unpaid 20-credit +// liability is never erased: its record stays open for operator inspection. +func TestRecoveryKeepsUnpaidSiblingsOpenAtCreditCap(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice", "pw", false) + p := h.seedCharacter("alice", "alice") + h.g.RunOnOwner(func() { + p.Credits = casino.MaxCredits - 30 + if err := h.g.AccountStore.SaveCharacter(p); err != nil { + t.Error(err) + return + } + for i := 0; i < 2; i++ { + if _, err := h.g.Economy.start("payout", p, 0, 20, p.Credits, p.Credits+20, 0, 0); err != nil { + t.Error(err) + return + } + } + h.g.recoverEconomy() + }) + loaded, err := h.g.AccountStore.LoadCharacter("alice") + if err != nil { + t.Fatal(err) + } + if loaded.Credits != casino.MaxCredits-10 { + t.Fatalf("balance after capped recovery = %d, want %d (one payout applied)", loaded.Credits, casino.MaxCredits-10) + } + if open := h.g.Economy.loadOpen(); len(open) != 1 { + t.Fatalf("unpaid liability must stay open for operator inspection, open records: %+v", open) + } +} + +// TestRecoveryRestoresChipsFromMixedRefundCrash: a mixed chips+credits +// refund whose credit side is resolved by a later payout record but whose +// chip side was never saved must still return the chips. The wager record +// may be settled only once nothing is owed. +func TestRecoveryRestoresChipsFromMixedRefundCrash(t *testing.T) { + h := newHarness(t) + h.seedAccount("alice", "pw", false) + p := h.seedCharacter("alice", "alice") + h.g.RunOnOwner(func() { + p.Credits = 100 + p.SetInvSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 10}) + if err := h.g.AccountStore.SaveCharacter(p); err != nil { + t.Error(err) + return + } + // Durable 20-credit (10 chips + 10 credits) wager: after the debit + // the file holds 90 credits and no chips. + w := casinoWallet{g: h.g, p: p} + if _, err := w.Wager(20); err != nil { + t.Error(err) + return + } + // Crash during RefundOutstanding, after the credit-side payout was + // journaled but before anything beyond the debit was durable. + if _, err := h.g.Economy.start("payout", p, 0, 10, 90, 100, 10, 10); err != nil { + t.Error(err) + return + } + h.g.recoverEconomy() + }) + loaded, err := h.g.AccountStore.LoadCharacter("alice") + if err != nil { + t.Fatal(err) + } + if loaded.Credits != 100 || countChips(loaded) != 10 { + t.Fatalf("mixed refund after recovery = (%d credits, %d chips), want (100, 10); open records: %+v", + loaded.Credits, countChips(loaded), h.g.Economy.loadOpen()) + } + if open := h.g.Economy.loadOpen(); len(open) != 0 { + t.Fatalf("nothing may stay owed after recovery: %+v", open) + } +} |
