package game // Shutdown-admission regression: once shutdown has begun, session input must // never mutate game state — not during the drain window, and especially not // after the owner has stopped (the transports keep draining until they // close, so an accepted line would execute concurrently with the shutdown // sequence or after the final-save barrier). import ( "testing" "thehouseoficarus/internal/net" ) func TestShutdownRejectsSessionInput(t *testing.T) { h := newHarness(t) h.seedAccount("alice") h.seedCharacter("alice", "alice") sess, _ := h.connect("alice") sess.SetState(net.StateGame) h.g.setShuttingDown() h.g.stopOwner() // The network listeners are closed only AFTER the owner stops, so // connections can still submit lines through the production entry // point. Every one of them must be rejected. h.g.HandleSessionOnOwner(sess, "look") // A durable mutation gives an unambiguous admission check: description // input would rewrite the character. sess.SetState(net.StateChangeDesc) h.g.HandleSessionOnOwner(sess, "Input after final save") if sess.Player.Description == "Input after final save" { t.Error("session input still mutates character after shutdown/owner stop") } } func TestShutdownRejectsInputDuringDrain(t *testing.T) { h := newHarness(t) h.seedAccount("alice") h.seedCharacter("alice", "alice") sess, _ := h.connect("alice") sess.SetState(net.StateGame) h.g.setShuttingDown() // During the drain window the owner is still alive, but new input is // already rejected: only work accepted before shutdown executes. h.g.HandleSessionOnOwner(sess, "look") sess.SetState(net.StateChangeDesc) h.g.HandleSessionOnOwner(sess, "Input during drain") if sess.Player.Description == "Input during drain" { t.Error("session input accepted after shutdown began") } h.barrier() }