@@ -43,7 +43,7 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
4343 }
4444
4545 override func tearDown( ) async throws {
46- LoginManager . shared. reset ( )
46+ LoginManager . shared. resetForTesting ( )
4747 resetViewController ( )
4848 }
4949
@@ -54,7 +54,147 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
5454
5555 XCTAssertTrue ( LoginManager . shared. isSetupFinished)
5656 }
57-
57+
58+ func testResetLoginManager( ) {
59+ // Ensure LoginManager is properly set up first
60+ XCTAssertTrue ( LoginManager . shared. isSetupFinished)
61+ XCTAssertNotNil ( Session . shared)
62+ XCTAssertNotNil ( AccessTokenStore . shared)
63+ XCTAssertNotNil ( LoginConfiguration . shared)
64+
65+ // Test resetting without active login process
66+ LoginManager . shared. reset ( )
67+
68+ // After reset, all shared instances should be nil and setup should be false
69+ XCTAssertFalse ( LoginManager . shared. isSetupFinished)
70+ XCTAssertNil ( Session . _shared)
71+ XCTAssertNil ( AccessTokenStore . _shared)
72+ XCTAssertNil ( LoginConfiguration . _shared)
73+
74+ // Test that we can reconfigure after reset
75+ let newURL = URL ( string: " https://newexample.com/auth " )
76+ LoginManager . shared. setup ( channelID: " 456 " , universalLinkURL: newURL)
77+
78+ // After reconfiguration, instances should exist again
79+ XCTAssertTrue ( LoginManager . shared. isSetupFinished)
80+ XCTAssertNotNil ( Session . shared)
81+ XCTAssertNotNil ( AccessTokenStore . shared)
82+ XCTAssertNotNil ( LoginConfiguration . shared)
83+ XCTAssertEqual ( LoginConfiguration . shared. channelID, " 456 " )
84+ XCTAssertEqual ( LoginConfiguration . shared. universalLinkURL, newURL)
85+ }
86+
87+ func testResetLoginManagerWithActiveProcess( ) {
88+ let expect = expectation ( description: " \( #file) _ \( #line) " )
89+
90+ // Mock a session that will never respond (to keep login process active)
91+ let delegateStub = SessionDelegateStub ( stubs: [ ] )
92+ Session . _shared = Session (
93+ configuration: LoginConfiguration . shared,
94+ delegate: delegateStub
95+ )
96+
97+ // Start a login process but don't complete it
98+ var process : LoginProcess !
99+ process = LoginManager . shared. login ( permissions: [ . profile] , in: setupViewController ( ) ) { loginResult in
100+ // This callback should receive a loginManagerReset error
101+ XCTAssertNotNil ( loginResult. error)
102+ if let error = loginResult. error {
103+ if case . generalError( let reason) = error {
104+ if case . loginManagerReset = reason {
105+ // Expected error when reset is called during login
106+ expect. fulfill ( )
107+ } else {
108+ XCTFail ( " Expected loginManagerReset error, got: \( reason) " )
109+ }
110+ } else {
111+ XCTFail ( " Expected generalError with loginManagerReset, got: \( error) " )
112+ }
113+ }
114+ }
115+
116+ // Verify process is active
117+ XCTAssertNotNil ( process)
118+ XCTAssertTrue ( LoginManager . shared. isAuthorizing)
119+
120+ // Reset the login manager while process is active
121+ LoginManager . shared. reset ( )
122+
123+ // After reset, no process should be active and setup should be false
124+ XCTAssertFalse ( LoginManager . shared. isSetupFinished)
125+ XCTAssertFalse ( LoginManager . shared. isAuthorizing)
126+
127+ waitForExpectations ( timeout: 1 , handler: nil )
128+
129+ // Re-setup after reset to avoid tearDown issues
130+ let url = URL ( string: " https://example.com/auth " )
131+ LoginManager . shared. setup ( channelID: " 123 " , universalLinkURL: url)
132+ }
133+
134+ func testResetKeepsTokenScopedByChannel( ) {
135+ let loginExpectation = expectation ( description: " \( #file) _ \( #line) _login " )
136+
137+ let delegateStub = SessionDelegateStub ( stubs: [
138+ . init( data: PostExchangeTokenRequest . successData, responseCode: 200 ) ,
139+ . init( data: GetUserProfileRequest . successData, responseCode: 200 )
140+ ] )
141+ Session . _shared = Session (
142+ configuration: LoginConfiguration . shared,
143+ delegate: delegateStub
144+ )
145+
146+ let viewController = setupViewController ( )
147+ var process : LoginProcess !
148+ var capturedToken : AccessToken ?
149+ process = LoginManager . shared. login ( permissions: [ . profile] , in: viewController) { result in
150+ switch result {
151+ case . success( let loginResult) :
152+ capturedToken = loginResult. accessToken
153+ XCTAssertEqual ( loginResult. accessToken. value, PostExchangeTokenRequest . successToken)
154+ XCTAssertEqual ( AccessTokenStore . shared. current? . value, PostExchangeTokenRequest . successToken)
155+ case . failure( let error) :
156+ XCTFail ( " Unexpected failure: \( error) " )
157+ }
158+
159+ loginExpectation. fulfill ( )
160+ } !
161+
162+ process. appUniversalLinkFlow = AppUniversalLinkFlow ( parameter: sampleFlowParameters)
163+
164+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
165+ let urlString = " \( Constant . thirdPartyAppReturnURL) ?code=123&state= \( process. processID) "
166+ let handled = process. resumeOpenURL ( url: URL ( string: urlString) !)
167+ XCTAssertTrue ( handled)
168+ }
169+
170+ waitForExpectations ( timeout: 2 , handler: nil )
171+
172+ guard let token = capturedToken else {
173+ XCTFail ( " Token should be captured after login " )
174+ return
175+ }
176+
177+ // After reset, in-memory singletons are cleared but the keychain token remains.
178+ LoginManager . shared. reset ( )
179+ XCTAssertFalse ( LoginManager . shared. isSetupFinished)
180+ XCTAssertNil ( LoginConfiguration . _shared)
181+ XCTAssertNil ( Session . _shared)
182+ XCTAssertNil ( AccessTokenStore . _shared)
183+
184+ // Setup with a different channel should not see the previous token.
185+ let secondaryURL = URL ( string: " https://alternate.example.com/auth " )
186+ LoginManager . shared. setup ( channelID: " 456 " , universalLinkURL: secondaryURL)
187+ XCTAssertTrue ( LoginManager . shared. isSetupFinished)
188+ XCTAssertNil ( AccessTokenStore . shared. current)
189+
190+ // Reset again to switch back to the original channel.
191+ LoginManager . shared. reset ( )
192+
193+ let primaryURL = URL ( string: " https://example.com/auth " )
194+ LoginManager . shared. setup ( channelID: " 123 " , universalLinkURL: primaryURL)
195+ XCTAssertEqual ( AccessTokenStore . shared. current? . value, token. value)
196+ }
197+
58198 func testLoginAction( ) {
59199 let expect = expectation ( description: " \( #file) _ \( #line) " )
60200
@@ -349,7 +489,7 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
349489 let mockAccessToken = try ! JSONDecoder ( ) . decode ( AccessToken . self, from: PostExchangeTokenRequest . successData)
350490
351491 // Create JWK from test RSA public key that matches the test JWT token
352- let testRSAPublicKeyPEM = """
492+ /* let testRSAPublicKeyPEM */ _ = """
353493 -----BEGIN PUBLIC KEY-----
354494 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
355495 UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
@@ -823,4 +963,3 @@ class LoginManagerTests: XCTestCase, ViewControllerCompatibleTest {
823963 }
824964
825965}
826-
0 commit comments