From bbef017989f6dab720b85f033479ea044501e7d6 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Mon, 14 Sep 2020 12:57:07 -0500 Subject: [PATCH 1/2] Add a testlogger util package for testing go-logr. Signed-off-by: Matt Moyer --- go.mod | 2 + go.sum | 5 ++ internal/testutil/testlogger/testlogger.go | 61 ++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 internal/testutil/testlogger/testlogger.go diff --git a/go.mod b/go.mod index 246020d6..5d8e7db8 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.14 require ( github.com/davecgh/go-spew v1.1.1 github.com/go-logr/logr v0.2.1 + github.com/go-logr/stdr v0.2.0 + github.com/go-logr/zapr v0.2.0 // indirect github.com/golang/mock v1.4.4 github.com/golangci/golangci-lint v1.31.0 github.com/google/go-cmp v0.5.2 diff --git a/go.sum b/go.sum index fb22470c..570aaaa0 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,10 @@ github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.2.1 h1:fV3MLmabKIZ383XifUjFSwcoGee0v9qgPp8wy5svibE= github.com/go-logr/logr v0.2.1/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/stdr v0.2.0 h1:EuTFw3BCZ6H/+1VNFlOLVK/sPKwmGMLx8/FTOFWuXpU= +github.com/go-logr/stdr v0.2.0/go.mod h1:NO1vneyJDqKVgJYnxhwXWWmQPOvNM391IG3H8ql3jiA= +github.com/go-logr/zapr v0.2.0 h1:v6Ji8yBW77pva6NkJKQdHLAJKrIJKRHz0RXwPqCHSR4= +github.com/go-logr/zapr v0.2.0/go.mod h1:qhKdvif7YF5GI9NWEpyxTSSBdGmzkNguibrdCNVPunU= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= @@ -622,6 +626,7 @@ go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/internal/testutil/testlogger/testlogger.go b/internal/testutil/testlogger/testlogger.go new file mode 100644 index 00000000..04ce5660 --- /dev/null +++ b/internal/testutil/testlogger/testlogger.go @@ -0,0 +1,61 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package testlogger implements a logr.Logger suitable for writing test assertions. +package testlogger + +import ( + "bytes" + "log" + "strings" + "sync" + "testing" + + "github.com/go-logr/logr" + "github.com/go-logr/stdr" +) + +// Logger implements logr.Logger in a way that captures logs for test assertions. +type Logger struct { + logr.Logger + t *testing.T + buffer syncBuffer +} + +// New returns a new test Logger. +func New(t *testing.T) *Logger { + res := Logger{t: t} + res.Logger = stdr.New(log.New(&res.buffer, "", 0)) + return &res +} + +// Lines returns the lines written to the test logger. +func (l *Logger) Lines() []string { + l.t.Helper() + l.buffer.mutex.Lock() + defer l.buffer.mutex.Unlock() + + // Trim leading/trailing whitespace and omit empty lines. + var result []string + for _, line := range strings.Split(l.buffer.buffer.String(), "\n") { + line = strings.TrimSpace(line) + if line != "" { + result = append(result, line) + } + } + return result +} + +// syncBuffer synchronizes access to a bytes.Buffer. +type syncBuffer struct { + mutex sync.Mutex + buffer bytes.Buffer +} + +func (s *syncBuffer) Write(p []byte) (n int, err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.buffer.Write(p) +} From 7d8c28a9dcf3499880f1b09f26c7c091a148f0c9 Mon Sep 17 00:00:00 2001 From: Matt Moyer Date: Mon, 14 Sep 2020 10:34:41 -0500 Subject: [PATCH 2/2] Extract testutil.TLSTestServer so it can be reused elsewhere. Signed-off-by: Matt Moyer --- internal/client/client_test.go | 21 ++++----------------- internal/testutil/tlsserver.go | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 17 deletions(-) create mode 100644 internal/testutil/tlsserver.go diff --git a/internal/client/client_test.go b/internal/client/client_test.go index 30b1b795..b932d1f7 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -8,10 +8,8 @@ package client import ( "context" "encoding/json" - "encoding/pem" "io/ioutil" "net/http" - "net/http/httptest" "testing" "time" @@ -20,20 +18,9 @@ import ( clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" "github.com/suzerain-io/pinniped/generated/1.19/apis/pinniped/v1alpha1" + "github.com/suzerain-io/pinniped/internal/testutil" ) -func startTestServer(t *testing.T, handler http.HandlerFunc) (string, string) { - t.Helper() - server := httptest.NewTLSServer(handler) - t.Cleanup(server.Close) - - caBundle := string(pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE", - Bytes: server.TLS.Certificates[0].Certificate[0], - })) - return caBundle, server.URL -} - func TestExchangeToken(t *testing.T) { t.Parallel() ctx := context.Background() @@ -48,7 +35,7 @@ func TestExchangeToken(t *testing.T) { t.Run("server error", func(t *testing.T) { t.Parallel() // Start a test server that returns only 500 errors. - caBundle, endpoint := startTestServer(t, func(w http.ResponseWriter, r *http.Request) { + caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) _, _ = w.Write([]byte("some server error")) }) @@ -62,7 +49,7 @@ func TestExchangeToken(t *testing.T) { t.Parallel() // Start a test server that returns success but with an error message errorMessage := "some login failure" - caBundle, endpoint := startTestServer(t, func(w http.ResponseWriter, r *http.Request) { + caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) { w.Header().Set("content-type", "application/json") _ = json.NewEncoder(w).Encode(&v1alpha1.CredentialRequest{ TypeMeta: metav1.TypeMeta{APIVersion: "pinniped.dev/v1alpha1", Kind: "CredentialRequest"}, @@ -80,7 +67,7 @@ func TestExchangeToken(t *testing.T) { expires := metav1.NewTime(time.Now().Truncate(time.Second)) // Start a test server that returns successfully and asserts various properties of the request. - caBundle, endpoint := startTestServer(t, func(w http.ResponseWriter, r *http.Request) { + caBundle, endpoint := testutil.TLSTestServer(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(t, http.MethodPost, r.Method) require.Equal(t, "/apis/pinniped.dev/v1alpha1/credentialrequests", r.URL.Path) require.Equal(t, "application/json", r.Header.Get("content-type")) diff --git a/internal/testutil/tlsserver.go b/internal/testutil/tlsserver.go new file mode 100644 index 00000000..0da0be94 --- /dev/null +++ b/internal/testutil/tlsserver.go @@ -0,0 +1,27 @@ +/* +Copyright 2020 VMware, Inc. +SPDX-License-Identifier: Apache-2.0 +*/ + +package testutil + +import ( + "encoding/pem" + "net/http" + "net/http/httptest" + "testing" +) + +// TLSTestServer starts a test server listening on a local port using a test CA. It returns the PEM CA bundle and the +// URL of the listening server. The lifetime of the server is bound to the provided *testing.T. +func TLSTestServer(t *testing.T, handler http.HandlerFunc) (caBundlePEM string, url string) { + t.Helper() + server := httptest.NewTLSServer(handler) + t.Cleanup(server.Close) + + caBundle := string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: server.TLS.Certificates[0].Certificate[0], + })) + return caBundle, server.URL +}