Make all tests that deal with the store interfaces go through the Context. This
is mainly important so that pre- and post- save/retrieval/deletion/whatever
transforms can be done without doing them in every single implementation of the
store.
Change the Endpoint URI property to be a string, not a *url.URL. This makes
testing easier, JSON responses cleaner, and is all around just a better
strategy. Just because we turn it into a URL every now and then doesn't mean
that's how we need to store it.
Add JSON tags to the Client type and Endpoint type.
Create normalizeURI and normalizeURIString methods to... well, normalize the
Endpoint URIs. This makes it so that we can compare them, and forgive some
arbitrary user behaviour (like slashes, etc.)
Add a NormalizedURI property to the Endpoint type. This is where we store the
NormalizedURI, which is what we'll be using when we want to check if an endpoint
is valid or not. For the sake of tests and predictability, however, we always
want to redirect to the URI, not the NormalizedURI.
Add checks to the Client creation API endpoint to give better errors. Now
leaving out the Type won't be considered an invalid type, it will be considered
a missing parameter. An empty name will be reported as a missing parameter, a
name with too few characters will be reported as an insufficient name, and a
name with too many characters will be reported as an overflow name. We gather as
many of these errors as apply before returning.
Check if an Endpoint URI is absolute before adding it as an endpoint, or return
an invalid value error if it is not.
Always return the errors array when creating a client. We could succeed in
creating one or more things and still have errors. We should return anything
that's created _as well as_ any errors encountered.
Add unit testing for our CreateClientHandler.
Fix our oauth2 tests so that if there's an error in the body, it's in the test
logs. This should help debugging significantly.
Fix our oauth2 tests so that the Profile only requires 1 iteration for its
password hashing. This means each time we want to validate a session, it doesn't
add a full second to our test runs. This is a big speed improvement for our
tests.
Add test helper methods for comparing API errors, API responses, and filling in
server-generated information in a response that it's impossible to have an
expectation around (e.g., IDs) so that we can use our comparison helpers to
check if a response is as we expect it.
Fix a typo in our Context helpers that was reporting no sessionStore being set
_only_ when a sessionStore was set. So yes, the opposite of what we wanted.
Oops. This was discovered by passing all our tests through the context. methods
instead of operating on the stores themselves.
7 "code.secondbit.org/uuid.hg"
10 var tokenStores = []tokenStore{NewMemstore()}
12 func compareTokens(token1, token2 Token) (success bool, field string, val1, val2 interface{}) {
13 if token1.AccessToken != token2.AccessToken {
14 return false, "access token", token1.AccessToken, token2.AccessToken
16 if token1.RefreshToken != token2.RefreshToken {
17 return false, "refresh token", token1.RefreshToken, token2.RefreshToken
19 if !token1.Created.Equal(token2.Created) {
20 return false, "created", token1.Created, token2.Created
22 if token1.CreatedFrom != token2.CreatedFrom {
23 return false, "created from", token1.CreatedFrom, token2.CreatedFrom
25 if token1.ExpiresIn != token2.ExpiresIn {
26 return false, "expires in", token1.ExpiresIn, token2.ExpiresIn
28 if token1.RefreshExpiresIn != token2.RefreshExpiresIn {
29 return false, "refresh expires in", token1.RefreshExpiresIn, token2.RefreshExpiresIn
31 if token1.TokenType != token2.TokenType {
32 return false, "token type", token1.TokenType, token2.TokenType
34 if token1.Scope != token2.Scope {
35 return false, "scope", token1.Scope, token2.Scope
37 if !token1.ProfileID.Equal(token2.ProfileID) {
38 return false, "profile ID", token1.ProfileID, token2.ProfileID
40 if token1.Revoked != token2.Revoked {
41 return false, "revoked", token1.Revoked, token2.Revoked
43 return true, "", nil, nil
46 func TestTokenStoreSuccess(t *testing.T) {
49 AccessToken: "access",
50 RefreshToken: "refresh",
55 ProfileID: uuid.NewID(),
57 for _, store := range tokenStores {
58 context := Context{tokens: store}
59 err := context.SaveToken(token)
61 t.Errorf("Error saving token to %T: %s", store, err)
63 err = context.SaveToken(token)
64 if err != ErrTokenAlreadyExists {
65 t.Errorf("Expected ErrTokenAlreadyExists from %T, got %s", store, err)
67 retrievedAccess, err := context.GetToken(token.AccessToken, false)
69 t.Errorf("Error retrieving token from %T: %s", store, err)
71 success, field, expectation, result := compareTokens(token, retrievedAccess)
73 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
75 retrievedRefresh, err := context.GetToken(token.RefreshToken, true)
77 t.Errorf("Error retrieving refresh token from %T: %s", store, err)
79 success, field, expectation, result = compareTokens(token, retrievedRefresh)
81 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
83 retrievedProfile, err := context.GetTokensByProfileID(token.ProfileID, 25, 0)
85 t.Errorf("Error retrieving token by profile from %T: %s", store, err)
87 if len(retrievedProfile) != 1 {
88 t.Errorf("Expected 1 token retrieved by profile ID from %T, got %+v", store, retrievedProfile)
90 success, field, expectation, result = compareTokens(token, retrievedProfile[0])
92 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
94 err = context.RevokeToken(token.AccessToken, false)
96 t.Errorf("Error revoking token in %T: %s", store, err)
98 retrievedRevoked, err := context.GetToken(token.AccessToken, false)
100 t.Errorf("Error retrieving token from %T: %s", store, err)
103 success, field, expectation, result = compareTokens(token, retrievedRevoked)
105 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
107 // TODO(paddy): test revoking by refresh token.
108 err = context.RemoveToken(token.AccessToken)
110 t.Errorf("Error removing token from %T: %s", store, err)
112 _, err = context.GetToken(token.AccessToken, false)
113 if err != ErrTokenNotFound {
114 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
116 _, err = context.GetToken(token.RefreshToken, true)
117 if err != ErrTokenNotFound {
118 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
120 retrievedProfile, err = context.GetTokensByProfileID(token.ProfileID, 25, 0)
122 t.Errorf("Error retrieving token by profile from %T: %s", store, err)
124 if len(retrievedProfile) != 0 {
125 t.Errorf("Expected list of 0 tokens from %T, got %+v", store, retrievedProfile)
127 err = context.RemoveToken(token.AccessToken)
128 if err != ErrTokenNotFound {
129 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
131 err = context.RevokeToken(token.AccessToken, false)
132 if err != ErrTokenNotFound {
133 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
135 err = context.RevokeToken(token.RefreshToken, true)
136 if err != ErrTokenNotFound {
137 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)