package casino import "testing" // recordingWallet records the stake IDs passed to PayCredits so a winning // spin's atomicity can be asserted directly. type recordingWallet struct { testWallet payStakes []string } func (w *recordingWallet) PayCredits(amount int, stakeIDs ...string) error { w.payStakes = append(w.payStakes, stakeIDs...) return w.testWallet.PayCredits(amount, stakeIDs...) } // TestWinningSpinConsumesStakeInPayoutSave: a winning paid spin must pass its // triggering stake to the SAME PayCredits call, so the credit and the stake // consumption are one durable save. Resolving the stake in a separate later // call leaves a crash window in which the payout is durable but the stake is // still pending — startup would then refund it on top of the win. func TestWinningSpinConsumesStakeInPayoutSave(t *testing.T) { m := newMachine(1, MachineConfig{ID: "slots", Game: GameSlots}, newTestRand()) w := &recordingWallet{testWallet: testWallet{credits: 100}} m.join("alice") m.beginSpinLocked(10, w, "", "") wagerID := m.stakeID m.spin = &SlotSpin{Payout: 25} m.finishSpin() if len(w.payStakes) != 1 || w.payStakes[0] != wagerID { t.Fatalf("winning spin passed stakes %v to PayCredits; want [%q] (one atomic payout+resolve)", w.payStakes, wagerID) } if len(w.open) != 0 { t.Fatalf("winning spin left %d stake(s) pending: %+v", len(w.open), w.open) } }