aboutsummaryrefslogtreecommitdiff
path: root/internal/player/hardening_max_inventory_slots_test.go
blob: 8cd9c37c336be144992af3807049b2b5eb030660 (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
package player

// Regression coverage for the inventory slot accounting helpers: the capacity
// is a named constant, FirstFreeSlot stays inside it, and FreeSlots ignores
// entries a corrupt save can carry that no gameplay path can free.

import "testing"

func TestMaxInventorySlotsMatchesSlotHelpers(t *testing.T) {
	if MaxInventorySlots != 28 {
		t.Fatalf("MaxInventorySlots = %d, want 28 (on-disk capacity)", MaxInventorySlots)
	}
	p := New("alice")
	if got := p.FreeSlots(); got != MaxInventorySlots {
		t.Fatalf("empty inventory free slots = %d, want %d", got, MaxInventorySlots)
	}
	if got := p.FirstFreeSlot(); got != 0 {
		t.Fatalf("empty inventory first free slot = %d, want 0", got)
	}

	// The last valid slot is addressable, and the first free slot walks up to
	// the capacity and only then fails.
	for i := 0; i < MaxInventorySlots-1; i++ {
		p.SetInvSlot(i, &InventorySlot{ItemID: "rock", Quantity: 1})
	}
	if got := p.FirstFreeSlot(); got != MaxInventorySlots-1 {
		t.Fatalf("first free slot = %d, want %d", got, MaxInventorySlots-1)
	}
	if got := p.FreeSlots(); got != 1 {
		t.Fatalf("free slots = %d, want 1", got)
	}
	p.SetInvSlot(MaxInventorySlots-1, &InventorySlot{ItemID: "rock", Quantity: 1})
	if got := p.FirstFreeSlot(); got != -1 {
		t.Fatalf("full inventory first free slot = %d, want -1", got)
	}
	if got := p.FreeSlots(); got != 0 {
		t.Fatalf("full inventory free slots = %d, want 0", got)
	}
}

// TestFreeSlotsIgnoresInvalidEntries: out-of-range keys (a negative one, and
// one past the capacity) and nil-valued entries must not consume capacity or
// drive the count negative, while the valid slots keep counting exactly as
// before.
func TestFreeSlotsIgnoresInvalidEntries(t *testing.T) {
	p := New("alice")
	p.Inventory = map[int]*InventorySlot{
		-1:                    {ItemID: "rock", Quantity: 1},
		MaxInventorySlots:     {ItemID: "rock", Quantity: 1},
		MaxInventorySlots + 5: {ItemID: "rock", Quantity: 1},
		3:                     nil, // an explicitly nil entry frees its slot
		7:                     {ItemID: "rock", Quantity: 1},
	}

	if got := p.FreeSlots(); got != MaxInventorySlots-1 {
		t.Fatalf("free slots with invalid entries = %d, want %d", got, MaxInventorySlots-1)
	}
	if got := p.FirstFreeSlot(); got != 0 {
		t.Fatalf("first free slot = %d, want 0", got)
	}

	// Only valid, non-nil slots are occupied: with every in-range slot filled
	// the invalid keys must not make the count negative.
	p.Inventory = make(map[int]*InventorySlot, MaxInventorySlots+2)
	for i := 0; i < MaxInventorySlots; i++ {
		p.Inventory[i] = &InventorySlot{ItemID: "rock", Quantity: 1}
	}
	p.Inventory[-7] = &InventorySlot{ItemID: "rock", Quantity: 1}
	p.Inventory[99] = &InventorySlot{ItemID: "rock", Quantity: 1}
	if got := p.FreeSlots(); got != 0 {
		t.Fatalf("free slots of a full inventory with stray keys = %d, want 0", got)
	}
	if got := p.FirstFreeSlot(); got != -1 {
		t.Fatalf("first free slot of a full inventory with stray keys = %d, want -1", got)
	}
}

func TestFreeSlotsCountsValidSlotsLikeLength(t *testing.T) {
	p := New("alice")
	for _, i := range []int{0, 5, 27} {
		p.SetInvSlot(i, &InventorySlot{ItemID: "rock", Quantity: 1})
	}
	if got, want := p.FreeSlots(), MaxInventorySlots-3; got != want {
		t.Fatalf("free slots = %d, want %d", got, want)
	}
}