Skip to content

Commit e64a8b1

Browse files
committed
Refactor auth magic link tests
Use isolated Clerk instances in callback tests to avoid shared state leaking through `Clerk.shared`. Simplify concurrent callback deduplication coverage by testing `URLHandlingCoordinator` directly.
1 parent 816ffe7 commit e64a8b1

1 file changed

Lines changed: 62 additions & 78 deletions

File tree

Tests/Domains/Auth/AuthTests.swift

Lines changed: 62 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)