aboutsummaryrefslogtreecommitdiff
path: root/internal/game/correct_audit_fixes_test.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-05 20:00:02 -0400
committerhistoria <[not public]>2026-10-05 20:00:02 -0400
commit4bc1daaf259e0c19c5fe4a12ec8bc7f6e5a735c1 (patch)
treef7ea1da2f88f37c44b937aaaf1fd12c722811147 /internal/game/correct_audit_fixes_test.go
parenta5447965e7808d656a5e5d9326a9ca0231148f85 (diff)
downloadthehouseoficarus-4bc1daaf259e0c19c5fe4a12ec8bc7f6e5a735c1.tar.gz
feat: terminal talk action, typed pai gow poker phase
Diffstat (limited to 'internal/game/correct_audit_fixes_test.go')
-rw-r--r--internal/game/correct_audit_fixes_test.go94
1 files changed, 94 insertions, 0 deletions
diff --git a/internal/game/correct_audit_fixes_test.go b/internal/game/correct_audit_fixes_test.go
index c32b1e5..03facc0 100644
--- a/internal/game/correct_audit_fixes_test.go
+++ b/internal/game/correct_audit_fixes_test.go
@@ -166,6 +166,100 @@ func TestGatherYieldsDrop(t *testing.T) {
})
}
+// End-to-end burn: the missing coverage the audit called out. Start a fire
+// with a tinderbox and tick until it catches.
+func TestBurnCreatesFire(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice")
+ p := h.seedCharacter("alice", "alice")
+ writeSeedFile(t, filepath.Join(h.dir, "items", "logs.yaml"),
+ []byte("id: logs\nname: logs\nburn_ticks: 100\nfire_level: 1\nfire_xp: 10\n"))
+ writeSeedFile(t, filepath.Join(h.dir, "items", "tinderbox.yaml"),
+ []byte("id: tinderbox\nname: tinderbox\ntool:\n type: fire\n speed: 1\n"))
+ sess := &net.Session{Conn: newChanConn(), Player: p}
+ sess.SetState(net.StateGame)
+ h.g.RunOnOwner(func() {
+ h.g.ItemStore.Reload(h.dir)
+ p.SetInvSlot(0, &player.InventorySlot{ItemID: "logs", Quantity: 5})
+ p.SetInvSlot(1, &player.InventorySlot{ItemID: "tinderbox", Quantity: 1})
+ h.g.startBurn(sess, p, "logs", false)
+ if p.Action == nil || p.Action.Type != behavior.TypeBurn {
+ t.Fatalf("burn action did not start: %s", sess.Conn.(*chanConn).Bytes())
+ }
+ // Each attempt is two calls (phase 1 then the roll); 200 calls is ~100
+ // attempts at >=5% success, so failure is effectively impossible.
+ for i := 0; i < 200 && p.Action != nil; i++ {
+ h.g.advanceBurn(sess, p)
+ }
+ if p.Action != nil {
+ t.Fatal("burn never completed")
+ }
+ if !h.g.hasFireObject(1) {
+ t.Fatalf("no fire created (output: %s)", sess.Conn.(*chanConn).Bytes())
+ }
+ })
+}
+
+// Regression: a terminal talk node (messages, no options, no goto) must clear
+// the talk action, or it lingers as a stale action that suppresses the prompt
+// and no-ops every tick.
+func TestTerminalTalkNodeClearsAction(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice")
+ p := h.seedCharacter("alice", "alice")
+ writeSeedFile(t, filepath.Join(h.dir, "objects", "statue.yaml"),
+ []byte("id: statue\nname: statue\ntalk:\n nodes:\n start:\n messages: [The statue says nothing more.]\n"))
+ sess := &net.Session{Conn: newChanConn(), Player: p}
+ sess.SetState(net.StateGame)
+ h.g.RunOnOwner(func() {
+ h.g.ObjectStore.Reload(h.dir)
+ obj, err := h.g.ObjectStore.Load("statue")
+ if err != nil {
+ t.Fatal(err)
+ }
+ h.g.startTalk(sess, p, obj)
+ if p.Action != nil {
+ t.Fatalf("terminal talk node left a stale action: %+v", p.Action)
+ }
+ if sess.State() != net.StateGame {
+ t.Fatalf("state = %v, want StateGame", sess.State())
+ }
+ })
+}
+
+// End-to-end object steal: the missing coverage the audit called out.
+func TestObjectStealYieldsLoot(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice")
+ p := h.seedCharacter("alice", "alice")
+ writeSeedFile(t, filepath.Join(h.dir, "objects", "market_stall.yaml"),
+ []byte("id: market_stall\nname: market stall\nsteal:\n level: 1\n xp: 10\n speed: 1\n drops:\n - item_id: stolen_coins\n weight: 100\n"))
+ writeSeedFile(t, filepath.Join(h.dir, "items", "stolen_coins.yaml"),
+ []byte("id: stolen_coins\nname: stolen coins\nstackable: true\n"))
+ sess := &net.Session{Conn: newChanConn(), Player: p}
+ sess.SetState(net.StateGame)
+ h.g.RunOnOwner(func() {
+ h.g.ItemStore.Reload(h.dir)
+ h.g.ObjectStore.Reload(h.dir)
+ h.g.World.EnsureObjectStates(1, []string{"market_stall"})
+ obj, err := h.g.ObjectStore.Load("market_stall")
+ if err != nil {
+ t.Fatal(err)
+ }
+ h.g.startSteal(sess, p, "object", nil, obj)
+ if p.Action == nil || p.Action.Type != behavior.TypeSteal {
+ t.Fatalf("steal action did not start: %s", sess.Conn.(*chanConn).Bytes())
+ }
+ // ~50% success per attempt; 200 attempts makes failure negligible.
+ for i := 0; i < 200 && p.CountItem("stolen_coins") == 0; i++ {
+ h.g.advanceSteal(sess, p)
+ }
+ if got := p.CountItem("stolen_coins"); got == 0 {
+ t.Fatalf("steal yielded no loot (output: %s)", sess.Conn.(*chanConn).Bytes())
+ }
+ })
+}
+
func TestPlayerDataFileNameCanonicalizesPrefixes(t *testing.T) {
for _, in := range []string{
"players/characters/alice.yaml",