package compatibility import ( "context" "net" "sync" "testing" "time" "github.com/eleksir/pekoe/internal/config" "github.com/eleksir/pekoe/internal/pubsub" "github.com/eleksir/pekoe/internal/server" ) // testServer manages a test server instance for compatibility tests type testServer struct { cfg *config.Config server *server.Server ctx context.Context cancel context.CancelFunc wg sync.WaitGroup addr string } // startTestServer starts a pekoe server on a random available port for testing func startTestServer(t *testing.T) *testServer { t.Helper() // Find an available port port := findAvailablePort(t) cfg := &config.Config{ Bind: "127.0.0.1", Port: port, MaxClients: 100, ReadTimeout: 0, WriteTimeout: 0, MaxMsgBytes: 10 * 1024 * 1024, // 10MB LogLevel: "error", // Suppress logs in tests } cfg.SetDefaults() hub := pubsub.NewHub() srv := server.New(cfg, hub) ctx, cancel := context.WithCancel(context.Background()) ts := &testServer{ cfg: cfg, server: srv, ctx: ctx, cancel: cancel, addr: cfg.Addr(), } // Start server in background ts.wg.Add(1) go func() { defer ts.wg.Done() _ = srv.Run() }() // Wait for server to be ready ts.waitForReady(t) return ts } // waitForReady blocks until the server accepts connections func (ts *testServer) waitForReady(t *testing.T) { t.Helper() deadline := time.Now().Add(5 * time.Second) for time.Now().Before(deadline) { conn, err := net.DialTimeout("tcp", ts.addr, 100*time.Millisecond) if err == nil { _ = conn.Close() return } time.Sleep(10 * time.Millisecond) } t.Fatalf("Server at %s not ready after 5 seconds", ts.addr) } // Addr returns the server address for client connections func (ts *testServer) Addr() string { return ts.addr } // Close shuts down the test server func (ts *testServer) Close() { ts.cancel() // Give time for graceful shutdown time.Sleep(100 * time.Millisecond) } // findAvailablePort finds an available TCP port for testing func findAvailablePort(t *testing.T) int { t.Helper() listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("Failed to find available port: %v", err) } port := listener.Addr().(*net.TCPAddr).Port _ = listener.Close() return port } // waitForMessage waits for a message on a channel with timeout func waitForMessage[T any](ch <-chan T, timeout time.Duration) (T, bool) { select { case msg := <-ch: return msg, true case <-time.After(timeout): var zero T return zero, false } }