@@ -272,6 +272,30 @@ describe('store auth service', () => {
272272 expect ( presenter . success ) . toHaveBeenCalledWith ( result )
273273 } )
274274
275+ test ( 'authenticateStoreWithApp records fqdn metadata before resolving existing scopes' , async ( ) => {
276+ await expect (
277+ authenticateStoreWithApp (
278+ {
279+ store : 'shop.myshopify.com' ,
280+ scopes : 'read_products' ,
281+ } ,
282+ {
283+ resolveExistingScopes : vi . fn ( ) . mockRejectedValue ( new Error ( 'scope lookup failed' ) ) ,
284+ presenter : {
285+ openingBrowser : vi . fn ( ) ,
286+ manualAuthUrl : vi . fn ( ) ,
287+ success : vi . fn ( ) ,
288+ } ,
289+ } ,
290+ ) ,
291+ ) . rejects . toThrow ( 'scope lookup failed' )
292+
293+ expect ( recordStoreFqdnMetadata ) . toHaveBeenCalledWith ( 'shop.myshopify.com' , false )
294+ expect ( recordStoreFqdnMetadata ) . not . toHaveBeenCalledWith ( 'shop.myshopify.com' , true )
295+ expect ( setLastSeenUserId ) . not . toHaveBeenCalled ( )
296+ expect ( setStoredStoreAppSession ) . not . toHaveBeenCalled ( )
297+ } )
298+
275299 test ( 'authenticateStoreWithApp records fqdn metadata before waiting for the auth callback' , async ( ) => {
276300 const waitForStoreAuthCodeMock = vi . fn ( ) . mockRejectedValue ( new Error ( 'callback failed' ) )
277301
@@ -299,6 +323,72 @@ describe('store auth service', () => {
299323 expect ( setStoredStoreAppSession ) . not . toHaveBeenCalled ( )
300324 } )
301325
326+ test ( 'authenticateStoreWithApp does not mark the store validated when token exchange fails' , async ( ) => {
327+ const waitForStoreAuthCodeMock = vi . fn ( ) . mockImplementation ( async ( options ) => {
328+ await options . onListening ?.( )
329+ return 'abc123'
330+ } )
331+
332+ await expect (
333+ authenticateStoreWithApp (
334+ {
335+ store : 'shop.myshopify.com' ,
336+ scopes : 'read_products' ,
337+ } ,
338+ {
339+ openURL : vi . fn ( ) . mockResolvedValue ( true ) ,
340+ waitForStoreAuthCode : waitForStoreAuthCodeMock ,
341+ exchangeStoreAuthCodeForToken : vi . fn ( ) . mockRejectedValue ( new Error ( 'token exchange failed' ) ) ,
342+ presenter : {
343+ openingBrowser : vi . fn ( ) ,
344+ manualAuthUrl : vi . fn ( ) ,
345+ success : vi . fn ( ) ,
346+ } ,
347+ } ,
348+ ) ,
349+ ) . rejects . toThrow ( 'token exchange failed' )
350+
351+ expect ( recordStoreFqdnMetadata ) . toHaveBeenCalledWith ( 'shop.myshopify.com' , false )
352+ expect ( recordStoreFqdnMetadata ) . not . toHaveBeenCalledWith ( 'shop.myshopify.com' , true )
353+ expect ( setLastSeenUserId ) . not . toHaveBeenCalled ( )
354+ expect ( setStoredStoreAppSession ) . not . toHaveBeenCalled ( )
355+ } )
356+
357+ test ( 'authenticateStoreWithApp marks the store validated after token exchange but before rejecting missing associated user data' , async ( ) => {
358+ const waitForStoreAuthCodeMock = vi . fn ( ) . mockImplementation ( async ( options ) => {
359+ await options . onListening ?.( )
360+ return 'abc123'
361+ } )
362+
363+ await expect (
364+ authenticateStoreWithApp (
365+ {
366+ store : 'shop.myshopify.com' ,
367+ scopes : 'read_products' ,
368+ } ,
369+ {
370+ openURL : vi . fn ( ) . mockResolvedValue ( true ) ,
371+ waitForStoreAuthCode : waitForStoreAuthCodeMock ,
372+ exchangeStoreAuthCodeForToken : vi . fn ( ) . mockResolvedValue ( {
373+ access_token : 'token' ,
374+ scope : 'read_products' ,
375+ expires_in : 86400 ,
376+ } ) ,
377+ presenter : {
378+ openingBrowser : vi . fn ( ) ,
379+ manualAuthUrl : vi . fn ( ) ,
380+ success : vi . fn ( ) ,
381+ } ,
382+ } ,
383+ ) ,
384+ ) . rejects . toThrow ( 'Shopify did not return associated user information for the online access token.' )
385+
386+ expect ( recordStoreFqdnMetadata ) . toHaveBeenNthCalledWith ( 1 , 'shop.myshopify.com' , false )
387+ expect ( recordStoreFqdnMetadata ) . toHaveBeenNthCalledWith ( 2 , 'shop.myshopify.com' , true )
388+ expect ( setLastSeenUserId ) . not . toHaveBeenCalled ( )
389+ expect ( setStoredStoreAppSession ) . not . toHaveBeenCalled ( )
390+ } )
391+
302392 test ( 'authenticateStoreWithApp rejects when Shopify grants fewer scopes than requested' , async ( ) => {
303393 const waitForStoreAuthCodeMock = vi . fn ( ) . mockImplementation ( async ( options ) => {
304394 await options . onListening ?.( )
0 commit comments