diff options
Diffstat (limited to 'internal/player/hardening_max_inventory_slots_test.go')
| -rw-r--r-- | internal/player/hardening_max_inventory_slots_test.go | 86 |
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) + } +} |
