diff options
| author | historia <[not public]> | 2026-09-15 13:56:10 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-15 13:56:48 -0400 |
| commit | b3568f03e4103dd46db2ccc680fbab0a6454d604 (patch) | |
| tree | db3f6bb4f3d7c1deefbcd50de0f5022756d6b9ae /internal/game/bank_transport_regression_test.go | |
| parent | f4ea40c4b3589104dfaced3481ba19c5735889ae (diff) | |
| download | thehouseoficarus-b3568f03e4103dd46db2ccc680fbab0a6454d604.tar.gz | |
fix: telnet buffer reuse, bank stack handling,
transport casino gate, and admin cache invalidation
Diffstat (limited to 'internal/game/bank_transport_regression_test.go')
| -rw-r--r-- | internal/game/bank_transport_regression_test.go | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/internal/game/bank_transport_regression_test.go b/internal/game/bank_transport_regression_test.go new file mode 100644 index 0000000..529df84 --- /dev/null +++ b/internal/game/bank_transport_regression_test.go @@ -0,0 +1,114 @@ +package game + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/casino" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/item" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" +) + +// regressionGame builds a minimal Game with real stores over a temp data dir. +func regressionGame(t *testing.T) (*Game, *net.Session) { + t.Helper() + dir := t.TempDir() + for _, sub := range []string{"items", "rooms"} { + if err := os.MkdirAll(filepath.Join(dir, sub), 0o755); err != nil { + t.Fatal(err) + } + } + if err := os.WriteFile(filepath.Join(dir, "items", "chips.yaml"), []byte("name: chips\nstackable: true\n"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "rooms", "1.yaml"), []byte("name: test\n"), 0o644); err != nil { + t.Fatal(err) + } + g := &Game{ + Deps: Deps{ + World: world.New(dir), + ItemStore: item.NewItemStore(dir), + AccountStore: player.NewAccountStore(dir), + }, + Casino: casino.NewManager(1), + Combat: combat.NewTracker(), + } + sess := &net.Session{Player: player.New("alice"), Conn: &casinoTestConn{}} + sess.Player.RoomID = 1 + return g, sess +} + +// Regression: a stackable withdrawal must succeed via stack merging even +// when the inventory has zero free slots. +func TestBankWithdrawMergesStackIntoFullInventory(t *testing.T) { + g, sess := regressionGame(t) + for i := 0; i < 28; i++ { + sess.Player.SetInvSlot(i, &player.InventorySlot{ItemID: "filler", Quantity: 1}) + } + sess.Player.SetInvSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 10}) + sess.Player.SetBankSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 5}) + + g.doBankWithdraw(sess, "1") + + inv := sess.Player.InvSlot(0) + if inv == nil || inv.Quantity != 15 { + t.Fatalf("stack withdrawal blocked by a full inventory: inventory=%+v", inv) + } + if sess.Player.BankSlot(0) != nil { + t.Fatal("bank slot was not cleared after the withdrawal") + } +} + +// Regression: stacks of the same item but different quality must not merge, +// which would silently destroy the withdrawn stack's properties. +func TestBankWithdrawKeepsIncompatibleQualitySeparate(t *testing.T) { + g, sess := regressionGame(t) + sess.Player.SetInvSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 10, Quality: 3, MaxQuality: 10}) + sess.Player.SetBankSlot(0, &player.InventorySlot{ItemID: "chips", Quantity: 5, Quality: 8, MaxQuality: 10}) + + g.doBankWithdraw(sess, "1") + + first, second := sess.Player.InvSlot(0), sess.Player.InvSlot(1) + if first == nil || first.Quantity != 10 || first.Quality != 3 { + t.Fatalf("existing stack was disturbed: %+v", first) + } + if second == nil || second.Quantity != 5 || second.Quality != 8 || second.MaxQuality != 10 { + t.Fatalf("incompatible stack was merged or metadata lost: first=%+v second=%+v", first, second) + } +} + +// Regression: sequenced transport modules (like transport_home) bypass +// triggerTransport entirely; the casino gate must also apply when the module +// is started. +func TestSequencedTransportBlockedWhilePlaying(t *testing.T) { + g, sess := regressionGame(t) + + oldMods, oldIndex := AllMods, modByID + t.Cleanup(func() { AllMods, modByID = oldMods, oldIndex }) + mod := &ModDef{ + ID: "transport_home", Name: "transport home", Category: ModTransport, + Level: 1, Destination: 2, + Sequence: []behavior.ModTriggerStep{{Message: "wait", Delay: 1}}, + } + AllMods = []*ModDef{mod} + modByID = map[string]*ModDef{mod.ID: mod} + + g.Casino.JoinMachine(1, casino.MachineConfig{ID: "slots", Game: casino.GameSlots}, sess.Player.Name) + conn := sess.Conn.(*casinoTestConn) + conn.data = nil + + g.doTrigger(sess, "transport_home") + + if sess.Player.Action != nil { + t.Fatalf("transport sequence scheduled despite casino participation: %+v", sess.Player.Action) + } + if !strings.Contains(string(conn.data), "playing a game") { + t.Fatalf("missing casino-gate message: %q", string(conn.data)) + } +} |
