aboutsummaryrefslogtreecommitdiff
path: root/internal/player/hardening_max_inventory_slots_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/player/hardening_max_inventory_slots_test.go')
-rw-r--r--internal/player/hardening_max_inventory_slots_test.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/internal/player/hardening_max_inventory_slots_test.go b/internal/player/hardening_max_inventory_slots_test.go
new file mode 100644
index 0000000..8cd9c37
--- /dev/null
+++ b/internal/player/hardening_max_inventory_slots_test.go
@@ -0,0 +1,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)
+ }
+}