aboutsummaryrefslogtreecommitdiff
path: root/internal/net/session_writer_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/net/session_writer_test.go')
-rw-r--r--internal/net/session_writer_test.go67
1 files changed, 67 insertions, 0 deletions
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")
+ }
+}