mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-12 04:04:26 +00:00
Cleanup [dec2025] (#101)
* Cleanup excessive comments. * Remove leftovers hanging around from previous refactor * Improve test coverage
This commit is contained in:
@@ -0,0 +1,620 @@
|
||||
package traefikoidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lukaszraczylo/traefikoidc/internal/testutil"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// ClockSkewEdgeCasesSuite tests clock skew tolerance scenarios
|
||||
type ClockSkewEdgeCasesSuite struct {
|
||||
suite.Suite
|
||||
|
||||
fixture *testutil.TokenFixture
|
||||
tOidc *TraefikOidc
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) SetupSuite() {
|
||||
var err error
|
||||
s.fixture, err = testutil.NewTokenFixture()
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) SetupTest() {
|
||||
// Create JWK for the test key
|
||||
jwk := JWK{
|
||||
Kty: "RSA",
|
||||
Kid: s.fixture.KeyID,
|
||||
Alg: "RS256",
|
||||
N: base64.RawURLEncoding.EncodeToString(s.fixture.RSAPublicKey.N.Bytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(bigIntToBytes(big.NewInt(int64(s.fixture.RSAPublicKey.E)))),
|
||||
}
|
||||
|
||||
jwkCache := &MockJWKCache{
|
||||
JWKS: &JWKSet{Keys: []JWK{jwk}},
|
||||
Err: nil,
|
||||
}
|
||||
|
||||
tokenBlacklist := NewCache()
|
||||
tokenCacheInternal := NewCache()
|
||||
tokenCache := &TokenCache{}
|
||||
if tokenCache.cache == nil {
|
||||
if wrapper, ok := tokenCacheInternal.(*CacheInterfaceWrapper); ok {
|
||||
tokenCache.cache = wrapper.cache
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger("error") // Reduce noise
|
||||
|
||||
s.tOidc = &TraefikOidc{
|
||||
issuerURL: s.fixture.Issuer,
|
||||
clientID: s.fixture.Audience,
|
||||
audience: s.fixture.Audience,
|
||||
clientSecret: "test-client-secret",
|
||||
roleClaimName: "roles",
|
||||
groupClaimName: "groups",
|
||||
userIdentifierClaim: "email",
|
||||
jwkCache: jwkCache,
|
||||
jwksURL: "https://test-jwks-url.com",
|
||||
limiter: rate.NewLimiter(rate.Every(time.Second), 10),
|
||||
tokenBlacklist: tokenBlacklist,
|
||||
tokenCache: tokenCache,
|
||||
logger: logger,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
extractClaimsFunc: extractClaims,
|
||||
initComplete: make(chan struct{}),
|
||||
goroutineWG: &sync.WaitGroup{},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
close(s.tOidc.initComplete)
|
||||
s.tOidc.tokenVerifier = s.tOidc
|
||||
s.tOidc.jwtVerifier = s.tOidc
|
||||
|
||||
s.T().Cleanup(func() {
|
||||
if s.tOidc.tokenBlacklist != nil {
|
||||
s.tOidc.tokenBlacklist.Close()
|
||||
}
|
||||
if s.tOidc.tokenCache != nil && s.tOidc.tokenCache.cache != nil {
|
||||
s.tOidc.tokenCache.cache.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) TestExactlyAtExpiry() {
|
||||
token, err := s.fixture.TokenWithSkew(0)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Token at exact expiry - behavior is implementation-defined
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.T().Logf("Exact expiry result: %v", err)
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) TestOneSecondBeforeExpiry() {
|
||||
token, err := s.fixture.TokenWithSkew(1 * time.Second)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Token should be valid 1 second before expiry")
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) TestOneSecondAfterExpiry() {
|
||||
token, err := s.fixture.TokenWithSkew(-1 * time.Second)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
// With default 2-minute clock skew tolerance, 1 second past expiry should still be valid
|
||||
s.NoError(err, "Token 1 second past expiry should be valid within clock skew tolerance")
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) TestWithinSkewTolerance() {
|
||||
// Most implementations allow 5-minute clock skew
|
||||
token, err := s.fixture.TokenWithSkew(-4 * time.Minute)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
// May pass or fail depending on implementation
|
||||
s.T().Logf("4-minute expired token result: %v", err)
|
||||
}
|
||||
|
||||
func (s *ClockSkewEdgeCasesSuite) TestBeyondSkewTolerance() {
|
||||
token, err := s.fixture.TokenWithSkew(-10 * time.Minute)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.Error(err, "Token should be invalid 10 minutes after expiry")
|
||||
}
|
||||
|
||||
func TestClockSkewEdgeCasesSuite(t *testing.T) {
|
||||
suite.Run(t, new(ClockSkewEdgeCasesSuite))
|
||||
}
|
||||
|
||||
// UnicodeClaimsSuite tests Unicode handling in JWT claims
|
||||
type UnicodeClaimsSuite struct {
|
||||
suite.Suite
|
||||
|
||||
fixture *testutil.TokenFixture
|
||||
tOidc *TraefikOidc
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) SetupSuite() {
|
||||
var err error
|
||||
s.fixture, err = testutil.NewTokenFixture()
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) SetupTest() {
|
||||
jwk := JWK{
|
||||
Kty: "RSA",
|
||||
Kid: s.fixture.KeyID,
|
||||
Alg: "RS256",
|
||||
N: base64.RawURLEncoding.EncodeToString(s.fixture.RSAPublicKey.N.Bytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(bigIntToBytes(big.NewInt(int64(s.fixture.RSAPublicKey.E)))),
|
||||
}
|
||||
|
||||
jwkCache := &MockJWKCache{
|
||||
JWKS: &JWKSet{Keys: []JWK{jwk}},
|
||||
Err: nil,
|
||||
}
|
||||
|
||||
tokenBlacklist := NewCache()
|
||||
tokenCacheInternal := NewCache()
|
||||
tokenCache := &TokenCache{}
|
||||
if tokenCache.cache == nil {
|
||||
if wrapper, ok := tokenCacheInternal.(*CacheInterfaceWrapper); ok {
|
||||
tokenCache.cache = wrapper.cache
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger("error")
|
||||
|
||||
s.tOidc = &TraefikOidc{
|
||||
issuerURL: s.fixture.Issuer,
|
||||
clientID: s.fixture.Audience,
|
||||
audience: s.fixture.Audience,
|
||||
clientSecret: "test-client-secret",
|
||||
roleClaimName: "roles",
|
||||
groupClaimName: "groups",
|
||||
userIdentifierClaim: "email",
|
||||
jwkCache: jwkCache,
|
||||
jwksURL: "https://test-jwks-url.com",
|
||||
limiter: rate.NewLimiter(rate.Every(time.Second), 10),
|
||||
tokenBlacklist: tokenBlacklist,
|
||||
tokenCache: tokenCache,
|
||||
logger: logger,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
extractClaimsFunc: extractClaims,
|
||||
initComplete: make(chan struct{}),
|
||||
goroutineWG: &sync.WaitGroup{},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
close(s.tOidc.initComplete)
|
||||
s.tOidc.tokenVerifier = s.tOidc
|
||||
s.tOidc.jwtVerifier = s.tOidc
|
||||
|
||||
s.T().Cleanup(func() {
|
||||
if s.tOidc.tokenBlacklist != nil {
|
||||
s.tOidc.tokenBlacklist.Close()
|
||||
}
|
||||
if s.tOidc.tokenCache != nil && s.tOidc.tokenCache.cache != nil {
|
||||
s.tOidc.tokenCache.cache.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) TestUnicodeEmail() {
|
||||
token, err := s.fixture.TokenWithEmail("用户@example.com")
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Unicode email should be handled correctly")
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) TestUnicodeName() {
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"name": "田中太郎",
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Unicode name should be handled correctly")
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) TestEmojiInClaims() {
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"name": "Test User 😀",
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Emoji in claims should be handled correctly")
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) TestRTLText() {
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"name": "مستخدم اختبار",
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "RTL text should be handled correctly")
|
||||
}
|
||||
|
||||
func (s *UnicodeClaimsSuite) TestMixedScripts() {
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"name": "Test 测试 テスト",
|
||||
"roles": []string{"admin", "管理者", "管理员"},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Mixed scripts should be handled correctly")
|
||||
}
|
||||
|
||||
func TestUnicodeClaimsSuite(t *testing.T) {
|
||||
suite.Run(t, new(UnicodeClaimsSuite))
|
||||
}
|
||||
|
||||
// LargeClaimsSuite tests large claim values
|
||||
type LargeClaimsSuite struct {
|
||||
suite.Suite
|
||||
|
||||
fixture *testutil.TokenFixture
|
||||
tOidc *TraefikOidc
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) SetupSuite() {
|
||||
var err error
|
||||
s.fixture, err = testutil.NewTokenFixture()
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) SetupTest() {
|
||||
jwk := JWK{
|
||||
Kty: "RSA",
|
||||
Kid: s.fixture.KeyID,
|
||||
Alg: "RS256",
|
||||
N: base64.RawURLEncoding.EncodeToString(s.fixture.RSAPublicKey.N.Bytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(bigIntToBytes(big.NewInt(int64(s.fixture.RSAPublicKey.E)))),
|
||||
}
|
||||
|
||||
jwkCache := &MockJWKCache{
|
||||
JWKS: &JWKSet{Keys: []JWK{jwk}},
|
||||
Err: nil,
|
||||
}
|
||||
|
||||
tokenBlacklist := NewCache()
|
||||
tokenCacheInternal := NewCache()
|
||||
tokenCache := &TokenCache{}
|
||||
if tokenCache.cache == nil {
|
||||
if wrapper, ok := tokenCacheInternal.(*CacheInterfaceWrapper); ok {
|
||||
tokenCache.cache = wrapper.cache
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger("error")
|
||||
|
||||
s.tOidc = &TraefikOidc{
|
||||
issuerURL: s.fixture.Issuer,
|
||||
clientID: s.fixture.Audience,
|
||||
audience: s.fixture.Audience,
|
||||
clientSecret: "test-client-secret",
|
||||
roleClaimName: "roles",
|
||||
groupClaimName: "groups",
|
||||
userIdentifierClaim: "email",
|
||||
jwkCache: jwkCache,
|
||||
jwksURL: "https://test-jwks-url.com",
|
||||
limiter: rate.NewLimiter(rate.Every(time.Second), 10),
|
||||
tokenBlacklist: tokenBlacklist,
|
||||
tokenCache: tokenCache,
|
||||
logger: logger,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
extractClaimsFunc: extractClaims,
|
||||
initComplete: make(chan struct{}),
|
||||
goroutineWG: &sync.WaitGroup{},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
close(s.tOidc.initComplete)
|
||||
s.tOidc.tokenVerifier = s.tOidc
|
||||
s.tOidc.jwtVerifier = s.tOidc
|
||||
|
||||
s.T().Cleanup(func() {
|
||||
if s.tOidc.tokenBlacklist != nil {
|
||||
s.tOidc.tokenBlacklist.Close()
|
||||
}
|
||||
if s.tOidc.tokenCache != nil && s.tOidc.tokenCache.cache != nil {
|
||||
s.tOidc.tokenCache.cache.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) TestManyRoles() {
|
||||
roles := make([]string, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
roles[i] = strings.Repeat("role", 10) + string(rune('A'+i%26))
|
||||
}
|
||||
|
||||
token, err := s.fixture.TokenWithRoles(roles)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Token with 100 roles should be handled")
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) TestManyGroups() {
|
||||
groups := make([]string, 50)
|
||||
for i := 0; i < 50; i++ {
|
||||
groups[i] = strings.Repeat("group", 5) + string(rune('A'+i%26))
|
||||
}
|
||||
|
||||
token, err := s.fixture.TokenWithGroups(groups)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Token with 50 groups should be handled")
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) TestLongEmail() {
|
||||
// RFC 5321 allows up to 254 characters
|
||||
localPart := strings.Repeat("a", 64)
|
||||
domain := strings.Repeat("b", 63) + ".com"
|
||||
email := localPart + "@" + domain
|
||||
|
||||
token, err := s.fixture.TokenWithEmail(email)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Token with long email should be handled")
|
||||
}
|
||||
|
||||
func (s *LargeClaimsSuite) TestLongSubject() {
|
||||
longSub := strings.Repeat("subject", 100)
|
||||
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"sub": longSub,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.tOidc.VerifyToken(token)
|
||||
s.NoError(err, "Token with long subject should be handled")
|
||||
}
|
||||
|
||||
func TestLargeClaimsSuite(t *testing.T) {
|
||||
suite.Run(t, new(LargeClaimsSuite))
|
||||
}
|
||||
|
||||
// URLPathEdgeCasesSuite tests URL handling edge cases
|
||||
type URLPathEdgeCasesSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (s *URLPathEdgeCasesSuite) TestVeryLongPath() {
|
||||
longPath := "/" + strings.Repeat("segment/", 100)
|
||||
req := httptest.NewRequest("GET", longPath, nil)
|
||||
|
||||
s.NotNil(req)
|
||||
s.Contains(req.URL.Path, "segment")
|
||||
}
|
||||
|
||||
func (s *URLPathEdgeCasesSuite) TestSpecialCharactersInPath() {
|
||||
paths := []string{
|
||||
"/path%20with%20spaces",
|
||||
"/path/with/日本語",
|
||||
"/path?query=value&another=test",
|
||||
"/path#fragment",
|
||||
"/path/../traversal",
|
||||
"/path/./current",
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
s.Run(path, func() {
|
||||
req := httptest.NewRequest("GET", path, nil)
|
||||
s.NotNil(req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *URLPathEdgeCasesSuite) TestEmptyPath() {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
s.Equal("/", req.URL.Path)
|
||||
}
|
||||
|
||||
func (s *URLPathEdgeCasesSuite) TestDoubleSlashes() {
|
||||
req := httptest.NewRequest("GET", "//double//slashes//", nil)
|
||||
s.NotNil(req)
|
||||
}
|
||||
|
||||
func TestURLPathEdgeCasesSuite(t *testing.T) {
|
||||
suite.Run(t, new(URLPathEdgeCasesSuite))
|
||||
}
|
||||
|
||||
// ConcurrencyEdgeCasesSuite tests concurrency scenarios
|
||||
type ConcurrencyEdgeCasesSuite struct {
|
||||
suite.Suite
|
||||
|
||||
fixture *testutil.TokenFixture
|
||||
tOidc *TraefikOidc
|
||||
}
|
||||
|
||||
func (s *ConcurrencyEdgeCasesSuite) SetupSuite() {
|
||||
var err error
|
||||
s.fixture, err = testutil.NewTokenFixture()
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *ConcurrencyEdgeCasesSuite) SetupTest() {
|
||||
jwk := JWK{
|
||||
Kty: "RSA",
|
||||
Kid: s.fixture.KeyID,
|
||||
Alg: "RS256",
|
||||
N: base64.RawURLEncoding.EncodeToString(s.fixture.RSAPublicKey.N.Bytes()),
|
||||
E: base64.RawURLEncoding.EncodeToString(bigIntToBytes(big.NewInt(int64(s.fixture.RSAPublicKey.E)))),
|
||||
}
|
||||
|
||||
jwkCache := &MockJWKCache{
|
||||
JWKS: &JWKSet{Keys: []JWK{jwk}},
|
||||
Err: nil,
|
||||
}
|
||||
|
||||
tokenBlacklist := NewCache()
|
||||
tokenCacheInternal := NewCache()
|
||||
tokenCache := &TokenCache{}
|
||||
if tokenCache.cache == nil {
|
||||
if wrapper, ok := tokenCacheInternal.(*CacheInterfaceWrapper); ok {
|
||||
tokenCache.cache = wrapper.cache
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger("error")
|
||||
|
||||
s.tOidc = &TraefikOidc{
|
||||
issuerURL: s.fixture.Issuer,
|
||||
clientID: s.fixture.Audience,
|
||||
audience: s.fixture.Audience,
|
||||
clientSecret: "test-client-secret",
|
||||
roleClaimName: "roles",
|
||||
groupClaimName: "groups",
|
||||
userIdentifierClaim: "email",
|
||||
jwkCache: jwkCache,
|
||||
jwksURL: "https://test-jwks-url.com",
|
||||
limiter: rate.NewLimiter(rate.Every(time.Second), 100), // Higher limit for concurrency tests
|
||||
tokenBlacklist: tokenBlacklist,
|
||||
tokenCache: tokenCache,
|
||||
logger: logger,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
extractClaimsFunc: extractClaims,
|
||||
initComplete: make(chan struct{}),
|
||||
goroutineWG: &sync.WaitGroup{},
|
||||
ctx: context.Background(),
|
||||
}
|
||||
close(s.tOidc.initComplete)
|
||||
s.tOidc.tokenVerifier = s.tOidc
|
||||
s.tOidc.jwtVerifier = s.tOidc
|
||||
|
||||
s.T().Cleanup(func() {
|
||||
if s.tOidc.tokenBlacklist != nil {
|
||||
s.tOidc.tokenBlacklist.Close()
|
||||
}
|
||||
if s.tOidc.tokenCache != nil && s.tOidc.tokenCache.cache != nil {
|
||||
s.tOidc.tokenCache.cache.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *ConcurrencyEdgeCasesSuite) TestConcurrentTokenValidation() {
|
||||
token, err := s.fixture.ValidToken(nil)
|
||||
s.Require().NoError(err)
|
||||
|
||||
const goroutines = 50
|
||||
var wg sync.WaitGroup
|
||||
errors := make(chan error, goroutines)
|
||||
|
||||
for i := 0; i < goroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := s.tOidc.VerifyToken(token); err != nil {
|
||||
errors <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errors)
|
||||
|
||||
var errCount int
|
||||
for err := range errors {
|
||||
s.T().Logf("Concurrent error: %v", err)
|
||||
errCount++
|
||||
}
|
||||
|
||||
s.Equal(0, errCount, "All concurrent validations should succeed")
|
||||
}
|
||||
|
||||
func (s *ConcurrencyEdgeCasesSuite) TestConcurrentDifferentTokens() {
|
||||
const goroutines = 20
|
||||
var wg sync.WaitGroup
|
||||
errors := make(chan error, goroutines)
|
||||
|
||||
for i := 0; i < goroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
token, err := s.fixture.TokenWithCustomClaims(map[string]interface{}{
|
||||
"custom": idx,
|
||||
})
|
||||
if err != nil {
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
if err := s.tOidc.VerifyToken(token); err != nil {
|
||||
errors <- err
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errors)
|
||||
|
||||
var errCount int
|
||||
for err := range errors {
|
||||
s.T().Logf("Concurrent different token error: %v", err)
|
||||
errCount++
|
||||
}
|
||||
|
||||
s.Equal(0, errCount, "All concurrent different token validations should succeed")
|
||||
}
|
||||
|
||||
func (s *ConcurrencyEdgeCasesSuite) TestConcurrentMixedValidInvalid() {
|
||||
validToken, err := s.fixture.ValidToken(nil)
|
||||
s.Require().NoError(err)
|
||||
expiredToken, err := s.fixture.ExpiredToken()
|
||||
s.Require().NoError(err)
|
||||
|
||||
const goroutines = 40
|
||||
var wg sync.WaitGroup
|
||||
validCount := int32(0)
|
||||
expiredCount := int32(0)
|
||||
|
||||
for i := 0; i < goroutines; i++ {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
var token string
|
||||
if idx%2 == 0 {
|
||||
token = validToken
|
||||
} else {
|
||||
token = expiredToken
|
||||
}
|
||||
|
||||
err := s.tOidc.VerifyToken(token)
|
||||
if idx%2 == 0 {
|
||||
if err == nil {
|
||||
atomic.AddInt32(&validCount, 1)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
atomic.AddInt32(&expiredCount, 1)
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
s.T().Logf("Valid passed: %d, Expired rejected: %d", validCount, expiredCount)
|
||||
}
|
||||
|
||||
func TestConcurrencyEdgeCasesSuite(t *testing.T) {
|
||||
suite.Run(t, new(ConcurrencyEdgeCasesSuite))
|
||||
}
|
||||
Reference in New Issue
Block a user