1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
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)
}
}
|