aboutsummaryrefslogtreecommitdiff
path: root/internal/game/migrate_rooms_test.go
blob: 57eb46a5f909917e6bc68fff82da8451321e6161 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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")
	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")
	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")
	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)
	}
}