cluster/identd/ident: add TestE2E

Change-Id: I8a95fadf19376de2806cb63897b77e370559392f
diff --git a/cluster/identd/ident/e2e_test.go b/cluster/identd/ident/e2e_test.go
new file mode 100644
index 0000000..f8016a0
--- /dev/null
+++ b/cluster/identd/ident/e2e_test.go
@@ -0,0 +1,68 @@
+package ident
+
+import (
+	"context"
+	"errors"
+	"net"
+	"testing"
+)
+
+// TestE2E performs an end-to-end test of the ident client and server,
+// exercising as much functionality as possible.
+func TestE2E(t *testing.T) {
+	lis, err := net.Listen("tcp", "127.0.0.1:0")
+	if err != nil {
+		t.Fatalf("Listen: %v", err)
+	}
+	defer lis.Close()
+
+	isrv := NewServer()
+	isrv.HandleFunc(func(ctx context.Context, w ResponseWriter, r *Request) {
+		switch r.ServerPort {
+		case 1:
+			w.SendError(NoUser)
+		default:
+			w.SendIdent(&IdentResponse{
+				UserID: "q3k",
+			})
+		}
+	})
+	go isrv.Serve(lis)
+	target := lis.Addr().String()
+
+	ctx := context.Background()
+
+	// Full end-to-end test, from high-level client Query call, to server
+	// handler.
+
+	// This call should succeed.
+	resp, err := Query(ctx, target, 123, 234)
+	if err != nil {
+		t.Fatalf("Query: %v", err)
+	}
+	if want, got := "q3k", resp.UserID; want != got {
+		t.Errorf("Resp.UserID: wanted %q, got %q", want, got)
+	}
+
+	// This call should error out and return an error that can be converted
+	// back into ErrorResponse.
+	resp, err = Query(ctx, target, 123, 1)
+	if err == nil {
+		t.Fatalf("Query returned nil error")
+	}
+	identErr := &IdentError{}
+	if errors.As(err, &identErr) {
+		if want, got := NoUser, identErr.Inner; want != got {
+			t.Errorf("Query error is %q, wanted %q", want, got)
+		}
+	} else {
+		t.Errorf("Query error did not match AnyError")
+	}
+	// Test matches against specific errors.
+	if errors.Is(err, &IdentError{UnknownError}) {
+		t.Errorf("Query error should not have matched UnknownError")
+	}
+	if !errors.Is(err, &IdentError{NoUser}) {
+		t.Errorf("Query error should have matcher NoUser")
+	}
+}