aboutsummaryrefslogtreecommitdiff
path: root/internal/game/payout_evidence_regression_test.go
blob: b58bf9d0b1070c1b0659f7288f2053163f69e71d (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
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")
		}
	})
}