diff options
| author | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-18 05:31:09 -0400 |
| commit | 153acb7f644d755c45533b7c5311e3ffa47e8973 (patch) | |
| tree | 7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/player/hardening_store_sanitize_test.go | |
| parent | 87d08f0151e643951769e323995f1c5ed89ee200 (diff) | |
| download | thehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz | |
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/player/hardening_store_sanitize_test.go')
| -rw-r--r-- | internal/player/hardening_store_sanitize_test.go | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/internal/player/hardening_store_sanitize_test.go b/internal/player/hardening_store_sanitize_test.go new file mode 100644 index 0000000..9416856 --- /dev/null +++ b/internal/player/hardening_store_sanitize_test.go @@ -0,0 +1,114 @@ +package player + +// Regression: LoadCharacter must sanitize the inventory/bank slot maps. A +// hand-edited or corrupt save with out-of-range keys, nil entries, or +// non-positive quantities used to leave items no gameplay loop could reach +// while still consuming capacity, and could drive FreeSlots negative. + +import ( + "os" + "testing" +) + +func TestLoadCharacterSanitizesSlotMaps(t *testing.T) { + s := testAccountStore(t) + content := `name: Corrupt +room_id: 1 +inventory: + 0: + item_id: rope + quantity: 2 + 3: null + 5: + item_id: ghost + quantity: 0 + 7: + item_id: negative + quantity: -4 + 99: + item_id: out_of_range + quantity: 1 +bank: + 0: + item_id: coins + quantity: 100 + 30: + item_id: far_slot + quantity: 1 + -2: + item_id: bogus + quantity: 3 + 4: null + 6: + item_id: empty + quantity: 0 +` + if err := os.WriteFile(s.CharPath("Corrupt"), []byte(content), 0600); err != nil { + t.Fatal(err) + } + + p, err := s.LoadCharacter("Corrupt") + if err != nil { + t.Fatal(err) + } + + if len(p.Inventory) != 1 { + t.Fatalf("inventory = %v, want only the valid slot 0", p.Inventory) + } + slot := p.Inventory[0] + if slot == nil || slot.ItemID != "rope" || slot.Quantity != 2 { + t.Errorf("valid slot 0 changed: %+v", slot) + } + if got := p.FreeSlots(); got != 27 { + t.Errorf("FreeSlots() = %d, want 27", got) + } + if got := p.FreeSlots(); got < 0 { + t.Errorf("FreeSlots() went negative: %d", got) + } + + if len(p.Bank) != 2 { + t.Fatalf("bank = %v, want only slots 0 and 30", p.Bank) + } + if bs := p.Bank[0]; bs == nil || bs.ItemID != "coins" || bs.Quantity != 100 { + t.Errorf("valid bank slot 0 changed: %+v", bs) + } + // Bank slots past the inventory's 28 are valid: the bank grows unbounded. + if bs := p.Bank[30]; bs == nil || bs.ItemID != "far_slot" { + t.Errorf("valid bank slot 30 dropped: %+v", bs) + } + if _, ok := p.Bank[-2]; ok { + t.Error("negative bank slot survived sanitization") + } +} + +func TestSaveLoadKeepsValidSlots(t *testing.T) { + s := testAccountStore(t) + p := New("Alice") + p.SetInvSlot(0, &InventorySlot{ItemID: "rope", Quantity: 2, Quality: 3, MaxQuality: 5}) + p.SetInvSlot(27, &InventorySlot{ItemID: "torch", Quantity: 1}) + p.SetBankSlot(0, &InventorySlot{ItemID: "coins", Quantity: 100}) + p.SetBankSlot(30, &InventorySlot{ItemID: "spade", Quantity: 1}) + if err := s.SaveCharacter(p); err != nil { + t.Fatal(err) + } + + loaded, err := s.LoadCharacter("Alice") + if err != nil { + t.Fatal(err) + } + if len(loaded.Inventory) != 2 || len(loaded.Bank) != 2 { + t.Fatalf("slots lost on a round trip: inventory=%v bank=%v", loaded.Inventory, loaded.Bank) + } + if got := loaded.Inventory[0]; got == nil || got.ItemID != "rope" || got.Quantity != 2 || got.Quality != 3 || got.MaxQuality != 5 { + t.Errorf("inventory slot 0 changed: %+v", got) + } + if got := loaded.Inventory[27]; got == nil || got.ItemID != "torch" { + t.Errorf("inventory slot 27 changed: %+v", got) + } + if got := loaded.Bank[30]; got == nil || got.ItemID != "spade" { + t.Errorf("bank slot 30 changed: %+v", got) + } + if loaded.FreeSlots() != 26 { + t.Errorf("FreeSlots() = %d, want 26", loaded.FreeSlots()) + } +} |
