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
|
package game
// Regression (P0): a purged account must leave every one of its sessions in a
// safe protocol state. A queued-mode Close only drops the transport after the
// writer drains its outbox, so input already buffered in the connection can
// still be dispatched to the owner after the purge; a session left in the
// character menu with a nil account then dereferences sess.Account and kills
// the owner goroutine (and with it every session and all unsaved state).
import (
"testing"
"thehouseoficarus/internal/net"
)
func TestPurgeResetsEveryAccountSession(t *testing.T) {
h := newHarness(t)
h.seedAccount("alice")
h.seedCharacter("alice", "alice")
other := h.connectMenu("alice")
purger := h.connectMenu("alice")
h.g.RunOnOwner(func() { h.g.handlePurgeAccount(purger, "PURGE alice") })
for name, sess := range map[string]*net.Session{"other": other, "purger": purger} {
if got := sess.State(); got != net.StateAccountName {
t.Errorf("%s session state = %v, want StateAccountName", name, got)
}
if sess.Account != nil {
t.Errorf("%s session retained the purged account: %+v", name, sess.Account)
}
}
// Input already buffered in either transport is still routed to the owner:
// it must land on the account-name prompt instead of faulting.
before := h.g.owner.panics.Load()
h.g.RunOnOwner(func() {
h.g.handleMenu(other, "c")
h.g.handleMenu(other, "l")
h.g.handlePurgeAccount(purger, "PURGE alice")
h.g.handleNewCharName(purger, "bob")
})
if got := h.g.owner.panics.Load(); got != before {
t.Fatalf("post-purge input panicked the owner (panic count %d -> %d)", before, got)
}
if got := other.State(); got != net.StateAccountName {
t.Errorf("state = %v after post-purge input, want StateAccountName", got)
}
}
|