Skip to content

Commit cead391

Browse files
committed
add token manager construct tests
1 parent 5b6c26c commit cead391

File tree

6 files changed

+117
-13
lines changed

6 files changed

+117
-13
lines changed

entraid.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
11
package entraid
2+
3+
const (
4+
DefaultExpirationRefreshRatio = 0.7
5+
DefaultRetryOptionsMaxAttempts = 3
6+
DefaultRetryOptionsInitialDelayMs = 1000
7+
DefaultRetryOptionsBackoffMultiplier = 2.0
8+
DefaultRetryOptionsMaxDelayMs = 10000
9+
MinTokenTTL = 60 * 1000 // 1 minute
10+
)

entraid_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package entraid
2+
3+
import (
4+
"github.com/stretchr/testify/mock"
5+
)
6+
7+
type mockIdentityProvider struct {
8+
// Mock implementation of the IdentityProvider interface
9+
// Add any necessary fields or methods for the mock identity provider here
10+
mock.Mock
11+
}
12+
13+
func (m *mockIdentityProvider) RequestToken() (IdentityProviderResponse, error) {
14+
args := m.Called()
15+
return args.Get(0).(IdentityProviderResponse), args.Error(1)
16+
}
17+
18+
// Ensure mockIdentityProvider implements the IdentityProvider interface
19+
var _ IdentityProvider = (*mockIdentityProvider)(nil)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
require (
1212
github.com/davecgh/go-spew v1.1.1 // indirect
1313
github.com/pmezard/go-difflib v1.0.0 // indirect
14+
github.com/stretchr/objx v0.5.2 // indirect
1415
gopkg.in/yaml.v3 v3.0.1 // indirect
1516
)
1617

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
2626
github.com/redis/go-redis/v9 v9.5.3-0.20250331212737-c248425ade4a h1:R5xgk8m+CF7lVE0EGr+tLkT1eM3Zfd39BJfnANQqpKA=
2727
github.com/redis/go-redis/v9 v9.5.3-0.20250331212737-c248425ade4a/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
2828
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
29+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
30+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
2931
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
3032
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
3133
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=

token_manager.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"github.com/golang-jwt/jwt/v5"
1212
)
1313

14-
const MinTokenTTL = 30 * time.Minute
15-
1614
// TokenManagerOptions is a struct that contains the options for the TokenManager.
1715
type TokenManagerOptions struct {
1816
// ExpirationRefreshRatio is the ratio of the token expiration time to refresh the token.
@@ -62,12 +60,10 @@ type RetryOptions struct {
6260
//
6361
// default: 1000 ms
6462
InitialDelayMs int
65-
6663
// MaxDelayMs is the maximum delay in milliseconds between retry attempts.
6764
//
6865
// default: 10000 ms
6966
MaxDelayMs int
70-
7167
// BackoffMultiplier is the multiplier for the backoff delay.
7268
// default: 2.0
7369
BackoffMultiplier float64
@@ -143,7 +139,7 @@ var defaultIdentityProviderResponseParser = func(response IdentityProviderRespon
143139
return nil, fmt.Errorf("expires on is in the past")
144140
}
145141
if time.Until(expiresOn) < MinTokenTTL {
146-
return nil, fmt.Errorf("expires on is less than minimum token TTL")
142+
return nil, fmt.Errorf("expires on is less than minimum token TTL which is %d", MinTokenTTL)
147143
}
148144
// parse token as jwt token and get claims
149145

@@ -162,10 +158,10 @@ var defaultIdentityProviderResponseParser = func(response IdentityProviderRespon
162158
// The IdentityProvider is used to obtain the token, and the TokenManagerOptions contains options for the TokenManager.
163159
// The TokenManager is responsible for managing the token and refreshing it when necessary.
164160
func NewTokenManager(idp IdentityProvider, options TokenManagerOptions) (TokenManager, error) {
165-
options = defaultTokenManagerOptionsOr(options)
166-
if options.ExpirationRefreshRatio <= 0 || options.ExpirationRefreshRatio > 1 {
161+
if options.ExpirationRefreshRatio < 0 || options.ExpirationRefreshRatio > 1 {
167162
return nil, fmt.Errorf("expiration refresh ratio must be between 0 and 1")
168163
}
164+
options = defaultTokenManagerOptionsOr(options)
169165

170166
if idp == nil {
171167
return nil, fmt.Errorf("identity provider is required")
@@ -397,16 +393,16 @@ func defaultRetryOptionsOr(retryOptions RetryOptions) RetryOptions {
397393
}
398394

399395
if retryOptions.MaxAttempts <= 0 {
400-
retryOptions.MaxAttempts = 3
396+
retryOptions.MaxAttempts = DefaultRetryOptionsMaxAttempts
401397
}
402398
if retryOptions.InitialDelayMs == 0 {
403-
retryOptions.InitialDelayMs = 1000
399+
retryOptions.InitialDelayMs = DefaultRetryOptionsInitialDelayMs
404400
}
405401
if retryOptions.BackoffMultiplier == 0 {
406-
retryOptions.BackoffMultiplier = 2.0
402+
retryOptions.BackoffMultiplier = DefaultRetryOptionsBackoffMultiplier
407403
}
408404
if retryOptions.MaxDelayMs == 0 {
409-
retryOptions.MaxDelayMs = 10000
405+
retryOptions.MaxDelayMs = DefaultRetryOptionsMaxDelayMs
410406
}
411407
return retryOptions
412408
}
@@ -424,8 +420,8 @@ func defaultIdentityProviderResponseParserOr(idpResponseParser IdentityProviderR
424420
func defaultTokenManagerOptionsOr(options TokenManagerOptions) TokenManagerOptions {
425421
options.RetryOptions = defaultRetryOptionsOr(options.RetryOptions)
426422
options.IdentityProviderResponseParser = defaultIdentityProviderResponseParserOr(options.IdentityProviderResponseParser)
427-
if options.ExpirationRefreshRatio <= 0 || options.ExpirationRefreshRatio > 1 {
428-
options.ExpirationRefreshRatio = 0.7
423+
if options.ExpirationRefreshRatio == 0 {
424+
options.ExpirationRefreshRatio = DefaultExpirationRefreshRatio
429425
}
430426
return options
431427
}

token_manager_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package entraid
2+
3+
import (
4+
"reflect"
5+
"runtime"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
var assertFuncNameMatches = func(t *testing.T, func1, func2 interface{}) {
12+
funcName1 := runtime.FuncForPC(reflect.ValueOf(func1).Pointer()).Name()
13+
funcName2 := runtime.FuncForPC(reflect.ValueOf(func2).Pointer()).Name()
14+
assert.Equal(t, funcName1, funcName2)
15+
}
16+
17+
func TestTokenManager(t *testing.T) {
18+
t.Parallel()
19+
t.Run("Without IDP", func(t *testing.T) {
20+
tokenManager, err := NewTokenManager(nil,
21+
TokenManagerOptions{},
22+
)
23+
assert.Error(t, err)
24+
assert.Nil(t, tokenManager)
25+
})
26+
27+
t.Run("With IDP", func(t *testing.T) {
28+
idp := &mockIdentityProvider{}
29+
tokenManager, err := NewTokenManager(idp,
30+
TokenManagerOptions{},
31+
)
32+
assert.NoError(t, err)
33+
assert.NotNil(t, tokenManager)
34+
})
35+
}
36+
37+
func TestTokenManagerWithOptions(t *testing.T) {
38+
t.Parallel()
39+
t.Run("Bad Expiration Refresh Ration", func(t *testing.T) {
40+
idp := &mockIdentityProvider{}
41+
options := TokenManagerOptions{
42+
ExpirationRefreshRatio: 5,
43+
}
44+
tokenManager, err := NewTokenManager(idp, options)
45+
assert.Error(t, err)
46+
assert.Nil(t, tokenManager)
47+
})
48+
t.Run("With IDP and Options", func(t *testing.T) {
49+
idp := &mockIdentityProvider{}
50+
options := TokenManagerOptions{
51+
ExpirationRefreshRatio: 0.5,
52+
}
53+
tokenManager, err := NewTokenManager(idp, options)
54+
assert.NoError(t, err)
55+
assert.NotNil(t, tokenManager)
56+
tm, ok := tokenManager.(*entraidTokenManager)
57+
assert.True(t, ok)
58+
assert.Equal(t, 0.5, tm.expirationRefreshRatio)
59+
})
60+
t.Run("Default Options", func(t *testing.T) {
61+
idp := &mockIdentityProvider{}
62+
options := TokenManagerOptions{}
63+
tokenManager, err := NewTokenManager(idp, options)
64+
assert.NoError(t, err)
65+
assert.NotNil(t, tokenManager)
66+
tm, ok := tokenManager.(*entraidTokenManager)
67+
assert.True(t, ok)
68+
assert.Equal(t, DefaultExpirationRefreshRatio, tm.expirationRefreshRatio)
69+
assert.NotNil(t, tm.retryOptions.IsRetryable)
70+
assertFuncNameMatches(t, tm.retryOptions.IsRetryable, defaultRetryableFunc)
71+
assert.Equal(t, DefaultRetryOptionsMaxAttempts, tm.retryOptions.MaxAttempts)
72+
assert.Equal(t, DefaultRetryOptionsInitialDelayMs, tm.retryOptions.InitialDelayMs)
73+
assert.Equal(t, DefaultRetryOptionsMaxDelayMs, tm.retryOptions.MaxDelayMs)
74+
assert.Equal(t, DefaultRetryOptionsBackoffMultiplier, tm.retryOptions.BackoffMultiplier)
75+
76+
})
77+
}

0 commit comments

Comments
 (0)