Skip to content

Commit bb1ac7f

Browse files
Merge pull request #42 from wagnerdevocelot/2br281-codex/add-unit-and-integration-tests
Expand test suite with adapter and DB coverage
2 parents f418bf2 + cbec511 commit bb1ac7f

6 files changed

Lines changed: 482 additions & 0 deletions

File tree

benchmark_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"strconv"
7+
"testing"
8+
9+
_ "github.com/mattn/go-sqlite3"
10+
"github.com/ory/fosite"
11+
)
12+
13+
func BenchmarkLoggingAdapterCreateToken(b *testing.B) {
14+
adapter := NewLoggingAdapter(NewInMemoryStore())
15+
req := &fosite.Request{}
16+
for i := 0; i < b.N; i++ {
17+
adapter.CreateToken(context.Background(), "access_token", "sig"+strconv.Itoa(i), "c1", req)
18+
}
19+
}
20+
21+
func BenchmarkLegacyDBCreateToken(b *testing.B) {
22+
db, err := sql.Open("sqlite3", ":memory:")
23+
if err != nil {
24+
b.Fatal(err)
25+
}
26+
stmts := []string{
27+
`CREATE TABLE tokens (signature TEXT PRIMARY KEY, client_id TEXT, token_type TEXT, data BLOB, revoked_at TIMESTAMP)`,
28+
}
29+
for _, s := range stmts {
30+
if _, err := db.Exec(s); err != nil {
31+
b.Fatal(err)
32+
}
33+
}
34+
adapter := NewLegacyDBAdapter(db)
35+
req := []byte("data")
36+
for i := 0; i < b.N; i++ {
37+
adapter.CreateToken(context.Background(), "access_token", "sig"+strconv.Itoa(i), "c1", req)
38+
}
39+
}

db_adapter_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"errors"
7+
"testing"
8+
"time"
9+
10+
_ "github.com/mattn/go-sqlite3"
11+
"github.com/ory/fosite"
12+
)
13+
14+
func setupTestDB(t *testing.T) *sql.DB {
15+
db, err := sql.Open("sqlite3", ":memory:")
16+
if err != nil {
17+
t.Fatalf("failed to open sqlite: %v", err)
18+
}
19+
// create tables
20+
statements := []string{
21+
`CREATE TABLE clients (id TEXT PRIMARY KEY, secret TEXT, redirect_uris TEXT, scopes TEXT, is_public BOOLEAN)`,
22+
`CREATE TABLE tokens (signature TEXT PRIMARY KEY, client_id TEXT, token_type TEXT, data BLOB, revoked_at TIMESTAMP)`,
23+
`CREATE TABLE sessions (id TEXT PRIMARY KEY, session_type TEXT, data BLOB)`,
24+
`CREATE TABLE used_jtis (jti TEXT PRIMARY KEY, expires_at TIMESTAMP)`,
25+
}
26+
for _, stmt := range statements {
27+
if _, err := db.Exec(stmt); err != nil {
28+
t.Fatalf("failed to create table: %v", err)
29+
}
30+
}
31+
return db
32+
}
33+
34+
func TestLegacyDBAdapterClientMethods(t *testing.T) {
35+
ctx := context.Background()
36+
db := setupTestDB(t)
37+
adapter := NewLegacyDBAdapter(db)
38+
39+
// Create client
40+
client := &fosite.DefaultClient{ID: "c1", Secret: []byte("secret"), RedirectURIs: []string{"http://localhost"}}
41+
if err := adapter.CreateClient(ctx, client); err != nil {
42+
t.Fatalf("CreateClient failed: %v", err)
43+
}
44+
45+
// Get client
46+
got, err := adapter.GetClient(ctx, "c1")
47+
if err != nil {
48+
t.Fatalf("GetClient failed: %v", err)
49+
}
50+
if got.GetID() != "c1" {
51+
t.Errorf("expected id c1 got %s", got.GetID())
52+
}
53+
54+
// Update client
55+
client.RedirectURIs = []string{"http://127.0.0.1"}
56+
if err := adapter.UpdateClient(ctx, client); err != nil {
57+
t.Fatalf("UpdateClient failed: %v", err)
58+
}
59+
60+
// Delete client
61+
if err := adapter.DeleteClient(ctx, "c1"); err != nil {
62+
t.Fatalf("DeleteClient failed: %v", err)
63+
}
64+
if _, err := adapter.GetClient(ctx, "c1"); !errors.Is(err, fosite.ErrNotFound) {
65+
t.Errorf("expected not found after delete got %v", err)
66+
}
67+
}
68+
69+
func TestLegacyDBAdapterTokenMethods(t *testing.T) {
70+
ctx := context.Background()
71+
db := setupTestDB(t)
72+
adapter := NewLegacyDBAdapter(db)
73+
74+
// create token
75+
payload := []byte("data")
76+
if err := adapter.CreateToken(ctx, "access_token", "sig1", "client", payload); err != nil {
77+
t.Fatalf("CreateToken failed: %v", err)
78+
}
79+
// get token
80+
got, err := adapter.GetToken(ctx, "access_token", "sig1")
81+
if err != nil {
82+
t.Fatalf("GetToken failed: %v", err)
83+
}
84+
if string(got.([]byte)) != "data" {
85+
t.Errorf("unexpected token data: %v", got)
86+
}
87+
// revoke
88+
if err := adapter.RevokeToken(ctx, "access_token", "sig1"); err != nil {
89+
t.Fatalf("RevokeToken failed: %v", err)
90+
}
91+
// delete
92+
if err := adapter.DeleteToken(ctx, "access_token", "sig1"); err != nil {
93+
t.Fatalf("DeleteToken failed: %v", err)
94+
}
95+
if _, err := adapter.GetToken(ctx, "access_token", "sig1"); !errors.Is(err, fosite.ErrNotFound) {
96+
t.Errorf("expected not found after delete got %v", err)
97+
}
98+
}
99+
100+
func TestLegacyDBAdapterSessionMethods(t *testing.T) {
101+
ctx := context.Background()
102+
db := setupTestDB(t)
103+
adapter := NewLegacyDBAdapter(db)
104+
105+
data := []byte("session")
106+
if err := adapter.CreateSession(ctx, "openid", "s1", data); err != nil {
107+
t.Fatalf("CreateSession failed: %v", err)
108+
}
109+
got, err := adapter.GetSession(ctx, "openid", "s1")
110+
if err != nil {
111+
t.Fatalf("GetSession failed: %v", err)
112+
}
113+
if string(got.([]byte)) != "session" {
114+
t.Errorf("unexpected session data: %v", got)
115+
}
116+
if err := adapter.DeleteSession(ctx, "openid", "s1"); err != nil {
117+
t.Fatalf("DeleteSession failed: %v", err)
118+
}
119+
if _, err := adapter.GetSession(ctx, "openid", "s1"); !errors.Is(err, fosite.ErrNotFound) {
120+
t.Errorf("expected not found after delete got %v", err)
121+
}
122+
}
123+
124+
func TestLegacyDBAdapterJWTMethods(t *testing.T) {
125+
ctx := context.Background()
126+
db := setupTestDB(t)
127+
adapter := NewLegacyDBAdapter(db)
128+
129+
// validate new jti
130+
if err := adapter.ValidateJWT(ctx, "j1"); err != nil {
131+
t.Fatalf("ValidateJWT unexpected: %v", err)
132+
}
133+
// mark used
134+
if err := adapter.MarkJWTAsUsed(ctx, "j1", time.Now().Add(time.Hour)); err != nil {
135+
t.Fatalf("MarkJWTAsUsed failed: %v", err)
136+
}
137+
// now validate should fail
138+
if err := adapter.ValidateJWT(ctx, "j1"); !errors.Is(err, fosite.ErrJTIKnown) {
139+
t.Errorf("expected JTIKnown after mark used got %v", err)
140+
}
141+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module identity-go
33
go 1.22
44

55
require (
6+
github.com/mattn/go-sqlite3 v1.14.16
67
github.com/ory/fosite v0.49.0
78
golang.org/x/crypto v0.31.0
89
)

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
296296
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
297297
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
298298
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
299+
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
299300
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
300301
github.com/mattn/goveralls v0.0.12 h1:PEEeF0k1SsTjOBQ8FOmrOAoCu4ytuMaWCnWe94zxbCg=
301302
github.com/mattn/goveralls v0.0.12/go.mod h1:44ImGEUfmqH8bBtaMrYKsM65LXfNLWmwaxFGjZwgMSQ=

integration_legacy_db_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
"net/http/httptest"
8+
"net/url"
9+
"strings"
10+
"testing"
11+
12+
_ "github.com/mattn/go-sqlite3"
13+
"github.com/ory/fosite"
14+
"github.com/ory/fosite/handler/openid"
15+
"github.com/ory/fosite/token/jwt"
16+
)
17+
18+
// setupLegacyStore initializes the global store with a LoggingAdapter over LegacyDBAdapter.
19+
func setupLegacyStore(t *testing.T) func() {
20+
db := setupTestDB(t)
21+
orig := store
22+
adapter := NewLegacyDBAdapter(db)
23+
// seed client similar to in-memory store
24+
client := &fosite.DefaultClient{
25+
ID: "my-test-client",
26+
Secret: []byte("foobar"),
27+
RedirectURIs: []string{"http://localhost:3000/callback", "http://127.0.0.1:3000/callback"},
28+
GrantTypes: fosite.Arguments{"authorization_code", "refresh_token", "client_credentials"},
29+
ResponseTypes: fosite.Arguments{"code", "token", "id_token", "code id_token", "code token", "id_token token", "code id_token token"},
30+
Scopes: fosite.Arguments{"openid", "profile", "email", "offline"},
31+
Audience: fosite.Arguments{"https://my-api.com"},
32+
}
33+
if err := adapter.CreateClient(context.Background(), client); err != nil {
34+
t.Fatalf("seed client: %v", err)
35+
}
36+
store = adapter
37+
return func() { store = orig }
38+
}
39+
40+
// TestFullFlowLegacyDB simulates login, consent, token issuance and validation using the legacy DB.
41+
func TestFullFlowLegacyDB(t *testing.T) {
42+
teardown := setupLegacyStore(t)
43+
defer teardown()
44+
45+
// mimic authorization code flow using fosite directly
46+
router := setupRouter()
47+
srv := httptest.NewServer(router)
48+
defer srv.Close()
49+
50+
// create authorize request manually
51+
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)
52+
ar, err := oauth2Provider.NewAuthorizeRequest(arReq.Context(), arReq)
53+
if err != nil {
54+
t.Fatalf("authorize request: %v", err)
55+
}
56+
ar.GrantScope("openid")
57+
ar.GrantScope("profile")
58+
ar.GrantScope("offline")
59+
sess := &openid.DefaultSession{Claims: &jwt.IDTokenClaims{Subject: "user"}, Headers: &jwt.Headers{}, Subject: "user"}
60+
resp, err := oauth2Provider.NewAuthorizeResponse(arReq.Context(), ar, sess)
61+
if err != nil {
62+
t.Fatalf("authorize response: %v", err)
63+
}
64+
recorder := httptest.NewRecorder()
65+
oauth2Provider.WriteAuthorizeResponse(arReq.Context(), recorder, ar, resp)
66+
location, _ := recorder.Result().Location()
67+
code := location.Query().Get("code")
68+
69+
data := url.Values{}
70+
data.Set("grant_type", "authorization_code")
71+
data.Set("code", code)
72+
data.Set("redirect_uri", "http://localhost:3000/callback")
73+
tokenReq, _ := http.NewRequest("POST", srv.URL+"/oauth2/token", strings.NewReader(data.Encode()))
74+
tokenReq.SetBasicAuth("my-test-client", "foobar")
75+
tokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
76+
resToken, err := http.DefaultClient.Do(tokenReq)
77+
if err != nil {
78+
t.Fatalf("token request err: %v", err)
79+
}
80+
var tokenResp map[string]interface{}
81+
json.NewDecoder(resToken.Body).Decode(&tokenResp)
82+
resToken.Body.Close()
83+
84+
access := tokenResp["access_token"].(string)
85+
86+
introspect := url.Values{"token": {access}}
87+
introReq, _ := http.NewRequest("POST", srv.URL+"/oauth2/introspect", strings.NewReader(introspect.Encode()))
88+
introReq.SetBasicAuth("my-test-client", "foobar")
89+
introReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
90+
resIntro, err := http.DefaultClient.Do(introReq)
91+
if err != nil {
92+
t.Fatalf("introspect err: %v", err)
93+
}
94+
var introResp map[string]interface{}
95+
json.NewDecoder(resIntro.Body).Decode(&introResp)
96+
resIntro.Body.Close()
97+
if active, ok := introResp["active"].(bool); !ok || !active {
98+
t.Errorf("token not active: %v", introResp)
99+
}
100+
}

0 commit comments

Comments
 (0)