-
Notifications
You must be signed in to change notification settings - Fork 0
Expand test suite with adapter and DB coverage #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "database/sql" | ||
| "strconv" | ||
| "testing" | ||
|
|
||
| _ "github.com/mattn/go-sqlite3" | ||
| "github.com/ory/fosite" | ||
| ) | ||
|
|
||
| func BenchmarkLoggingAdapterCreateToken(b *testing.B) { | ||
| adapter := NewLoggingAdapter(NewInMemoryStore()) | ||
| req := &fosite.Request{} | ||
| for i := 0; i < b.N; i++ { | ||
| adapter.CreateToken(context.Background(), "access_token", "sig"+strconv.Itoa(i), "c1", req) | ||
| } | ||
| } | ||
|
|
||
| func BenchmarkLegacyDBCreateToken(b *testing.B) { | ||
| db, err := sql.Open("sqlite3", ":memory:") | ||
| if err != nil { | ||
| b.Fatal(err) | ||
| } | ||
| stmts := []string{ | ||
| `CREATE TABLE tokens (signature TEXT PRIMARY KEY, client_id TEXT, token_type TEXT, data BLOB, revoked_at TIMESTAMP)`, | ||
| } | ||
| for _, s := range stmts { | ||
| if _, err := db.Exec(s); err != nil { | ||
| b.Fatal(err) | ||
| } | ||
| } | ||
| adapter := NewLegacyDBAdapter(db) | ||
| req := []byte("data") | ||
| for i := 0; i < b.N; i++ { | ||
| adapter.CreateToken(context.Background(), "access_token", "sig"+strconv.Itoa(i), "c1", req) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "database/sql" | ||
| "errors" | ||
| "testing" | ||
| "time" | ||
|
|
||
| _ "github.com/mattn/go-sqlite3" | ||
| "github.com/ory/fosite" | ||
| ) | ||
|
|
||
| func setupTestDB(t *testing.T) *sql.DB { | ||
|
||
| db, err := sql.Open("sqlite3", ":memory:") | ||
| if err != nil { | ||
| t.Fatalf("failed to open sqlite: %v", err) | ||
| } | ||
| // create tables | ||
| statements := []string{ | ||
| `CREATE TABLE clients (id TEXT PRIMARY KEY, secret TEXT, redirect_uris TEXT, scopes TEXT, is_public BOOLEAN)`, | ||
| `CREATE TABLE tokens (signature TEXT PRIMARY KEY, client_id TEXT, token_type TEXT, data BLOB, revoked_at TIMESTAMP)`, | ||
| `CREATE TABLE sessions (id TEXT PRIMARY KEY, session_type TEXT, data BLOB)`, | ||
| `CREATE TABLE used_jtis (jti TEXT PRIMARY KEY, expires_at TIMESTAMP)`, | ||
| } | ||
| for _, stmt := range statements { | ||
| if _, err := db.Exec(stmt); err != nil { | ||
| t.Fatalf("failed to create table: %v", err) | ||
| } | ||
| } | ||
| return db | ||
| } | ||
|
|
||
| func TestLegacyDBAdapterClientMethods(t *testing.T) { | ||
| ctx := context.Background() | ||
| db := setupTestDB(t) | ||
| adapter := NewLegacyDBAdapter(db) | ||
|
|
||
| // Create client | ||
| client := &fosite.DefaultClient{ID: "c1", Secret: []byte("secret"), RedirectURIs: []string{"http://localhost"}} | ||
| if err := adapter.CreateClient(ctx, client); err != nil { | ||
| t.Fatalf("CreateClient failed: %v", err) | ||
| } | ||
|
|
||
| // Get client | ||
| got, err := adapter.GetClient(ctx, "c1") | ||
| if err != nil { | ||
| t.Fatalf("GetClient failed: %v", err) | ||
| } | ||
| if got.GetID() != "c1" { | ||
| t.Errorf("expected id c1 got %s", got.GetID()) | ||
| } | ||
|
|
||
| // Update client | ||
| client.RedirectURIs = []string{"http://127.0.0.1"} | ||
| if err := adapter.UpdateClient(ctx, client); err != nil { | ||
| t.Fatalf("UpdateClient failed: %v", err) | ||
| } | ||
|
|
||
| // Delete client | ||
| if err := adapter.DeleteClient(ctx, "c1"); err != nil { | ||
| t.Fatalf("DeleteClient failed: %v", err) | ||
| } | ||
| if _, err := adapter.GetClient(ctx, "c1"); !errors.Is(err, fosite.ErrNotFound) { | ||
| t.Errorf("expected not found after delete got %v", err) | ||
| } | ||
| } | ||
|
|
||
| func TestLegacyDBAdapterTokenMethods(t *testing.T) { | ||
| ctx := context.Background() | ||
| db := setupTestDB(t) | ||
| adapter := NewLegacyDBAdapter(db) | ||
|
|
||
| // create token | ||
| payload := []byte("data") | ||
| if err := adapter.CreateToken(ctx, "access_token", "sig1", "client", payload); err != nil { | ||
| t.Fatalf("CreateToken failed: %v", err) | ||
| } | ||
| // get token | ||
| got, err := adapter.GetToken(ctx, "access_token", "sig1") | ||
| if err != nil { | ||
| t.Fatalf("GetToken failed: %v", err) | ||
| } | ||
| if string(got.([]byte)) != "data" { | ||
| t.Errorf("unexpected token data: %v", got) | ||
| } | ||
| // revoke | ||
| if err := adapter.RevokeToken(ctx, "access_token", "sig1"); err != nil { | ||
| t.Fatalf("RevokeToken failed: %v", err) | ||
| } | ||
| // delete | ||
| if err := adapter.DeleteToken(ctx, "access_token", "sig1"); err != nil { | ||
| t.Fatalf("DeleteToken failed: %v", err) | ||
| } | ||
| if _, err := adapter.GetToken(ctx, "access_token", "sig1"); !errors.Is(err, fosite.ErrNotFound) { | ||
| t.Errorf("expected not found after delete got %v", err) | ||
| } | ||
| } | ||
|
|
||
| func TestLegacyDBAdapterSessionMethods(t *testing.T) { | ||
| ctx := context.Background() | ||
| db := setupTestDB(t) | ||
| adapter := NewLegacyDBAdapter(db) | ||
|
|
||
| data := []byte("session") | ||
| if err := adapter.CreateSession(ctx, "openid", "s1", data); err != nil { | ||
| t.Fatalf("CreateSession failed: %v", err) | ||
| } | ||
| got, err := adapter.GetSession(ctx, "openid", "s1") | ||
| if err != nil { | ||
| t.Fatalf("GetSession failed: %v", err) | ||
| } | ||
| if string(got.([]byte)) != "session" { | ||
| t.Errorf("unexpected session data: %v", got) | ||
| } | ||
| if err := adapter.DeleteSession(ctx, "openid", "s1"); err != nil { | ||
| t.Fatalf("DeleteSession failed: %v", err) | ||
| } | ||
| if _, err := adapter.GetSession(ctx, "openid", "s1"); !errors.Is(err, fosite.ErrNotFound) { | ||
| t.Errorf("expected not found after delete got %v", err) | ||
| } | ||
| } | ||
|
|
||
| func TestLegacyDBAdapterJWTMethods(t *testing.T) { | ||
| ctx := context.Background() | ||
| db := setupTestDB(t) | ||
| adapter := NewLegacyDBAdapter(db) | ||
|
|
||
| // validate new jti | ||
| if err := adapter.ValidateJWT(ctx, "j1"); err != nil { | ||
| t.Fatalf("ValidateJWT unexpected: %v", err) | ||
| } | ||
| // mark used | ||
| if err := adapter.MarkJWTAsUsed(ctx, "j1", time.Now().Add(time.Hour)); err != nil { | ||
| t.Fatalf("MarkJWTAsUsed failed: %v", err) | ||
| } | ||
| // now validate should fail | ||
| if err := adapter.ValidateJWT(ctx, "j1"); !errors.Is(err, fosite.ErrJTIKnown) { | ||
| t.Errorf("expected JTIKnown after mark used got %v", err) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "net/url" | ||
| "strings" | ||
| "testing" | ||
|
|
||
| _ "github.com/mattn/go-sqlite3" | ||
| "github.com/ory/fosite" | ||
| "github.com/ory/fosite/handler/openid" | ||
| "github.com/ory/fosite/token/jwt" | ||
| ) | ||
|
|
||
| // setupLegacyStore initializes the global store with a LoggingAdapter over LegacyDBAdapter. | ||
|
||
| func setupLegacyStore(t *testing.T) func() { | ||
| db := setupTestDB(t) | ||
| orig := store | ||
| adapter := NewLegacyDBAdapter(db) | ||
| // seed client similar to in-memory store | ||
| client := &fosite.DefaultClient{ | ||
| ID: "my-test-client", | ||
| Secret: []byte("foobar"), | ||
| RedirectURIs: []string{"http://localhost:3000/callback", "http://127.0.0.1:3000/callback"}, | ||
| GrantTypes: fosite.Arguments{"authorization_code", "refresh_token", "client_credentials"}, | ||
| ResponseTypes: fosite.Arguments{"code", "token", "id_token", "code id_token", "code token", "id_token token", "code id_token token"}, | ||
| Scopes: fosite.Arguments{"openid", "profile", "email", "offline"}, | ||
| Audience: fosite.Arguments{"https://my-api.com"}, | ||
| } | ||
| if err := adapter.CreateClient(context.Background(), client); err != nil { | ||
| t.Fatalf("seed client: %v", err) | ||
| } | ||
| store = adapter | ||
| return func() { store = orig } | ||
| } | ||
|
|
||
| // TestFullFlowLegacyDB simulates login, consent, token issuance and validation using the legacy DB. | ||
| func TestFullFlowLegacyDB(t *testing.T) { | ||
| teardown := setupLegacyStore(t) | ||
| defer teardown() | ||
|
|
||
| // mimic authorization code flow using fosite directly | ||
| router := setupRouter() | ||
| srv := httptest.NewServer(router) | ||
| defer srv.Close() | ||
|
|
||
| // create authorize request manually | ||
| arReq, _ := http.NewRequest("GET", srv.URL+"/oauth2/auth?response_type=code&client_id=my-test-client&redirect_uri=http://localhost:3000/callback&scope=openid+profile+offline&state=12345678", nil) | ||
| ar, err := oauth2Provider.NewAuthorizeRequest(arReq.Context(), arReq) | ||
| if err != nil { | ||
| t.Fatalf("authorize request: %v", err) | ||
| } | ||
| ar.GrantScope("openid") | ||
| ar.GrantScope("profile") | ||
| ar.GrantScope("offline") | ||
| sess := &openid.DefaultSession{Claims: &jwt.IDTokenClaims{Subject: "user"}, Headers: &jwt.Headers{}, Subject: "user"} | ||
| resp, err := oauth2Provider.NewAuthorizeResponse(arReq.Context(), ar, sess) | ||
| if err != nil { | ||
| t.Fatalf("authorize response: %v", err) | ||
| } | ||
| recorder := httptest.NewRecorder() | ||
| oauth2Provider.WriteAuthorizeResponse(arReq.Context(), recorder, ar, resp) | ||
| location, _ := recorder.Result().Location() | ||
| code := location.Query().Get("code") | ||
|
|
||
| data := url.Values{} | ||
| data.Set("grant_type", "authorization_code") | ||
| data.Set("code", code) | ||
| data.Set("redirect_uri", "http://localhost:3000/callback") | ||
| tokenReq, _ := http.NewRequest("POST", srv.URL+"/oauth2/token", strings.NewReader(data.Encode())) | ||
| tokenReq.SetBasicAuth("my-test-client", "foobar") | ||
| tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
| resToken, err := http.DefaultClient.Do(tokenReq) | ||
| if err != nil { | ||
| t.Fatalf("token request err: %v", err) | ||
| } | ||
| var tokenResp map[string]interface{} | ||
| json.NewDecoder(resToken.Body).Decode(&tokenResp) | ||
| resToken.Body.Close() | ||
|
|
||
| access := tokenResp["access_token"].(string) | ||
|
|
||
| introspect := url.Values{"token": {access}} | ||
| introReq, _ := http.NewRequest("POST", srv.URL+"/oauth2/introspect", strings.NewReader(introspect.Encode())) | ||
| introReq.SetBasicAuth("my-test-client", "foobar") | ||
| introReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
| resIntro, err := http.DefaultClient.Do(introReq) | ||
| if err != nil { | ||
| t.Fatalf("introspect err: %v", err) | ||
| } | ||
| var introResp map[string]interface{} | ||
| json.NewDecoder(resIntro.Body).Decode(&introResp) | ||
| resIntro.Body.Close() | ||
| if active, ok := introResp["active"].(bool); !ok || !active { | ||
| t.Errorf("token not active: %v", introResp) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The benchmark setup (opening DB and creating tables) is included in the measurement; to focus the benchmark on token creation only, call b.ResetTimer() after setup and before the measurement loop.