- dev auth flow (side-step OAuth) - db event processing integration tests - dev scripts (eg Makefile) - db / test db migration setup scripts.
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
package authentication
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"ruben/inventory2/consts"
|
|
"ruben/inventory2/internal/testdb"
|
|
)
|
|
|
|
func TestDevLogin(t *testing.T) {
|
|
pool := testdb.Pool(t)
|
|
auth := &Authenticator{db: pool}
|
|
ctx := context.Background()
|
|
|
|
userID := testdb.NewUserID(t)
|
|
t.Cleanup(func() {
|
|
pool.Exec(context.Background(), "DELETE FROM oauth_tokens WHERE id_token_subject = $1", userID)
|
|
pool.Exec(context.Background(), "DELETE FROM oauth_users WHERE user_id = $1", userID)
|
|
})
|
|
|
|
accessToken, expiration, err := auth.DevLogin(ctx, userID, "Test User")
|
|
if err != nil {
|
|
t.Fatalf("DevLogin() error = %v", err)
|
|
}
|
|
if accessToken == "" {
|
|
t.Fatal("DevLogin() returned an empty access token")
|
|
}
|
|
if !expiration.After(time.Now().Add(30 * 24 * time.Hour)) {
|
|
t.Errorf("DevLogin() expiration = %v, want something far enough out to avoid the near-expiry refresh path", expiration)
|
|
}
|
|
|
|
claims, err := auth.GetAccessTokenClaimsAndExpiration(ctx, accessToken)
|
|
if err != nil {
|
|
t.Fatalf("GetAccessTokenClaimsAndExpiration() error = %v", err)
|
|
}
|
|
if claims.Name != "Test User" {
|
|
t.Errorf("claims.Name = %q, want %q", claims.Name, "Test User")
|
|
}
|
|
// Postgres timestamptz has microsecond precision, so the round-tripped
|
|
// value loses the sub-microsecond portion of Go's nanosecond clock.
|
|
if diff := claims.Expiration.Sub(expiration); diff > time.Millisecond || diff < -time.Millisecond {
|
|
t.Errorf("claims.Expiration = %v, want ~%v (diff %v)", claims.Expiration, expiration, diff)
|
|
}
|
|
|
|
_, tokenType, err := auth.getRefreshTokenForAccessToken(ctx, accessToken)
|
|
if err != nil {
|
|
t.Fatalf("getRefreshTokenForAccessToken() error = %v", err)
|
|
}
|
|
if tokenType != "dev" {
|
|
t.Errorf("tokenType = %q, want %q", tokenType, "dev")
|
|
}
|
|
}
|
|
|
|
// DevLogin should be safe to call more than once for the same user_id -
|
|
// e.g. testing multiple times as the same dev identity - since oauth_users
|
|
// is keyed on user_id but each call mints its own oauth_tokens row.
|
|
func TestDevLogin_SameUserIDTwice(t *testing.T) {
|
|
pool := testdb.Pool(t)
|
|
auth := &Authenticator{db: pool}
|
|
ctx := context.Background()
|
|
|
|
userID := testdb.NewUserID(t)
|
|
t.Cleanup(func() {
|
|
pool.Exec(context.Background(), "DELETE FROM oauth_tokens WHERE id_token_subject = $1", userID)
|
|
pool.Exec(context.Background(), "DELETE FROM oauth_users WHERE user_id = $1", userID)
|
|
})
|
|
|
|
token1, _, err := auth.DevLogin(ctx, userID, "Test User")
|
|
if err != nil {
|
|
t.Fatalf("first DevLogin() error = %v", err)
|
|
}
|
|
|
|
token2, _, err := auth.DevLogin(ctx, userID, "Test User")
|
|
if err != nil {
|
|
t.Fatalf("second DevLogin() error = %v", err)
|
|
}
|
|
|
|
if token1 == token2 {
|
|
t.Fatalf("DevLogin() returned the same access token twice: %q", token1)
|
|
}
|
|
}
|
|
|
|
func TestGetAccessTokenClaimsAndExpiration_UnknownToken(t *testing.T) {
|
|
pool := testdb.Pool(t)
|
|
auth := &Authenticator{db: pool}
|
|
ctx := context.Background()
|
|
|
|
_, err := auth.GetAccessTokenClaimsAndExpiration(ctx, "no-such-token-"+testdb.NewUserID(t))
|
|
if !errors.Is(err, consts.ErrNotFound) {
|
|
t.Fatalf("GetAccessTokenClaimsAndExpiration() error = %v, want %v", err, consts.ErrNotFound)
|
|
}
|
|
}
|