package control import ( "errors" "net" "strings" "testing" "time" ) func TestRoundTrip(t *testing.T) { server, client := net.Pipe() go serveConn(server, func(cmd string) string { if cmd != CmdUpdateNow { return "ERR unknown command: " + cmd } return "OK v0.2.2 is up to date" }) reply, err := readReply(client, CmdUpdateNow) if err != nil { t.Fatal(err) } if reply != "OK v0.2.2 is up to date" { t.Fatalf("reply = %q", reply) } server2, client2 := net.Pipe() go serveConn(server2, func(cmd string) string { return "ERR unknown command: " + cmd }) reply, err = readReply(client2, "bogus") if err != nil { t.Fatal(err) } if !strings.HasPrefix(reply, "ERR ") { t.Fatalf("reply = %q, want ERR prefix", reply) } } func TestEmptyReplyIsUnavailable(t *testing.T) { server, client := net.Pipe() go serveConn(server, func(cmd string) string { server.Close() // hang up without answering return "" }) if _, err := readReply(client, CmdUpdateNow); !errors.Is(err, ErrUnavailable) { t.Fatalf("err = %v, want ErrUnavailable", err) } } func TestServeCap(t *testing.T) { for i := 0; i < maxConns; i++ { connSem <- struct{}{} } defer func() { for i := 0; i < maxConns; i++ { <-connSem } }() server, client := net.Pipe() defer server.Close() defer client.Close() if serve(server, func(string) string { return "OK" }) { t.Fatal("serve accepted a connection beyond the cap") } } // forcePipe records ForceClose calls for the watchdog test. type forcePipe struct { net.Conn forced chan struct{} } func (c forcePipe) ForceClose() error { err := c.Conn.Close() close(c.forced) return err } func TestConnWatchdog(t *testing.T) { old := connTimeout connTimeout = 50 * time.Millisecond defer func() { connTimeout = old }() server, client := net.Pipe() defer client.Close() fc := forcePipe{Conn: server, forced: make(chan struct{})} done := make(chan struct{}) go func() { serveConn(fc, func(string) string { return "OK" }) close(done) }() // The client never sends anything; the watchdog must tear the // connection down instead of blocking forever. select { case <-fc.forced: case <-time.After(5 * time.Second): t.Fatal("watchdog did not force-close the stalled connection") } select { case <-done: case <-time.After(5 * time.Second): t.Fatal("serveConn still blocked after the force close") } }