@@ -36,7 +36,7 @@ func newTestManager(t *testing.T, passportHandler http.HandlerFunc) (*Manager, *
3636 t .Setenv ("DRYCC_PASSPORT_URL" , ts .URL )
3737 t .Setenv ("DRYCC_PASSPORT_KEY" , "test-key" )
3838 t .Setenv ("DRYCC_PASSPORT_SECRET" , "test-secret" )
39- t .Setenv ("DRYCC_PASSPORT_SCOPES" , "controller:hook " )
39+ t .Setenv ("DRYCC_PASSPORT_SCOPES" , "passport:message " )
4040
4141 mgr , err := NewManager (client )
4242 require .NoError (t , err )
@@ -57,7 +57,20 @@ func passportJSON(t *testing.T, accessToken string, expiresIn int64) http.Handle
5757}
5858
5959func TestGet_FastPathReturnsCachedToken (t * testing.T ) {
60- mgr , mr , _ := newTestManager (t , passportJSON (t , "should-not-be-fetched" , 2592000 ))
60+ // The introspect endpoint is required for the fast path validity check now.
61+ handler := func (w http.ResponseWriter , req * http.Request ) {
62+ if req .URL .Path == "/oauth/introspect/" {
63+ w .Header ().Set ("Content-Type" , "application/json" )
64+ _ = json .NewEncoder (w ).Encode (map [string ]any {
65+ "active" : true ,
66+ "scope" : "passport:message" ,
67+ })
68+ return
69+ }
70+ // Fallback for token requests
71+ passportJSON (t , "should-not-be-fetched" , 2592000 )(w , req )
72+ }
73+ mgr , mr , _ := newTestManager (t , handler )
6174
6275 // Pre-populate Valkey with a still-valid token.
6376 p := payload {AccessToken : "cached-token" , ExpiresAt : time .Now ().Add (24 * time .Hour ).Unix ()}
@@ -96,13 +109,30 @@ func TestGet_ColdStartFetchesFromPassport(t *testing.T) {
96109}
97110
98111func TestGet_ConcurrentCallsHitPassportOnce (t * testing.T ) {
99- var calls int32
100- handler := func (w http.ResponseWriter , _ * http.Request ) {
101- atomic .AddInt32 (& calls , 1 )
112+ var activeCalls int32
113+ var tokenCalls int32
114+ var mu sync.Mutex
115+
116+ handler := func (w http.ResponseWriter , req * http.Request ) {
117+ mu .Lock ()
118+ if req .URL .Path == "/oauth/introspect/" {
119+ activeCalls ++
120+ mu .Unlock ()
121+ w .Header ().Set ("Content-Type" , "application/json" )
122+ _ = json .NewEncoder (w ).Encode (map [string ]any {
123+ "active" : true ,
124+ "scope" : "passport:message" ,
125+ })
126+ return
127+ }
128+
129+ tokenCalls ++
130+ mu .Unlock ()
131+
102132 // Slow handler to widen the race window.
103133 time .Sleep (50 * time .Millisecond )
104134 w .Header ().Set ("Content-Type" , "application/json" )
105- _ , _ = w .Write ([]byte (`{"access_token":"only-one","token_type":"Bearer","expires_in":2592000}` ))
135+ _ , _ = w .Write ([]byte (`{"access_token":"only-one","token_type":"Bearer","expires_in":2592000,"scope":"passport:message" }` ))
106136 }
107137 mgr , _ , _ := newTestManager (t , handler )
108138
@@ -123,15 +153,24 @@ func TestGet_ConcurrentCallsHitPassportOnce(t *testing.T) {
123153 require .NoErrorf (t , errs [i ], "goroutine %d" , i )
124154 assert .Equal (t , "only-one" , results [i ])
125155 }
126- assert .Equal (t , int32 (1 ), atomic .LoadInt32 (& calls ), "passport should be called exactly once" )
156+ assert .Equal (t , int32 (1 ), atomic .LoadInt32 (& tokenCalls ), "passport should be called exactly once" )
127157}
128158
129159func TestRefresh_SkipsWhenPlentyOfLifetimeRemains (t * testing.T ) {
130- var calls int32
131- handler := func (w http.ResponseWriter , _ * http.Request ) {
132- atomic .AddInt32 (& calls , 1 )
160+ var tokenCalls int32
161+ handler := func (w http.ResponseWriter , req * http.Request ) {
162+ if req .URL .Path == "/oauth/introspect/" {
163+ w .Header ().Set ("Content-Type" , "application/json" )
164+ _ = json .NewEncoder (w ).Encode (map [string ]any {
165+ "active" : true ,
166+ "scope" : "passport:message" ,
167+ })
168+ return
169+ }
170+
171+ atomic .AddInt32 (& tokenCalls , 1 )
133172 w .WriteHeader (http .StatusOK )
134- _ , _ = w .Write ([]byte (`{"access_token":"new","expires_in":2592000}` ))
173+ _ , _ = w .Write ([]byte (`{"access_token":"new","expires_in":2592000,"scope":"passport:message" }` ))
135174 }
136175 mgr , mr , _ := newTestManager (t , handler )
137176
@@ -141,7 +180,7 @@ func TestRefresh_SkipsWhenPlentyOfLifetimeRemains(t *testing.T) {
141180 require .NoError (t , mr .Set (TokenKey , string (raw )))
142181
143182 require .NoError (t , mgr .Refresh (context .Background (), false ))
144- assert .Equal (t , int32 (0 ), atomic .LoadInt32 (& calls ))
183+ assert .Equal (t , int32 (0 ), atomic .LoadInt32 (& tokenCalls ))
145184
146185 got , _ := mr .Get (TokenKey )
147186 assert .Equal (t , string (raw ), got , "token must be untouched" )
@@ -386,6 +425,15 @@ func TestNewClientFromEnv_MissingURL(t *testing.T) {
386425func TestRequestToken_SendsControllerHookScope (t * testing.T ) {
387426 var captured url.Values
388427 handler := func (w http.ResponseWriter , r * http.Request ) {
428+ if r .URL .Path == "/oauth/introspect/" {
429+ w .Header ().Set ("Content-Type" , "application/json" )
430+ _ = json .NewEncoder (w ).Encode (map [string ]any {
431+ "active" : true ,
432+ "scope" : "passport:message" ,
433+ })
434+ return
435+ }
436+
389437 body , _ := io .ReadAll (r .Body )
390438 captured , _ = url .ParseQuery (string (body ))
391439 w .Header ().Set ("Content-Type" , "application/json" )
@@ -399,5 +447,5 @@ func TestRequestToken_SendsControllerHookScope(t *testing.T) {
399447 assert .Equal (t , "client_credentials" , captured .Get ("grant_type" ))
400448 assert .Equal (t , "test-key" , captured .Get ("client_id" ))
401449 assert .Equal (t , "test-secret" , captured .Get ("client_secret" ))
402- assert .Equal (t , "controller:hook " , captured .Get ("scope" ))
450+ assert .Equal (t , "passport:message " , captured .Get ("scope" ))
403451}
0 commit comments