Skip to content

Commit

Permalink
Unit tests (#106)
Browse files Browse the repository at this point in the history
* added key provide unit test

* added basculechecks unit test, fixed logic

* added basic token factory unit test

* added nil, other checks to basculehttp options

* replace strings with consts

* small tweaks to cover some cases

* Removed outdated SermoDigital dependency and unused metrics

given the old dependency, we haven't been missing these histograms
so it seemed best to just remove them.

* fixed bearer token factory test

* some basculehttp tests

* updated metric listener, added placeholder test files

* remove impossible test case
  • Loading branch information
kristinapathak authored May 28, 2021
1 parent c011b12 commit 41ed346
Show file tree
Hide file tree
Showing 21 changed files with 629 additions and 387 deletions.
78 changes: 78 additions & 0 deletions basculechecks/capabilitiesvalidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"errors"
"fmt"
"net/url"
"regexp"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -340,6 +342,82 @@ func TestGetCapabilities(t *testing.T) {
}
}

func TestNewCapabilitiesValidator(t *testing.T) {
require := require.New(t)
goodCheck, err := NewRegexEndpointCheck("", "")
require.Nil(err)
es := []string{"abc", "def", `\M`, "adbecf"}
goodEndpoints := []*regexp.Regexp{
regexp.MustCompile(es[0]),
regexp.MustCompile(es[1]),
regexp.MustCompile(es[3]),
}
_, err = regexp.Compile(es[2])
require.Error(err)

tests := []struct {
description string
config CapabilitiesValidatorConfig
expectedOut CapabilitiesCheckerOut
expectedErr error
}{
{
description: "Success",
config: CapabilitiesValidatorConfig{
Type: "enforce",
EndpointBuckets: es,
},
expectedOut: CapabilitiesCheckerOut{
Checker: CapabilitiesValidator{Checker: goodCheck},
Options: []MetricOption{
WithEndpoints(goodEndpoints),
},
},
},
{
description: "Monitor success",
config: CapabilitiesValidatorConfig{
Type: "monitor",
EndpointBuckets: es,
},
expectedOut: CapabilitiesCheckerOut{
Checker: CapabilitiesValidator{Checker: goodCheck},
Options: []MetricOption{
WithEndpoints(goodEndpoints),
MonitorOnly(),
},
},
},
{
description: "Disabled success",
},
{
description: "New check error",
config: CapabilitiesValidatorConfig{
Type: "enforce",
Prefix: `\M`,
},
expectedErr: errors.New("failed to compile prefix"),
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
assert := assert.New(t)
out, err := NewCapabilitiesValidator(tc.config)
assert.Equal(tc.expectedOut.Checker, out.Checker)
assert.Equal(len(tc.expectedOut.Options), len(out.Options))
if tc.expectedErr == nil {
assert.NoError(err)
return
}
assert.True(strings.Contains(err.Error(), tc.expectedErr.Error()),
fmt.Errorf("error [%v] doesn't contain error [%v]",
err, tc.expectedErr),
)
})
}
}

func buildDummyAttributes(keyPath []string, val interface{}) map[string]interface{} {
keyLen := len(keyPath)
if keyLen == 0 {
Expand Down
14 changes: 9 additions & 5 deletions basculehttp/basicTokenFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ var (
ErrorInvalidPassword = errors.New("invalid password")
)

type EncodedBasicKeys struct {
Basic []string
}

// EncodedBasicKeysIn contains string representations of the basic auth allowed.
type EncodedBasicKeysIn struct {
fx.In
Basic []string
Keys EncodedBasicKeys `name:"encoded_basic_auths"`
}

// TokenFactoryFunc makes it so any function that has the same signature as
Expand Down Expand Up @@ -120,19 +124,19 @@ func ProvideBasicTokenFactory(key string) fx.Option {
return fx.Provide(
fx.Annotated{
Name: "encoded_basic_auths",
Target: arrange.UnmarshalKey(key, EncodedBasicKeysIn{}),
Target: arrange.UnmarshalKey(key, EncodedBasicKeys{}),
},
fx.Annotated{
Group: "bascule_constructor_options",
Target: func(in EncodedBasicKeysIn) (COption, error) {
if len(in.Basic) == 0 {
if len(in.Keys.Basic) == 0 {
return nil, nil
}
tf, err := NewBasicTokenFactoryFromList(in.Basic)
tf, err := NewBasicTokenFactoryFromList(in.Keys.Basic)
if err != nil {
return nil, err
}
return WithTokenFactory("Basic", tf), nil
return WithTokenFactory(BasicAuthorization, tf), nil
},
},
)
Expand Down
78 changes: 78 additions & 0 deletions basculehttp/basicTokenFactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/http/httptest"
"strings"
"testing"

"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/xmidt-org/arrange"
"github.com/xmidt-org/bascule"
"go.uber.org/fx"
)

func TestBasicTokenFactory(t *testing.T) {
Expand Down Expand Up @@ -131,5 +137,77 @@ func TestNewBasicTokenFactoryFromList(t *testing.T) {
}
})
}
}

func TestProvideBasicTokenFactory(t *testing.T) {
type In struct {
fx.In
Options []COption `group:"bascule_constructor_options"`
}

const yaml = `
good:
basic: ["dXNlcjpwYXNz", "dXNlcjpwYXNz", "dXNlcjpwYXNz"]
bad:
basic: ["AAAAAAAA"]
`
v := viper.New()
v.SetConfigType("yaml")
require.NoError(t, v.ReadConfig(strings.NewReader(yaml)))

tests := []struct {
description string
key string
optionExpected bool
expectedErr error
}{
{
description: "Success",
key: "good",
optionExpected: true,
},
{
description: "Disabled success",
key: "nonexistent",
},
{
description: "Failure",
key: "bad",
expectedErr: errors.New("malformed"),
},
}
for _, tc := range tests {
t.Run(tc.description, func(t *testing.T) {
result := In{}
assert := assert.New(t)
require := require.New(t)
app := fx.New(
arrange.TestLogger(t),
arrange.ForViper(v),
ProvideBasicTokenFactory(tc.key),
fx.Invoke(
func(in In) {
result = in
},
),
)
err := app.Err()
if tc.expectedErr == nil {
assert.NoError(err)
assert.True(len(result.Options) == 1)
if tc.optionExpected {
require.NotNil(result.Options[0])
return
}
require.Nil(result.Options[0])
return
}
assert.Nil(result.Options)
require.Error(err)
assert.True(strings.Contains(err.Error(), tc.expectedErr.Error()),
fmt.Errorf("error [%v] doesn't contain error [%v]",
err, tc.expectedErr),
)
})
}
}
29 changes: 13 additions & 16 deletions basculehttp/bearerTokenFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ const (
)

var (
ErrorInvalidPrincipal = errors.New("invalid principal")
ErrorInvalidToken = errors.New("token isn't valid")
ErrorUnexpectedClaims = errors.New("claims wasn't MapClaims as expected")
ErrEmptyValue = errors.New("empty value")
ErrInvalidPrincipal = errors.New("invalid principal")
ErrInvalidToken = errors.New("token isn't valid")
ErrUnexpectedClaims = errors.New("claims wasn't MapClaims as expected")

ErrNilResolver = errors.New("resolver cannot be nil")
)
Expand All @@ -58,7 +59,7 @@ type BearerTokenFactory struct {
// well, a Token of type "jwt" is returned.
func (btf BearerTokenFactory) ParseAndValidate(ctx context.Context, _ *http.Request, _ bascule.Authorization, value string) (bascule.Token, error) {
if len(value) == 0 {
return nil, errors.New("empty value")
return nil, ErrEmptyValue
}

keyfunc := func(token *jwt.Token) (interface{}, error) {
Expand All @@ -79,34 +80,30 @@ func (btf BearerTokenFactory) ParseAndValidate(ctx context.Context, _ *http.Requ
Leeway: btf.Leeway,
}

jwsToken, err := btf.Parser.ParseJWT(value, &leewayclaims, keyfunc)
jwtToken, err := btf.Parser.ParseJWT(value, &leewayclaims, keyfunc)
if err != nil {
return nil, fmt.Errorf("failed to parse JWS: %v", err)
}
if !jwsToken.Valid {
return nil, ErrorInvalidToken
if !jwtToken.Valid {
return nil, ErrInvalidToken
}

claims, ok := jwsToken.Claims.(*bascule.ClaimsWithLeeway)

claims, ok := jwtToken.Claims.(*bascule.ClaimsWithLeeway)
if !ok {
return nil, fmt.Errorf("failed to parse JWS: %w", ErrorUnexpectedClaims)
return nil, fmt.Errorf("failed to parse JWS: %w", ErrUnexpectedClaims)
}

claimsMap, err := claims.GetMap()
if err != nil {
return nil, fmt.Errorf("failed to get map of claims with object [%v]: %v", claims, err)
}

jwtClaims := bascule.NewAttributes(claimsMap)

principalVal, ok := jwtClaims.Get(jwtPrincipalKey)
if !ok {
return nil, fmt.Errorf("%w: principal value not found at key %v", ErrorInvalidPrincipal, jwtPrincipalKey)
return nil, fmt.Errorf("%w: principal value not found at key %v", ErrInvalidPrincipal, jwtPrincipalKey)
}
principal, ok := principalVal.(string)
if !ok {
return nil, fmt.Errorf("%w: principal value [%v] not a string", ErrorInvalidPrincipal, principalVal)
return nil, fmt.Errorf("%w: principal value [%v] not a string", ErrInvalidPrincipal, principalVal)
}

return bascule.NewToken("jwt", principal, jwtClaims), nil
Expand Down Expand Up @@ -136,7 +133,7 @@ func ProvideBearerTokenFactory(configKey string, optional bool) fx.Option {
}
return nil, ErrNilResolver
}
return WithTokenFactory("Bearer", f), nil
return WithTokenFactory(BearerAuthorization, f), nil
},
},
),
Expand Down
Loading

0 comments on commit 41ed346

Please sign in to comment.