aboutsummaryrefslogtreecommitdiff
path: root/internal/game/migrate_rooms_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/migrate_rooms_test.go')
-rw-r--r--internal/game/migrate_rooms_test.go115
1 files changed, 115 insertions, 0 deletions
diff --git a/internal/game/migrate_rooms_test.go b/internal/game/migrate_rooms_test.go
new file mode 100644
index 0000000..eb4a44c
--- /dev/null
+++ b/internal/game/migrate_rooms_test.go
@@ -0,0 +1,115 @@
+package game
+
+// Runtime room-ID migration regressions: Hub buckets, casino room keys,
+// world room-keyed state, mob instances, and player state all move
+// together through Game.MigrateRoomIDs (owner-executed).
+
+import (
+ "testing"
+
+ "thehouseoficarus/internal/casino"
+)
+
+// TestMigrateRoomIDsMovesEverythingTogether: a live player in room 2, a
+// casino machine in room 2, and seeded world state all migrate to room 4
+// in one owner task; nothing stays attached to room 2.
+func TestMigrateRoomIDsMovesEverythingTogether(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice", "pw", false)
+ p := h.seedCharacter("alice", "alice")
+ sess, _ := h.connect("alice")
+ p = sess.Player
+
+ h.g.RunOnOwner(func() {
+ p.RoomID = 2
+ h.hub.EnterRoom(sess, 2)
+ // A machine in room 2 with a committed stake.
+ h.g.Casino.JoinMachine(2, casino.MachineConfig{ID: "slots", Game: casino.GameSlots, MinBet: 1}, "alice")
+ })
+
+ // Migrate 2 -> 4 through the full runtime entry point.
+ h.g.MigrateRoomIDs(map[int]int{2: 4})
+ h.barrier()
+
+ h.g.RunOnOwner(func() {
+ if p.RoomID != 4 {
+ t.Fatalf("player room = %d, want 4", p.RoomID)
+ }
+ if room := h.hub.PlayersInRoom(4); len(room) != 1 || room[0] != sess {
+ t.Fatalf("hub bucket for room 4 = %v, want [alice]", len(room))
+ }
+ if room := h.hub.PlayersInRoom(2); len(room) != 0 {
+ t.Fatalf("stale hub bucket for room 2: %d players", len(room))
+ }
+ if _, playing := h.g.Casino.Participation("alice"); !playing {
+ t.Fatal("casino participation lost during migration")
+ }
+ // The machine's events must route to room 4 now: emit one and
+ // check the room target.
+ machine, ok := h.g.Casino.MachineFor("alice")
+ if !ok {
+ t.Fatal("machine lost during migration")
+ }
+ if machine.RoomID != 4 {
+ t.Fatalf("machine room = %d, want 4", machine.RoomID)
+ }
+ })
+}
+
+// TestMigrateRoomIDsRoundTrip preserves participation both directions.
+func TestMigrateRoomIDsRoundTrip(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice", "pw", false)
+ h.seedCharacter("alice", "alice")
+ sess, _ := h.connect("alice")
+ p := sess.Player
+
+ h.g.RunOnOwner(func() {
+ p.RoomID = 3
+ h.hub.EnterRoom(sess, 3)
+ })
+ h.g.MigrateRoomIDs(map[int]int{3: 9})
+ h.g.RunOnOwner(func() {
+ if p.RoomID != 9 {
+ t.Fatalf("forward migration: player room = %d, want 9", p.RoomID)
+ }
+ })
+ h.g.MigrateRoomIDs(map[int]int{9: 3})
+ h.g.RunOnOwner(func() {
+ if p.RoomID != 3 {
+ t.Fatalf("inverse migration: player room = %d, want 3", p.RoomID)
+ }
+ if room := h.hub.PlayersInRoom(3); len(room) != 1 {
+ t.Fatalf("inverse migration: hub bucket room 3 = %d players", len(room))
+ }
+ })
+}
+
+// TestMigrateRoomIDsPersistsOnlinePlayer: the migrated player state is
+// saved to disk (RoomID rewritten in the character file).
+func TestMigrateRoomIDsPersistsOnlinePlayer(t *testing.T) {
+ h := newHarness(t)
+ h.seedAccount("alice", "pw", false)
+ h.seedCharacter("alice", "alice")
+ sess, _ := h.connect("alice")
+ p := sess.Player
+
+ h.g.RunOnOwner(func() {
+ p.RoomID = 2
+ p.Flags["hidden_exit_2_north"] = true
+ h.hub.EnterRoom(sess, 2)
+ })
+ h.g.MigrateRoomIDs(map[int]int{2: 4})
+ h.barrier()
+
+ loaded, err := h.g.AccountStore.LoadCharacter("alice")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if loaded.RoomID != 4 {
+ t.Fatalf("saved room = %d, want 4", loaded.RoomID)
+ }
+ if _, ok := loaded.Flags["hidden_exit_4_north"]; !ok {
+ t.Fatalf("discovered-exit flag not rewritten: %v", loaded.Flags)
+ }
+}