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") } }