// Package testutil provides an in-memory WebSocket connection so the engine's // concurrency tests (#26) can run without real sockets. FakeConn satisfies the // ws.Conn interface structurally. package testutil import ( "io" "sync" "time" ) // textMessage mirrors gorilla/websocket.TextMessage (1) without importing it, so // this helper stays dependency-light. const textMessage = 1 // FakeConn is a thread-safe, in-memory connection. Outbound frames are captured // in Writes(); inbound frames can be injected with Push() and are returned by // ReadMessage in order until the connection is closed. type FakeConn struct { mu sync.Mutex writes [][]byte incoming chan []byte closed bool pong func(string) error } // NewFakeConn returns a ready connection. func NewFakeConn() *FakeConn { return &FakeConn{incoming: make(chan []byte, 1024)} } // ReadMessage returns the next injected frame, blocking until one is available or // the connection is closed (then io.EOF). func (f *FakeConn) ReadMessage() (int, []byte, error) { b, ok := <-f.incoming if !ok { return 0, nil, io.EOF } return textMessage, b, nil } // Push injects an inbound frame for ReadMessage to return. func (f *FakeConn) Push(frame []byte) { f.mu.Lock() defer f.mu.Unlock() if f.closed { return } f.incoming <- frame } // WriteMessage captures an outbound frame. func (f *FakeConn) WriteMessage(_ int, data []byte) error { f.mu.Lock() defer f.mu.Unlock() if f.closed { return io.ErrClosedPipe } cp := make([]byte, len(data)) copy(cp, data) f.writes = append(f.writes, cp) return nil } // Writes returns a copy of all captured outbound frames. func (f *FakeConn) Writes() [][]byte { f.mu.Lock() defer f.mu.Unlock() out := make([][]byte, len(f.writes)) copy(out, f.writes) return out } // WriteCount returns how many frames have been written. func (f *FakeConn) WriteCount() int { f.mu.Lock() defer f.mu.Unlock() return len(f.writes) } func (f *FakeConn) WriteControl(int, []byte, time.Time) error { return nil } func (f *FakeConn) SetReadLimit(int64) {} func (f *FakeConn) SetReadDeadline(time.Time) error { return nil } func (f *FakeConn) SetWriteDeadline(time.Time) error { return nil } func (f *FakeConn) SetPongHandler(h func(string) error) { f.pong = h } // Pong invokes the registered pong handler (used by heartbeat tests). func (f *FakeConn) Pong(appData string) error { if f.pong != nil { return f.pong(appData) } return nil } // Close makes ReadMessage return EOF and rejects further writes. Idempotent. func (f *FakeConn) Close() error { f.mu.Lock() defer f.mu.Unlock() if f.closed { return nil } f.closed = true close(f.incoming) return nil }