Skip to content

Commit

Permalink
WIP add tenant tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kernle32dll committed May 28, 2024
1 parent d4bd43f commit 9958eef
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 0 deletions.
129 changes: 129 additions & 0 deletions tenant/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package tenant_test

import (
"github.com/google/uuid"
"github.com/justinas/alice"
"github.com/kernle32dll/turtleware"
"github.com/kernle32dll/turtleware/tenant"
"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jws"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/stretchr/testify/suite"

"fmt"
"io"
"net/http"
"os"
"path"
"strings"
)

type CommonSuite struct {
suite.Suite

entityUUID string
userUUID string
tenantUUID string
}

func (s *CommonSuite) SetupTest() {
s.entityUUID = uuid.NewString()
s.userUUID = uuid.NewString()
s.tenantUUID = uuid.NewString()
}

func (s *CommonSuite) buildEntityUUIDChain(h http.Handler) http.Handler {
return turtleware.EntityUUIDMiddleware(func(r *http.Request) (string, error) {
return s.entityUUID, nil
})(h)
}

func (s *CommonSuite) buildAuthChain(h http.Handler) http.Handler {
s.T().Helper()

privateKey, err := jwk.FromRaw([]byte("secret-passphrase"))
s.Require().NoError(err)
s.Require().NoError(privateKey.Set(jwk.KeyIDKey, "super-key"))
s.Require().NoError(privateKey.Set(jwk.AlgorithmKey, jwa.HS512))

keySet := jwk.NewSet()
s.Require().NoError(keySet.AddKey(privateKey))

return alice.New(
func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := s.generateToken(
jwa.HS512,
privateKey,
map[string]interface{}{
"uuid": s.userUUID,
"tenant_uuid": s.tenantUUID,
},
map[string]interface{}{jwk.KeyIDKey: privateKey.KeyID()},
)

r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
handler.ServeHTTP(w, r)
})
},
turtleware.AuthBearerHeaderMiddleware,
turtleware.AuthClaimsMiddleware(keySet),
tenant.UUIDMiddleware,
).Then(h)
}

func (s *CommonSuite) generateToken(
algo jwa.SignatureAlgorithm,
key interface{},
claims map[string]interface{},
headers map[string]interface{},
) string {
t := jwt.New()

for k, v := range claims {
if err := t.Set(k, v); err != nil {
s.Require().NoError(err)
}
}

hdr := jws.NewHeaders()
for k, v := range headers {
if err := hdr.Set(k, v); err != nil {
s.Require().NoError(err)
}
}

signedT, err := jwt.Sign(t, jwt.WithKey(algo, key, jws.WithProtectedHeaders(hdr)))
if err != nil {
s.Require().NoError(err)
}

return string(signedT)
}

func (s *CommonSuite) loadTestDataString(name string) string {
bufBytes, err := io.ReadAll(s.loadTestData(name))
if err != nil {
s.Require().NoError(err)
}

return string(bufBytes)
}

func (s *CommonSuite) loadTestData(name string) io.Reader {
filePath := path.Join("testdata", name)

f, err := os.Open(filePath)
if err != nil {
s.Require().NoError(err)
}

s.T().Cleanup(func() {
if err := f.Close(); err != nil && !strings.Contains(err.Error(), "file already closed") {
s.T().Logf("Failed to close file handle for test data %q: %s", name, err)
}
})

return f
}
5 changes: 5 additions & 0 deletions tenant/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ toolchain go1.22.0
replace github.com/kernle32dll/turtleware => ./..

require (
github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0
github.com/justinas/alice v1.2.0
github.com/kernle32dll/turtleware v0.0.0-20240211133627-8a740328cf04
github.com/lestrrat-go/jwx/v2 v2.0.21
github.com/rs/zerolog v1.33.0
github.com/stretchr/testify v1.9.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
Expand All @@ -31,11 +34,13 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect
go.opentelemetry.io/otel v1.27.0 // indirect
go.opentelemetry.io/otel/metric v1.27.0 // indirect
go.opentelemetry.io/otel/trace v1.27.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/sys v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
6 changes: 6 additions & 0 deletions tenant/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/kernle32dll/emissione-go v1.1.0 h1:ecsp58tVs8sJA0FJftIVHHPv/hagtjQJui
github.com/kernle32dll/emissione-go v1.1.0/go.mod h1:h3zrmXUggdVPQW7hHv0WUGHoO4VwTieXvUpC/Go95kE=
github.com/kernle32dll/keybox-go v1.2.0 h1:4bfv3uilJi8y971G2m62W2NV+n9OoYryT5Z9ULgzT6Q=
github.com/kernle32dll/keybox-go v1.2.0/go.mod h1:+avlBw/jrVKyR/tHaWsA8YMT9zLsbnhPqmZH+a94sRY=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
Expand Down Expand Up @@ -58,6 +60,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -88,6 +92,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
93 changes: 93 additions & 0 deletions tenant/middleware_common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package tenant_test

import (
"context"
"github.com/kernle32dll/turtleware/tenant"
"github.com/stretchr/testify/suite"
"strings"

"net/http"
"net/http/httptest"
"testing"
)

type MiddlewareCommonSuite struct {
CommonSuite

response *httptest.ResponseRecorder
request *http.Request
}

func TestMiddlewareCommonSuite(t *testing.T) {
suite.Run(t, &MiddlewareCommonSuite{})
}

func (s *MiddlewareCommonSuite) SetupTest() {
s.CommonSuite.SetupTest()

s.response = httptest.NewRecorder()
s.request = httptest.NewRequest(http.MethodGet, "https://example.com/foo", http.NoBody)
}

func (s *MiddlewareCommonSuite) SetupSubTest() {
s.SetupTest()
}

func (s *MiddlewareCommonSuite) Test_UUIDMiddleware_Success() {
// given
recordedUUID := ""
middlewareVerify := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
tenantUUID, err := tenant.UUIDFromRequestContext(r.Context())
s.Require().NoError(err)

recordedUUID = tenantUUID
})

// when
s.buildAuthChain(middlewareVerify).ServeHTTP(s.response, s.request)

// then
s.Empty(s.response.Body.String())
s.Equal(s.tenantUUID, recordedUUID)
}

func (s *MiddlewareCommonSuite) Test_UUIDMiddleware_Error() {
// given
middlewareVerify := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
s.Fail("unexpected middleware invocation")
})

middleware := tenant.UUIDMiddleware

// when
middleware(middlewareVerify).ServeHTTP(s.response, s.request)

// then
s.Equal(http.StatusInternalServerError, s.response.Code)
s.JSONEq(s.loadTestDataString("errors/missing_auth_claims_error.json"), s.response.Body.String())
}

func (s *MiddlewareCommonSuite) Test_UUIDFromRequestContext_Error() {
// given
ctx := context.Background()

// when
tenantUUID, err := tenant.UUIDFromRequestContext(ctx)

// then
s.Empty(tenantUUID)
s.ErrorIs(err, tenant.ErrContextMissingTenantUUID)
}

func (s *MiddlewareCommonSuite) Test_UUIDFromRequestContext_ErrTokenMissingTenantUUID() {
// given
s.tenantUUID = ""

chain := s.buildAuthChain(nil)

// when
chain.ServeHTTP(s.response, s.request)

// then
s.True(strings.Contains(s.response.Body.String(), tenant.ErrTokenMissingTenantUUID.Error()))
}
7 changes: 7 additions & 0 deletions tenant/testdata/errors/missing_auth_claims_error.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"status": 500,
"text": "Internal Server Error",
"errors": [
"missing auth claims in context"
]
}

0 comments on commit 9958eef

Please sign in to comment.