@@ -100,10 +100,11 @@ func TestM2mNotSupported(t *testing.T) {
100100}
101101
102102// TestM2mCredentials_DirectTokenSource verifies that M2mCredentials.Configure
103- // returns a token source that calls the HTTP endpoint on every Token() call
104- // (no inner ReuseTokenSource caching). With the old code that passed
105- // ccfg.TokenSource(ctx) (a ReuseTokenSource) only the first of N calls would
106- // hit the endpoint; the fix passes ccfg.Token(ctx) directly so each call does.
103+ // plumbs a direct token source (ccfg.Token) through cachedTokenSource rather
104+ // than wrapping clientcredentials.Config.TokenSource, which returns an
105+ // oauth2.ReuseTokenSource that adds a second cache layer. With double-caching,
106+ // the proactive async refresh in cachedTokenSource is silently suppressed until
107+ // ~10 s before expiry, causing bursts of 401 errors at token rotation boundaries.
107108// See https://github.com/databricks/databricks-sdk-go/issues/1549.
108109func TestM2mCredentials_DirectTokenSource (t * testing.T ) {
109110 var tokenCalls int32
@@ -125,13 +126,12 @@ func TestM2mCredentials_DirectTokenSource(t *testing.T) {
125126 }
126127
127128 cfg := & Config {
128- Host : "a" ,
129- ClientID : "b" ,
130- ClientSecret : "c" ,
131- AuthType : "oauth-m2m" ,
132- ConfigFile : "/dev/null" ,
133- DisableAsyncTokenRefresh : true ,
134- HTTPTransport : transport ,
129+ Host : "a" ,
130+ ClientID : "b" ,
131+ ClientSecret : "c" ,
132+ AuthType : "oauth-m2m" ,
133+ ConfigFile : "/dev/null" ,
134+ HTTPTransport : transport ,
135135 }
136136
137137 err := cfg .EnsureResolved ()
@@ -147,22 +147,18 @@ func TestM2mCredentials_DirectTokenSource(t *testing.T) {
147147
148148 oauthProvider := provider .(credentials.OAuthCredentialsProvider )
149149
150- // Call Token() 3 times. With the fix, each call hits the endpoint.
151- // Without the fix (ReuseTokenSource), only the first call does.
152- const nCalls = 3
153- for i := 0 ; i < nCalls ; i ++ {
154- tok , err := oauthProvider .Token (context .Background ())
155- if err != nil {
156- t .Fatalf ("Token() call %d: %v" , i + 1 , err )
157- }
158- if tok .AccessToken == "" {
159- t .Fatalf ("Token() call %d: empty access token" , i + 1 )
160- }
150+ // Token() goes through cachedTokenSource, which fetches once and caches.
151+ // Verify the endpoint is reached (not short-circuited by an inner cache).
152+ tok , err := oauthProvider .Token (context .Background ())
153+ if err != nil {
154+ t .Fatalf ("Token(): %v" , err )
155+ }
156+ if tok .AccessToken == "" {
157+ t .Fatalf ("Token(): empty access token" )
161158 }
162159
163- got := int (atomic .LoadInt32 (& tokenCalls ))
164- if got != nCalls {
165- t .Errorf ("token endpoint calls = %d, want %d" , got , nCalls )
160+ if got := int (atomic .LoadInt32 (& tokenCalls )); got != 1 {
161+ t .Errorf ("token endpoint calls = %d, want 1" , got )
166162 }
167163}
168164
0 commit comments