From fcb806c3445ce56f2e57e208de773b4dac957c8d Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 15 Sep 2026 20:35:23 -0400 Subject: fix: owner re-entrancy deadlock, telnet write queue, recovery/refund money safety, admin path traversal --- internal/net/session_writer_test.go | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 internal/net/session_writer_test.go (limited to 'internal/net/session_writer_test.go') diff --git a/internal/net/session_writer_test.go b/internal/net/session_writer_test.go new file mode 100644 index 0000000..94de4ba --- /dev/null +++ b/internal/net/session_writer_test.go @@ -0,0 +1,67 @@ +package net + +import ( + stdnet "net" + "testing" + "time" + + "thehouseoficarus/internal/config" +) + +// TestEnqueueAfterCloseDoesNotPanic: a broadcast racing Close() must drop +// the message, never panic on a send to the closed outbound queue. +func TestEnqueueAfterCloseDoesNotPanic(t *testing.T) { + conn := &testConn{} + sess := &Session{Conn: conn} + sess.StartWriter() + sess.Close() + + defer func() { + if r := recover(); r != nil { + t.Fatalf("write after Close panicked: %v", r) + } + // Wait for the writer goroutine to drain and exit cleanly. + select { + case <-sess.writeDone: + case <-time.After(time.Second): + t.Fatal("writer goroutine never exited after close") + } + }() + for i := 0; i < 3; i++ { + sess.WriteLine("late broadcast") + } +} + +// TestTelnetSessionUsesQueuedWriter: production telnet sessions must start +// the queued writer like the websocket transport, so slow clients get +// per-message write deadlines and can never block game code. +func TestTelnetSessionUsesQueuedWriter(t *testing.T) { + cfg := config.Default() + cfg.Telnet.Enabled = true + cfg.Telnet.Port = 0 + s, err := NewServer(cfg) + if err != nil { + t.Fatal(err) + } + defer s.Shutdown() + + observed := make(chan bool, 1) + s.handler = func(sess *Session, _ string) { observed <- sess.outbox != nil } + go s.serveTelnet() + + conn, err := stdnet.Dial("tcp", s.telnetLn.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + conn.Write([]byte("hello\n")) + + select { + case queued := <-observed: + if !queued { + t.Fatal("telnet session has no outbound queue; writes are synchronous and can stall the world") + } + case <-time.After(time.Second): + t.Fatal("session handler was not invoked") + } +} -- cgit v1.2.3