aboutsummaryrefslogtreecommitdiff
path: root/internal/casino/manager_test.go
blob: b037efafff5a1a3d947bfbf3cf3b20421422433c (plain)
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package casino

import "testing"

type testWallet struct {
	chips, credits, paid int
	open                 []Wager
}

func (w *testWallet) Wager(amount int) (Wager, error) {
	if w.chips+w.credits < amount {
		return Wager{}, ErrInsufficientFunds
	}
	chips := amount
	if chips > w.chips {
		chips = w.chips
	}
	w.chips -= chips
	credits := amount - chips
	w.credits -= credits
	wager := Wager{Total: amount, Chips: chips, Credits: credits, ID: string(rune('a' + len(w.open)))}
	w.open = append(w.open, wager)
	return wager, nil
}

func (w *testWallet) PayCredits(amount int) error {
	w.paid += amount
	return nil
}

// PayStakeCover routes through PayCredits so a wallet rejecting payouts
// (failingPayoutWallet) rejects stake-covering settlements identically.
func (w *testWallet) PayStakeCover(amount int, _ []string) error {
	return w.PayCredits(amount)
}

// ResolveStake settles one outstanding stake by handle, mirroring the
// production wallet.
func (w *testWallet) ResolveStake(stakeID string) {
	kept := w.open[:0]
	for _, wager := range w.open {
		if wager.ID != stakeID {
			kept = append(kept, wager)
		}
	}
	w.open = kept
}

// ResolveStakes mirrors the production wallet: every outstanding stake is
// settled (no longer refundable).
func (w *testWallet) ResolveStakes() { w.open = nil }

// RefundOutstanding returns every outstanding stake to the balances and
// resolves the stakes, mirroring the production refund path: credits are
// paid through PayCredits (recorded in paid), chips return as chips.
func (w *testWallet) RefundOutstanding() (chips, credits int, err error) {
	for _, wager := range w.open {
		chips += wager.Chips
		credits += wager.Credits
	}
	w.open = nil
	w.chips += chips
	if credits > 0 {
		w.paid += credits
	}
	return chips, credits, nil
}

func TestMixedWagerUsesChipsFirst(t *testing.T) {
	w := &testWallet{chips: 7, credits: 50}
	wager, err := w.Wager(12)
	if err != nil || wager.Chips != 7 || wager.Credits != 5 || w.chips != 0 || w.credits != 45 {
		t.Fatalf("unexpected wager: %+v wallet=%+v", wager, w)
	}
}

func TestJoinUnknownTableGameRejected(t *testing.T) {
	m := NewManager(1)
	events := m.Join(1, TableConfig{ID: "slots", Game: GameSlots}, "alice")
	if len(events) != 1 || !contains(events[0].Message, "not available") {
		t.Fatalf("joining an unsupported table game: %+v", events)
	}
	if m.Table(1, "slots") != nil {
		t.Fatal("unsupported game was cached as a table")
	}
}

func TestMachineAutobetCountdownAndStop(t *testing.T) {
	m := NewManager(1)
	cfg := MachineConfig{ID: "slots", Game: GameSlots, SpinTicks: 1}
	m.JoinMachine(1, cfg, "alice")
	w := &testWallet{credits: 100}
	if events := m.SetMachineBet(1, "slots", "alice", 10, w); len(events) == 0 {
		t.Fatalf("setting a bet did not start a spin: %+v", events)
	}
	for m.MachineBusy("alice") {
		m.Tick()
	}
	if events := m.EnableMachineAutoBet(1, "slots", "alice", w); len(events) != 0 {
		t.Fatalf("enabling autobet emitted events before countdown: %+v", events)
	}
	for i, want := range []string{"Autobet enabled!", `Type "stop" to stop playing`, "Betting... 10 credits."} {
		events := m.Tick()
		if len(events) == 0 || eventText(events) != want {
			t.Fatalf("tick %d message=%q events=%+v, want %q", i+1, eventText(events), events, want)
		}
	}
	if !m.MachineBusy("alice") {
		t.Fatal("autobet did not start a spin after countdown")
	}
	if events, stopped := m.StopMachineAutoBet(1, "slots", "alice"); !stopped || len(events) != 1 {
		t.Fatalf("stop autobet events=%+v stopped=%v", events, stopped)
	}
	if _, playing := m.MachineFor("alice"); !playing {
		t.Fatal("stopping autobet removed the player from the machine")
	}
}

func eventText(events []Event) string {
	for _, event := range events {
		if event.PrivateMessage != "" {
			return event.PrivateMessage
		}
		if event.Message != "" {
			return event.Message
		}
	}
	return ""
}