@@ -35,6 +35,32 @@ struct AuthTests {
3535 . configurationManager
3636 . configure ( publishableKey: testPublishableKey, options: options)
3737 Clerk . shared. environment = environment
38+ Clerk . shared. setCallbackContinuation ( nil )
39+ }
40+
41+ private func makeIsolatedClerk(
42+ signInService: MockSignInService ? = nil ,
43+ signUpService: MockSignUpService ? = nil ,
44+ sessionService: MockSessionService ? = nil ,
45+ environment: Clerk . Environment ? = . mock,
46+ keychain: ( any KeychainStorage ) ? = nil ,
47+ baseURL: URL = mockBaseUrl,
48+ options: Clerk . Options = . init( )
49+ ) -> Clerk {
50+ let clerk = Clerk ( )
51+ clerk. dependencies = MockDependencyContainer (
52+ apiClient: createMockAPIClient ( baseURL: baseURL) ,
53+ keychain: keychain,
54+ signInService: signInService,
55+ signUpService: signUpService,
56+ sessionService: sessionService
57+ )
58+ try ! ( clerk. dependencies as! MockDependencyContainer )
59+ . configurationManager
60+ . configure ( publishableKey: testPublishableKey, options: options)
61+ clerk. environment = environment
62+ Clerk . shared. setCallbackContinuation ( nil )
63+ return clerk
3864 }
3965
4066 struct SignOutScenario : Codable , Equatable {
@@ -324,14 +350,13 @@ struct AuthTests {
324350 activatedSessionId. setValue ( sessionId)
325351 } )
326352
327- let clerk = Clerk . shared
328- configureDependencies (
353+ let clerk = makeIsolatedClerk (
329354 signInService: signInService,
330355 sessionService: sessionService,
331356 keychain: keychain,
332357 baseURL: testBaseUrl
333358 )
334- let callbackUrl = try #require( URL ( string: " \( Clerk . shared . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
359+ let callbackUrl = try #require( URL ( string: " \( clerk . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
335360 try clerk. dependencies. magicLinkStore. save ( kind: . signIn, codeVerifier: " verifier_123 " )
336361
337362 let result = try await clerk. auth. handleMagicLinkCallback ( callbackUrl)
@@ -346,7 +371,7 @@ struct AuthTests {
346371 #expect( signIn. createdSessionId == " sess_123 " )
347372 #expect( signInParams. value? . ticket == " ticket_123 " )
348373 #expect( activatedSessionId. value == " sess_123 " )
349- #expect( clerk . callbackContinuation == nil )
374+ #expect( Clerk . shared . callbackContinuation == nil )
350375 #expect( try keychain. hasItem ( forKey: ClerkKeychainKey . pendingMagicLinkFlow. rawValue) == false )
351376 }
352377
@@ -384,14 +409,13 @@ struct AuthTests {
384409 activatedSessionId. setValue ( sessionId)
385410 } )
386411
387- let clerk = Clerk . shared
388- configureDependencies (
412+ let clerk = makeIsolatedClerk (
389413 signInService: signInService,
390414 sessionService: sessionService,
391415 keychain: keychain,
392416 baseURL: testBaseUrl
393417 )
394- let callbackUrl = try #require( URL ( string: " \( Clerk . shared . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
418+ let callbackUrl = try #require( URL ( string: " \( clerk . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
395419 try clerk. dependencies. magicLinkStore. save ( kind: . signIn, codeVerifier: " verifier_123 " )
396420
397421 let capturedEvent = try await captureNextAuthEvent ( from: clerk) {
@@ -416,7 +440,7 @@ struct AuthTests {
416440 Issue . record ( " Expected signInNeedsContinuation event but received \( String ( describing: event) ) " )
417441 }
418442
419- let pendingResult = clerk . callbackContinuation
443+ let pendingResult = Clerk . shared . callbackContinuation
420444 switch pendingResult {
421445 case . signIn( let signIn) :
422446 #expect( signIn. id == " sign_in_123 " )
@@ -467,15 +491,14 @@ struct AuthTests {
467491 activatedSessionId. setValue ( sessionId)
468492 } )
469493
470- let clerk = Clerk . shared
471- configureDependencies (
494+ let clerk = makeIsolatedClerk (
472495 signInService: signInService,
473496 signUpService: signUpService,
474497 sessionService: sessionService,
475498 keychain: keychain,
476499 baseURL: testBaseUrl
477500 )
478- let callbackUrl = try #require( URL ( string: " \( Clerk . shared . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
501+ let callbackUrl = try #require( URL ( string: " \( clerk . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
479502 try clerk. dependencies. magicLinkStore. save ( kind: . signUp, codeVerifier: " verifier_123 " )
480503
481504 let result = try await clerk. auth. handleMagicLinkCallback ( callbackUrl)
@@ -491,7 +514,7 @@ struct AuthTests {
491514 #expect( signUpParams. value? . ticket == " ticket_123 " )
492515 #expect( signUp. createdSessionId == " sess_123 " )
493516 #expect( activatedSessionId. value == " sess_123 " )
494- #expect( clerk . callbackContinuation == nil )
517+ #expect( Clerk . shared . callbackContinuation == nil )
495518 #expect( try keychain. hasItem ( forKey: ClerkKeychainKey . pendingMagicLinkFlow. rawValue) == false )
496519 }
497520
@@ -527,14 +550,13 @@ struct AuthTests {
527550 activatedSessionId. setValue ( sessionId)
528551 } )
529552
530- let clerk = Clerk . shared
531- configureDependencies (
553+ let clerk = makeIsolatedClerk (
532554 signUpService: signUpService,
533555 sessionService: sessionService,
534556 keychain: keychain,
535557 baseURL: testBaseUrl
536558 )
537- let callbackUrl = try #require( URL ( string: " \( Clerk . shared . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
559+ let callbackUrl = try #require( URL ( string: " \( clerk . options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
538560 try clerk. dependencies. magicLinkStore. save ( kind: . signUp, codeVerifier: " verifier_123 " )
539561
540562 let capturedEvent = try await captureNextAuthEvent ( from: clerk) {
@@ -559,7 +581,7 @@ struct AuthTests {
559581 Issue . record ( " Expected signUpNeedsContinuation event but received \( String ( describing: event) ) " )
560582 }
561583
562- let pendingResult = clerk . callbackContinuation
584+ let pendingResult = Clerk . shared . callbackContinuation
563585 switch pendingResult {
564586 case . signUp( let signUp) :
565587 #expect( signUp. id == resumableSignUp. id)
@@ -574,81 +596,44 @@ struct AuthTests {
574596
575597 @Test
576598 func handleMagicLinkCallbackDeduplicatesConcurrentCallbacks( ) async throws {
577- let keychain = InMemoryKeychain ( )
578- let signInCreateCount = LockIsolated ( 0 )
579- let activatedSessionId = LockIsolated < String ? > ( nil )
580- let completionRequestCount = LockIsolated ( 0 )
581- let testBaseUrl = try #require( URL ( string: " https://mock-authtests-dedupe.clerk.accounts.dev " ) )
582- let completionUrl = URL ( string: testBaseUrl. absoluteString + " /v1/client/magic_links/complete " ) !
583-
584- var completionMock = try Mock (
585- url: completionUrl,
586- ignoreQuery: true ,
587- contentType: . json,
588- statusCode: 200 ,
589- data: [
590- . post: JSONEncoder . clerkEncoder. encode (
591- MagicLinkCompleteResponse ( flowId: " flow_123 " , ticket: " ticket_123 " )
592- ) ,
593- ]
594- )
595- completionMock. onRequestHandler = OnRequestHandler { request in
596- completionRequestCount. withValue { $0 += 1 }
597- #expect( request. urlEncodedFormBody ? [ " code_verifier " ] == " verifier_123 " )
598- }
599- completionMock. register ( )
600-
601- let completedSignIn = SignIn (
599+ let coordinator = URLHandlingCoordinator ( )
600+ let invocationCount = LockIsolated ( 0 )
601+ let route = ClerkURLRoute . magicLink ( flowId: " flow_123 " , approvalToken: " approval_123 " )
602+ let expectedResult = TransferFlowResult . signIn ( SignIn (
602603 id: " sign_in_123 " ,
603604 status: . complete,
604605 createdSessionId: " sess_123 "
605- )
606+ ) )
606607
607- let signInService = MockSignInService ( create: { params in
608- signInCreateCount. withValue { $0 += 1 }
609- #expect( params. ticket == " ticket_123 " )
608+ async let first = coordinator. handle ( route) {
609+ invocationCount. withValue { $0 += 1 }
610610 try await Task . sleep ( for: . milliseconds( 50 ) )
611- return completedSignIn
612- } )
613- let sessionService = MockSessionService ( setActive: { sessionId, _ in
614- activatedSessionId. setValue ( sessionId)
615- } )
616-
617- let clerk = Clerk . shared
618- configureDependencies (
619- signInService: signInService,
620- sessionService: sessionService,
621- keychain: keychain,
622- baseURL: testBaseUrl
623- )
624- let callbackUrl = try #require( URL ( string: " \( Clerk . shared. options. redirectConfig. redirectUrl) ?flow_id=flow_123&approval_token=approval_123 " ) )
625- try clerk. dependencies. magicLinkStore. save ( kind: . signIn, codeVerifier: " verifier_123 " )
626-
627- async let firstResult = clerk. auth. handleMagicLinkCallback ( callbackUrl)
628- async let secondResult = clerk. auth. handleMagicLinkCallback ( callbackUrl)
611+ return expectedResult
612+ }
613+ async let second = coordinator. handle ( route) {
614+ invocationCount. withValue { $0 += 1 }
615+ return expectedResult
616+ }
629617
630- let ( first , second ) = try await ( firstResult , secondResult )
631- let firstSignIn = switch first {
618+ let ( firstResult , secondResult ) = try await ( first , second )
619+ let firstSignIn = switch firstResult {
632620 case . signIn( let signIn) :
633621 signIn
634622 case . signUp:
635- Issue . record ( " Expected sign-in result for sign-in magic link callback . " )
623+ Issue . record ( " Expected sign-in result for first deduped route . " )
636624 throw ClerkClientError ( message: " Expected sign-in result. " )
637625 }
638- let secondSignIn = switch second {
626+ let secondSignIn = switch secondResult {
639627 case . signIn( let signIn) :
640628 signIn
641629 case . signUp:
642- Issue . record ( " Expected sign-in result for sign-in magic link callback . " )
630+ Issue . record ( " Expected sign-in result for second deduped route . " )
643631 throw ClerkClientError ( message: " Expected sign-in result. " )
644632 }
645633
646634 #expect( firstSignIn. createdSessionId == " sess_123 " )
647635 #expect( secondSignIn. createdSessionId == " sess_123 " )
648- #expect( signInCreateCount. value == 1 )
649- #expect( completionRequestCount. value == 1 )
650- #expect( activatedSessionId. value == " sess_123 " )
651- #expect( try keychain. hasItem ( forKey: ClerkKeychainKey . pendingMagicLinkFlow. rawValue) == false )
636+ #expect( invocationCount. value == 1 )
652637 }
653638
654639 @Test
@@ -696,14 +681,13 @@ struct AuthTests {
696681 activatedSessionId. setValue ( sessionId)
697682 } )
698683
699- let clerk = Clerk . shared
700- configureDependencies (
684+ let clerk = makeIsolatedClerk (
701685 signInService: signInService,
702686 sessionService: sessionService,
703687 keychain: keychain,
704688 baseURL: testBaseUrl
705689 )
706- let callbackUrl = try #require( URL ( string: " \( Clerk . shared . options. redirectConfig. redirectUrl) ?flow_id=flow_old&approval_token=approval_old " ) )
690+ let callbackUrl = try #require( URL ( string: " \( clerk . options. redirectConfig. redirectUrl) ?flow_id=flow_old&approval_token=approval_old " ) )
707691 try clerk. dependencies. magicLinkStore. save ( kind: . signIn, codeVerifier: " verifier_new " )
708692
709693 do {
@@ -721,8 +705,8 @@ struct AuthTests {
721705
722706 @Test
723707 func canHandleMagicLinkCallbackRejectsMismatchedOrigin( ) throws {
724- let clerk = Clerk . shared
725708 configureDependencies ( )
709+ let clerk = Clerk . shared
726710
727711 let mismatchedUrl = try #require( URL ( string: " https://example.com/callback?flow_id=flow_123&approval_token=approval_123 " ) )
728712
@@ -731,8 +715,8 @@ struct AuthTests {
731715
732716 @Test
733717 func handleMagicLinkCallbackRejectsMismatchedOrigin( ) async throws {
734- let clerk = Clerk . shared
735718 configureDependencies ( )
719+ let clerk = Clerk . shared
736720
737721 let mismatchedUrl = try #require( URL ( string: " https://example.com/callback?flow_id=flow_123&approval_token=approval_123 " ) )
738722
@@ -783,13 +767,13 @@ struct AuthTests {
783767 activatedSessionId. setValue ( sessionId)
784768 } )
785769
786- let clerk = Clerk . shared
787770 configureDependencies (
788771 signInService: signInService,
789772 sessionService: sessionService,
790773 keychain: keychain,
791774 baseURL: testBaseUrl
792775 )
776+ let clerk = Clerk . shared
793777 try clerk. dependencies. magicLinkStore. save ( kind: . signIn, codeVerifier: " verifier_123 " )
794778
795779 let result = try await clerk. auth. completeMagicLink ( flowId: " flow_123 " , approvalToken: " approval_123 " )
0 commit comments