Files
cms-server/internal/authtoken/authtoken_test.go
Asai Neko 210b8b08ce
All checks were successful
Server Check Build (NixCN CMS) TeamCity build finished
Add test for all components
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-03-26 18:19:26 +08:00

208 lines
4.7 KiB
Go

package authtoken
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"nixcn-cms/data"
"nixcn-cms/testutil"
)
func makeToken() Token {
return Token{Application: "test-app"}
}
// seedClient persists a client row whose encrypted secret decrypts to the
// plaintext given by testutil.TestClientSecret.
func seedTestClient(t *testing.T) {
t.Helper()
testutil.SeedClient(t)
}
func TestGenerateRefreshToken(t *testing.T) {
tok := makeToken()
r1, err := tok.GenerateRefreshToken()
require.NoError(t, err)
assert.NotEmpty(t, r1)
r2, err := tok.GenerateRefreshToken()
require.NoError(t, err)
assert.NotEqual(t, r1, r2, "refresh tokens must be unique")
}
func TestIssueTokens(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
access, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
assert.NotEmpty(t, access)
assert.NotEmpty(t, refresh)
}
func TestRefreshAccessToken(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
newAccess, err := tok.RefreshAccessToken(ctx, refresh)
require.NoError(t, err)
assert.NotEmpty(t, newAccess)
}
func TestRefreshAccessTokenInvalid(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
tok := makeToken()
_, err := tok.RefreshAccessToken(ctx, "invalid-refresh-token")
require.Error(t, err)
}
func TestRenewRefreshToken(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
_, old, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
newRefresh, err := tok.RenewRefreshToken(ctx, old)
require.NoError(t, err)
assert.NotEmpty(t, newRefresh)
assert.NotEqual(t, old, newRefresh)
// Old token should no longer be valid
_, err = tok.RefreshAccessToken(ctx, old)
require.Error(t, err)
}
func TestRevokeRefreshToken(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
require.NoError(t, tok.RevokeRefreshToken(ctx, refresh))
_, err = tok.RefreshAccessToken(ctx, refresh)
require.Error(t, err)
}
func TestRevokeNonExistentTokenNoError(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
tok := makeToken()
// should not fail even if token does not exist
assert.NoError(t, tok.RevokeRefreshToken(ctx, "does-not-exist"))
}
func TestHeaderVerifyValid(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
access, _, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
uid, err := tok.HeaderVerify(ctx, "Bearer "+access)
require.NoError(t, err)
assert.Equal(t, userId.String(), uid)
}
func TestHeaderVerifyEmpty(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
tok := makeToken()
uid, err := tok.HeaderVerify(ctx, "")
require.Error(t, err)
assert.Empty(t, uid)
}
func TestHeaderVerifyMalformed(t *testing.T) {
testutil.Setup(t)
ctx := context.Background()
tok := makeToken()
_, err := tok.HeaderVerify(ctx, "NotBearer token")
require.Error(t, err)
}
func TestHeaderVerifyExpiredToken(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
// Use a very short access TTL
viper.Set("ttl.access_ttl", -1*time.Second)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
access, _, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
_, err = tok.HeaderVerify(ctx, "Bearer "+access)
require.Error(t, err)
}
func TestNewClaims(t *testing.T) {
tok := makeToken()
userId := uuid.New()
claims := tok.NewClaims(testutil.TestClientID, userId)
assert.Equal(t, testutil.TestClientID, claims.ClientId)
assert.Equal(t, userId, claims.UserID)
assert.Equal(t, "test-app", claims.Issuer)
}
// Verify that IssueTokens registers the refresh token in the user and
// client index sets in Redis.
func TestIssueTokensRedisIndex(t *testing.T) {
testutil.Setup(t)
seedTestClient(t)
ctx := context.Background()
userId := uuid.New()
tok := makeToken()
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
require.NoError(t, err)
userSetKey := "user:" + userId.String() + ":refresh_tokens"
isMember, err := data.Redis.SIsMember(ctx, userSetKey, refresh).Result()
require.NoError(t, err)
assert.True(t, isMember)
}