aboutsummaryrefslogtreecommitdiff
path: root/internal/game/shutdown_admission_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/shutdown_admission_test.go')
-rw-r--r--internal/game/shutdown_admission_test.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/internal/game/shutdown_admission_test.go b/internal/game/shutdown_admission_test.go
new file mode 100644
index 0000000..6aa4be5
--- /dev/null
+++ b/internal/game/shutdown_admission_test.go
@@ -0,0 +1,55 @@
+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", "pw", false)
+ h.seedCharacter("alice", "alice")
+ sess, _ := h.connect("alice")
+ sess.State = 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.State = 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", "pw", false)
+ h.seedCharacter("alice", "alice")
+ sess, _ := h.connect("alice")
+ sess.State = 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.State = 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()
+}