Skip to content

Commit af66794

Browse files
committed
add few more tests
1 parent c48eb03 commit af66794

File tree

8 files changed

+128
-12
lines changed

8 files changed

+128
-12
lines changed

.github/workflows/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#file: noinspection GrazieInspection,GrazieInspection
12
# For most projects, this workflow file will not need changing; you simply need
23
# to commit it to your repository.
34
#
@@ -55,6 +56,7 @@ jobs:
5556
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
5657
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
5758
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
59+
# noinspection GrazieInspection,GrazieInspection
5860
steps:
5961
- name: Checkout repository
6062
uses: actions/checkout@v4

.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: 2
2+
linters:
3+
disable:
4+
- depguard
5+

confidential_identity_provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"crypto/x509"
77
"fmt"
88

9-
confidential "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
9+
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
1010
)
1111

1212
// ConfidentialIdentityProviderOptions represents the options for the confidential identity provider.

credentials_provider.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"fmt"
55
"sync"
66

7-
auth "github.com/redis/go-redis/v9/auth"
7+
"github.com/redis/go-redis/v9/auth"
88
)
99

1010
// entraidCredentialsProvider implements the auth.StreamingCredentialsProvider interface.

entraid_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package entraid
22

33
import (
4+
"net"
5+
46
"github.com/stretchr/testify/mock"
57
)
68

@@ -17,3 +19,26 @@ func (m *mockIdentityProvider) RequestToken() (IdentityProviderResponse, error)
1719

1820
// Ensure mockIdentityProvider implements the IdentityProvider interface
1921
var _ IdentityProvider = (*mockIdentityProvider)(nil)
22+
23+
type mockError struct {
24+
// Mock implementation of the network error
25+
error
26+
isTimeout bool
27+
isTemporary bool
28+
}
29+
30+
func (m *mockError) Timeout() bool {
31+
return m.isTimeout
32+
}
33+
func (m *mockError) Temporary() bool {
34+
return m.isTemporary
35+
}
36+
func (m *mockError) Unwrap() error {
37+
return m.error
38+
}
39+
40+
func (m *mockError) Is(err error) bool {
41+
return m.error == err
42+
}
43+
44+
var _ net.Error = (*mockError)(nil)

qodana.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#-------------------------------------------------------------------------------#
2+
# Qodana analysis is configured by qodana.yaml file #
3+
# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
4+
#-------------------------------------------------------------------------------#
5+
version: "1.0"
6+
7+
#Specify inspection profile for code analysis
8+
profile:
9+
name: qodana.starter
10+
11+
#Enable inspections
12+
#include:
13+
# - name: <SomeEnabledInspectionId>
14+
15+
#Disable inspections
16+
#exclude:
17+
# - name: <SomeDisabledInspectionId>
18+
# paths:
19+
# - <path/where/not/run/inspection>
20+
21+
#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
22+
#bootstrap: sh ./prepare-qodana.sh
23+
24+
#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
25+
#plugins:
26+
# - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
27+
28+
#Specify Qodana linter for analysis (Applied in CI/CD pipeline)
29+
linter: jetbrains/qodana-go:2024.3

token_manager.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"log"
77
"net"
8+
"os"
89
"sync"
910
"time"
1011

@@ -21,23 +22,20 @@ type TokenManagerOptions struct {
2122
//
2223
// default: 0.7
2324
ExpirationRefreshRatio float64
24-
2525
// LowerRefreshBoundMs is the lower bound for the refresh time in milliseconds.
2626
// Represents the minimum time in milliseconds before token expiration to trigger a refresh, in milliseconds.
2727
// This value sets a fixed lower bound for when a token refresh should occur, regardless
2828
// of the token's total lifetime.
2929
//
3030
// default: 0 ms (no lower bound, refresh based on ExpirationRefreshRatio)
3131
LowerRefreshBoundMs int64
32-
3332
// IdentityProviderResponseParser is a function that parses the IdentityProviderResponse.
3433
// The function takes the response and based on its type returns the populated Token object.
3534
// If this function is not provided, the default implementation will be used.
3635
//
3736
// required: true
3837
// default: defaultIdentityProviderResponseParser
3938
IdentityProviderResponseParser IdentityProviderResponseParserFunc
40-
4139
// RetryOptions is a struct that contains the options for retrying the token request.
4240
// It contains the maximum number of attempts, initial delay, maximum delay, and backoff multiplier.
4341
//
@@ -83,7 +81,7 @@ type TokenManager interface {
8381
}
8482

8583
// defaultIdentityProviderResponseParser is a function that parses the token and returns the username and password.
86-
var defaultIdentityProviderResponseParser = func(response IdentityProviderResponse) (*Token, error) {
84+
var defaultIdentityProviderResponseParser IdentityProviderResponseParserFunc = func(response IdentityProviderResponse) (*Token, error) {
8785
var username, password, rawToken string
8886
var expiresOn time.Time
8987
if response == nil {
@@ -253,7 +251,7 @@ func (e *entraidTokenManager) GetToken() (*Token, error) {
253251
// cancelFunc is a function that cancels the token manager.
254252
type cancelFunc func() error
255253

256-
// TokenListener is a interface that contains the methods for receiving updates from the token manager.
254+
// TokenListener is an interface that contains the methods for receiving updates from the token manager.
257255
// The token manager will call the listener's OnTokenNext method with the updated token.
258256
// If an error occurs, the token manager will call the listener's OnTokenError method with the error.
259257
type TokenListener interface {
@@ -371,16 +369,18 @@ func (e *entraidTokenManager) Close() error {
371369
// defaultRetryableFunc is a function that checks if the error is retryable.
372370
// It takes an error as an argument and returns a boolean value.
373371
// The function checks if the error is a net.Error and if it is a timeout or temporary error.
374-
var defaultRetryableFunc = func(err error) bool {
372+
var defaultIsRetryable func(err error) bool = func(err error) bool {
375373
var netErr net.Error
376374
if err == nil {
377375
return true
378376
}
379377

378+
// nolint:staticcheck // SA1019 deprecated netErr.Temporary
380379
if ok := errors.As(err, &netErr); ok {
381-
return netErr.Timeout()
380+
return netErr.Timeout() || netErr.Temporary()
382381
}
383-
return false
382+
383+
return errors.Is(err, os.ErrDeadlineExceeded)
384384
}
385385

386386
// defaultRetryOptionsOr returns the default retry options if the provided options are not set.
@@ -389,7 +389,7 @@ var defaultRetryableFunc = func(err error) bool {
389389
// The values can be overridden by the user.
390390
func defaultRetryOptionsOr(retryOptions RetryOptions) RetryOptions {
391391
if retryOptions.IsRetryable == nil {
392-
retryOptions.IsRetryable = defaultRetryableFunc
392+
retryOptions.IsRetryable = defaultIsRetryable
393393
}
394394

395395
if retryOptions.MaxAttempts <= 0 {

token_manager_test.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package entraid
22

33
import (
4+
"fmt"
5+
"os"
46
"reflect"
57
"runtime"
68
"testing"
79

10+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
811
"github.com/stretchr/testify/assert"
912
)
1013

@@ -67,11 +70,63 @@ func TestTokenManagerWithOptions(t *testing.T) {
6770
assert.True(t, ok)
6871
assert.Equal(t, DefaultExpirationRefreshRatio, tm.expirationRefreshRatio)
6972
assert.NotNil(t, tm.retryOptions.IsRetryable)
70-
assertFuncNameMatches(t, tm.retryOptions.IsRetryable, defaultRetryableFunc)
73+
assertFuncNameMatches(t, tm.retryOptions.IsRetryable, defaultIsRetryable)
7174
assert.Equal(t, DefaultRetryOptionsMaxAttempts, tm.retryOptions.MaxAttempts)
7275
assert.Equal(t, DefaultRetryOptionsInitialDelayMs, tm.retryOptions.InitialDelayMs)
7376
assert.Equal(t, DefaultRetryOptionsMaxDelayMs, tm.retryOptions.MaxDelayMs)
7477
assert.Equal(t, DefaultRetryOptionsBackoffMultiplier, tm.retryOptions.BackoffMultiplier)
78+
})
79+
}
80+
81+
func TestDefaultIdentityProviderResponseParserOr(t *testing.T) {
82+
t.Parallel()
83+
var f IdentityProviderResponseParserFunc = func(response IdentityProviderResponse) (*Token, error) {
84+
return nil, nil
85+
}
86+
87+
result := defaultIdentityProviderResponseParserOr(f)
88+
assert.NotNil(t, result)
89+
assertFuncNameMatches(t, result, f)
90+
91+
defaultFunc := defaultIdentityProviderResponseParserOr(nil)
92+
assert.NotNil(t, defaultFunc)
93+
assertFuncNameMatches(t, defaultFunc, defaultIdentityProviderResponseParser)
94+
}
95+
96+
func TestDefaultIsRetryable(t *testing.T) {
97+
t.Parallel()
98+
// with network error timeout
99+
t.Run("Non-Retryable Error", func(t *testing.T) {
100+
err := &azcore.ResponseError{
101+
StatusCode: 500,
102+
}
103+
is := defaultIsRetryable(err)
104+
assert.False(t, is)
105+
})
106+
107+
t.Run("Nil Error", func(t *testing.T) {
108+
var err error
109+
is := defaultIsRetryable(err)
110+
assert.True(t, is)
111+
112+
is = defaultIsRetryable(nil)
113+
assert.True(t, is)
114+
})
115+
116+
t.Run("Retryable Error with Timeout", func(t *testing.T) {
117+
err := &mockError{isTimeout: true}
118+
result := defaultIsRetryable(err)
119+
assert.True(t, result)
120+
})
121+
t.Run("Retryable Error with Temporary", func(t *testing.T) {
122+
err := &mockError{isTemporary: true}
123+
result := defaultIsRetryable(err)
124+
assert.True(t, result)
125+
})
75126

127+
t.Run("Retryable Error with err parent of os.ErrDeadlineExceeded", func(t *testing.T) {
128+
err := fmt.Errorf("timeout: %w", os.ErrDeadlineExceeded)
129+
res := defaultIsRetryable(err)
130+
assert.True(t, res)
76131
})
77132
}

0 commit comments

Comments
 (0)