auth
auth/grant_test.go
Create a grant confirmation endpoint and its first test. Lay the framework for how we're going to write endpoints, and how we're going to test them by doing a super simple grant confirmation endpoint (where the user authorizes the grant, which can then be exchanged for a token) and a simple test to ensure that a page gets rendered when valid input is provided. We're still missing a lot of test cases: when different forms of valid input are provided (e.g., no scope, no redirect URI, etc.); when invalid input is provided; etc.
| paddy@29 | 1 package auth |
| paddy@29 | 2 |
| paddy@29 | 3 import ( |
| paddy@51 | 4 "html/template" |
| paddy@51 | 5 "net/http" |
| paddy@51 | 6 "net/http/httptest" |
| paddy@29 | 7 "testing" |
| paddy@29 | 8 "time" |
| paddy@29 | 9 |
| paddy@45 | 10 "code.secondbit.org/uuid" |
| paddy@29 | 11 ) |
| paddy@29 | 12 |
| paddy@29 | 13 var grantStores = []GrantStore{NewMemstore()} |
| paddy@29 | 14 |
| paddy@34 | 15 func compareGrants(grant1, grant2 Grant) (success bool, field string, grant1val, grant2val interface{}) { |
| paddy@34 | 16 if grant1.Code != grant2.Code { |
| paddy@34 | 17 return false, "code", grant1.Code, grant2.Code |
| paddy@34 | 18 } |
| paddy@34 | 19 if !grant1.Created.Equal(grant2.Created) { |
| paddy@34 | 20 return false, "created", grant1.Created, grant2.Created |
| paddy@34 | 21 } |
| paddy@34 | 22 if grant1.ExpiresIn != grant2.ExpiresIn { |
| paddy@34 | 23 return false, "expires in", grant1.ExpiresIn, grant2.ExpiresIn |
| paddy@34 | 24 } |
| paddy@34 | 25 if !grant1.ClientID.Equal(grant2.ClientID) { |
| paddy@34 | 26 return false, "client ID", grant1.ClientID, grant2.ClientID |
| paddy@34 | 27 } |
| paddy@34 | 28 if grant1.Scope != grant2.Scope { |
| paddy@34 | 29 return false, "scope", grant1.Scope, grant2.Scope |
| paddy@34 | 30 } |
| paddy@34 | 31 if grant1.RedirectURI != grant2.RedirectURI { |
| paddy@34 | 32 return false, "redirect URI", grant1.RedirectURI, grant2.RedirectURI |
| paddy@34 | 33 } |
| paddy@34 | 34 if grant1.State != grant2.State { |
| paddy@34 | 35 return false, "state", grant1.State, grant2.State |
| paddy@34 | 36 } |
| paddy@34 | 37 return true, "", nil, nil |
| paddy@34 | 38 } |
| paddy@34 | 39 |
| paddy@29 | 40 func TestGrantStoreSuccess(t *testing.T) { |
| paddy@36 | 41 t.Parallel() |
| paddy@29 | 42 grant := Grant{ |
| paddy@29 | 43 Code: "code", |
| paddy@29 | 44 Created: time.Now(), |
| paddy@29 | 45 ExpiresIn: 180, |
| paddy@29 | 46 ClientID: uuid.NewID(), |
| paddy@29 | 47 Scope: "scope", |
| paddy@29 | 48 RedirectURI: "redirectURI", |
| paddy@29 | 49 State: "state", |
| paddy@29 | 50 } |
| paddy@34 | 51 for _, store := range grantStores { |
| paddy@29 | 52 err := store.SaveGrant(grant) |
| paddy@29 | 53 if err != nil { |
| paddy@34 | 54 t.Errorf("Error saving grant to %T: %s", store, err) |
| paddy@34 | 55 } |
| paddy@34 | 56 err = store.SaveGrant(grant) |
| paddy@34 | 57 if err != ErrGrantAlreadyExists { |
| paddy@34 | 58 t.Errorf("Expected ErrGrantAlreadyExists from %T, got %+v", store, err) |
| paddy@29 | 59 } |
| paddy@29 | 60 retrieved, err := store.GetGrant(grant.Code) |
| paddy@29 | 61 if err != nil { |
| paddy@34 | 62 t.Errorf("Error retrieving grant from %T: %s", store, err) |
| paddy@29 | 63 } |
| paddy@34 | 64 match, field, expectation, result := compareGrants(grant, retrieved) |
| paddy@34 | 65 if !match { |
| paddy@34 | 66 t.Errorf("Expected `%v` in the `%s` field of grant retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@34 | 67 } |
| paddy@29 | 68 err = store.DeleteGrant(grant.Code) |
| paddy@29 | 69 if err != nil { |
| paddy@34 | 70 t.Errorf("Error removing grant from %T: %s", store, err) |
| paddy@29 | 71 } |
| paddy@29 | 72 retrieved, err = store.GetGrant(grant.Code) |
| paddy@29 | 73 if err != ErrGrantNotFound { |
| paddy@34 | 74 t.Errorf("Expected ErrGrantNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@34 | 75 } |
| paddy@34 | 76 err = store.DeleteGrant(grant.Code) |
| paddy@34 | 77 if err != ErrGrantNotFound { |
| paddy@34 | 78 t.Errorf("Expected ErrGrantNotFound from %T, got %+v", store, err) |
| paddy@29 | 79 } |
| paddy@29 | 80 } |
| paddy@29 | 81 } |
| paddy@51 | 82 |
| paddy@51 | 83 func TestGrantCodeRedirect(t *testing.T) { |
| paddy@51 | 84 t.Parallel() |
| paddy@51 | 85 store := NewMemstore() |
| paddy@51 | 86 testContext := Context{ |
| paddy@51 | 87 template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")), |
| paddy@51 | 88 clients: store, |
| paddy@51 | 89 grants: store, |
| paddy@51 | 90 profiles: store, |
| paddy@51 | 91 tokens: store, |
| paddy@51 | 92 } |
| paddy@51 | 93 w := httptest.NewRecorder() |
| paddy@51 | 94 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@51 | 95 if err != nil { |
| paddy@51 | 96 t.Fatal("Can't build request:", err) |
| paddy@51 | 97 } |
| paddy@51 | 98 // see OAuth 2.0 spec, section 4.1.1 |
| paddy@51 | 99 req.URL.Query().Set("response_type", "code") |
| paddy@51 | 100 req.URL.Query().Set("client_id", "test_client_id") |
| paddy@51 | 101 req.URL.Query().Set("redirect_uri", "https://test.secondbit.org/redirect") |
| paddy@51 | 102 req.URL.Query().Set("scope", "testscope") |
| paddy@51 | 103 req.URL.Query().Set("state", "my super secure state string") |
| paddy@51 | 104 GetGrantHandler(w, req, testContext) |
| paddy@51 | 105 if w.Code != http.StatusOK { |
| paddy@51 | 106 t.Errorf("Expected status code to be %d, got %d", http.StatusOK, w.Code) |
| paddy@51 | 107 } |
| paddy@51 | 108 if w.Body.String() != "Get auth grant" { |
| paddy@51 | 109 t.Errorf("Expected body to be `%s`, got `%s`", "Get auth grant", w.Body.String()) |
| paddy@51 | 110 } |
| paddy@51 | 111 } |