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.
5 func compareErrors(err1, err2 requestError) (success bool, field string, val1, val2 interface{}) {
6 if err1.Slug != err2.Slug {
7 return false, "Slug", err1.Slug, err2.Slug
9 if err1.Field != err2.Field {
10 return false, "Field", err1.Field, err2.Field
12 if err1.Param != err2.Param {
13 return false, "Param", err1.Param, err2.Param
15 if err1.Header != err2.Header {
16 return false, "Header", err1.Header, err2.Header
18 return true, "", nil, nil
21 func compareResponses(resp1, resp2 response) (success bool, field string, val1, val2 interface{}) {
22 if len(resp1.Errors) != len(resp2.Errors) {
23 return false, "Errors", resp1.Errors, resp2.Errors
25 if len(resp1.Logins) != len(resp2.Logins) {
26 return false, "Logins", resp1.Logins, resp2.Logins
28 if len(resp1.Profiles) != len(resp2.Profiles) {
29 return false, "Profiles", resp1.Profiles, resp2.Profiles
31 if len(resp1.Clients) != len(resp2.Clients) {
32 return false, "Clients", resp1.Clients, resp2.Clients
34 if len(resp1.Endpoints) != len(resp2.Endpoints) {
35 return false, "Endpoints", resp1.Endpoints, resp2.Endpoints
37 for pos := range resp1.Errors {
38 success, field, val1, val2 = compareErrors(resp1.Errors[pos], resp2.Errors[pos])
40 field = fmt.Sprintf("Error %d %s", pos, field)
44 for pos := range resp1.Logins {
45 success, field, val1, val2 = compareLogins(resp1.Logins[pos], resp2.Logins[pos])
47 field = fmt.Sprintf("Login %d %s", pos, field)
51 for pos := range resp1.Profiles {
52 success, field, val1, val2 = compareProfiles(resp1.Profiles[pos], resp2.Profiles[pos])
54 field = fmt.Sprintf("Profile %d %s", pos, field)
58 for pos := range resp1.Clients {
59 success, field, val1, val2 = compareClients(resp1.Clients[pos], resp2.Clients[pos])
61 field = fmt.Sprintf("Client %d %s", pos, field)
65 for pos := range resp1.Endpoints {
66 success, field, val1, val2 = compareEndpoints(resp1.Endpoints[pos], resp2.Endpoints[pos])
68 field = fmt.Sprintf("Endpoint %d %s", pos, field)
72 return true, "", nil, nil
75 func fillInServerGenerated(expectation, result response) {
76 if len(expectation.Profiles) > 0 {
77 for pos, profile := range expectation.Profiles {
78 profile.ID = result.Profiles[pos].ID
79 profile.Created = result.Profiles[pos].Created
80 profile.LastSeen = result.Profiles[pos].LastSeen
81 expectation.Profiles[pos] = profile
84 if len(expectation.Logins) > 0 {
85 for pos, login := range expectation.Logins {
86 login.ProfileID = result.Logins[pos].ProfileID
87 login.Created = result.Logins[pos].Created
88 login.LastUsed = result.Logins[pos].LastUsed
89 expectation.Logins[pos] = login
92 if len(expectation.Clients) > 0 {
93 for pos, client := range expectation.Clients {
94 client.ID = result.Clients[pos].ID
95 client.Secret = result.Clients[pos].Secret
96 client.OwnerID = result.Clients[pos].OwnerID
97 expectation.Clients[pos] = client
100 if len(expectation.Endpoints) > 0 {
101 for pos, endpoint := range expectation.Endpoints {
102 endpoint.ID = result.Endpoints[pos].ID
103 endpoint.ClientID = result.Endpoints[pos].ClientID
104 endpoint.Added = result.Endpoints[pos].Added
105 expectation.Endpoints[pos] = endpoint