@@ -5,6 +5,7 @@ package v1
55
66import (
77 "context"
8+ "errors"
89 "fmt"
910 "sync"
1011 "sync/atomic"
@@ -194,38 +195,69 @@ func TestGetRequestMetadata_RefreshFailureReturnsStaleCachedToken(t *testing.T)
194195 tokenFunc : func () (* oauth2.Token , error ) {
195196 n := callNum .Add (1 )
196197 if n == 1 {
197- return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (- time .Minute )}, nil
198+ return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (5 * time .Second )}, nil
198199 }
199200 return nil , fmt .Errorf ("idp unavailable" )
200201 },
201202 }
202- provider , err := RefreshableToken (src , WithLeeway ( 0 ))
203+ provider , err := RefreshableToken (src ) // default 10s leeway
203204 require .NoError (t , err )
204205
205- // First call succeeds but returns already-expired token.
206+ // First call succeeds with a token inside the leeway window .
206207 _ , err = provider .GetRequestMetadata (context .Background ())
207208 require .NoError (t , err )
208209
209- // Second call: refresh fails, should return stale token.
210+ // Second call: refresh fails, should return the unexpired stale token.
210211 md , err := provider .GetRequestMetadata (context .Background ())
211212 require .NoError (t , err )
212213 assert .Equal (t , "Bearer stale" , md ["authorization" ])
213214}
214215
216+ func TestGetRequestMetadata_RefreshFailureWithExpiredCachedTokenReturnsError (t * testing.T ) {
217+ errIDP := errors .New ("idp unavailable" )
218+ var callNum atomic.Int32
219+ src := & mockTokenSource {
220+ tokenFunc : func () (* oauth2.Token , error ) {
221+ n := callNum .Add (1 )
222+ if n == 1 {
223+ return & oauth2.Token {AccessToken : "expired" , Expiry : time .Now ().Add (- time .Minute )}, nil
224+ }
225+ return nil , errIDP
226+ },
227+ }
228+ logger := & captureLogger {}
229+ provider , err := RefreshableToken (src , WithLeeway (0 ), WithLogger (logger ))
230+ require .NoError (t , err )
231+
232+ _ , err = provider .GetRequestMetadata (context .Background ())
233+ require .NoError (t , err )
234+
235+ md , err := provider .GetRequestMetadata (context .Background ())
236+ require .ErrorIs (t , err , errIDP )
237+ assert .Nil (t , md )
238+ assert .Empty (t , logger .errors )
239+
240+ // During backoff, the same error is returned without calling the source.
241+ md , err = provider .GetRequestMetadata (context .Background ())
242+ require .ErrorIs (t , err , errIDP )
243+ assert .Nil (t , md )
244+ assert .Equal (t , 2 , src .calls ())
245+ }
246+
215247func TestGetRequestMetadata_RefreshFailureLogsWarning (t * testing.T ) {
216248 var callNum atomic.Int32
217249 src := & mockTokenSource {
218250 tokenFunc : func () (* oauth2.Token , error ) {
219251 n := callNum .Add (1 )
220252 if n == 1 {
221- return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (- time .Minute )}, nil
253+ return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (5 * time .Second )}, nil
222254 }
223255 return nil , fmt .Errorf ("idp unavailable" )
224256 },
225257 }
226258
227259 logger := & captureLogger {}
228- provider , err := RefreshableToken (src , WithLeeway ( 0 ), WithLogger (logger ))
260+ provider , err := RefreshableToken (src , WithLogger (logger ))
229261 require .NoError (t , err )
230262
231263 _ , _ = provider .GetRequestMetadata (context .Background ())
@@ -255,12 +287,12 @@ func TestGetRequestMetadata_RefreshFailureNoLoggerNoPanic(t *testing.T) {
255287 tokenFunc : func () (* oauth2.Token , error ) {
256288 n := callNum .Add (1 )
257289 if n == 1 {
258- return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (- time .Minute )}, nil
290+ return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (5 * time .Second )}, nil
259291 }
260292 return nil , fmt .Errorf ("idp unavailable" )
261293 },
262294 }
263- provider , err := RefreshableToken (src , WithLeeway ( 0 ) )
295+ provider , err := RefreshableToken (src )
264296 require .NoError (t , err )
265297
266298 _ , _ = provider .GetRequestMetadata (context .Background ())
@@ -348,12 +380,12 @@ func TestGetRequestMetadata_BackoffSkipsRefreshDuringWindow(t *testing.T) {
348380 tokenFunc : func () (* oauth2.Token , error ) {
349381 n := callNum .Add (1 )
350382 if n == 1 {
351- return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (- time .Minute )}, nil
383+ return & oauth2.Token {AccessToken : "stale" , Expiry : time .Now ().Add (5 * time .Second )}, nil
352384 }
353385 return nil , fmt .Errorf ("idp down" )
354386 },
355387 }
356- provider , err := RefreshableToken (src , WithLeeway ( 0 ) )
388+ provider , err := RefreshableToken (src )
357389 require .NoError (t , err )
358390
359391 _ , err = provider .GetRequestMetadata (context .Background ())
@@ -374,6 +406,24 @@ func TestGetRequestMetadata_BackoffSkipsRefreshDuringWindow(t *testing.T) {
374406 assert .Equal (t , beforeCount , src .calls (), "should not call Token() during backoff window" )
375407}
376408
409+ func TestGetRequestMetadata_BackoffWithoutCachedTokenReturnsLastError (t * testing.T ) {
410+ errIDP := errors .New ("idp down" )
411+ src := & mockTokenSource {
412+ tokenFunc : func () (* oauth2.Token , error ) {
413+ return nil , errIDP
414+ },
415+ }
416+ provider , err := RefreshableToken (src )
417+ require .NoError (t , err )
418+
419+ _ , err = provider .GetRequestMetadata (context .Background ())
420+ require .ErrorIs (t , err , errIDP )
421+
422+ _ , err = provider .GetRequestMetadata (context .Background ())
423+ require .ErrorIs (t , err , errIDP )
424+ assert .Equal (t , 1 , src .calls (), "should not call Token() during backoff window" )
425+ }
426+
377427func TestGetRequestMetadata_BackoffResetsOnSuccess (t * testing.T ) {
378428 var callNum atomic.Int32
379429 src := & mockTokenSource {
0 commit comments