@@ -68,8 +68,6 @@ class AuthenticationUtilitiesTest {
6868 private val handleScreenLockPolicy: (OAuth2 .IdServiceResponse ? , UserAccount ) -> Unit = mockk()
6969 private val handleBiometricAuthPolicy: (OAuth2 .IdServiceResponse ? , UserAccount ) -> Unit = mockk()
7070 private val handleDuplicateUserAccount: (UserAccountManager , UserAccount , OAuth2 .IdServiceResponse ? ) -> Unit = mockk()
71- private var blockIntegrationUser: Boolean = false
72- private var nativeLogin: Boolean = false
7371
7472 @Before
7573 fun setUp () {
@@ -97,16 +95,13 @@ class AuthenticationUtilitiesTest {
9795 every { mockUserAccountManager.createAccount(any()) } returns mockk< android.os.Bundle > ()
9896 every { mockUserAccountManager.switchToUser(any()) } returns Unit
9997 every { mockUserAccountManager.sendUserSwitchIntent(any(), any()) } returns Unit
100-
10198 }
10299
103100 @Test
104101 fun testOnAuthFlowComplete_blockIntegrationUser_shouldCallError () = runTest {
105- // Given
106- blockIntegrationUser = true
107102
108103 // When
109- callOnAuthFlowComplete()
104+ callOnAuthFlowComplete(blockIntegrationUser = true )
110105
111106 // Then
112107 verify { onAuthFlowError.invoke(" Error" , " Authentication error. Please try again." , null ) }
@@ -151,7 +146,7 @@ class AuthenticationUtilitiesTest {
151146 .accountName(buildAccountName(userIdentity.username, tokenResponse.instanceUrl))
152147 .loginServer(" https://login.salesforce.com" )
153148 .clientId(" test_consumer_key" )
154- .nativeLogin(nativeLogin )
149+ .nativeLogin(false )
155150 .build()
156151
157152 // When
@@ -177,7 +172,7 @@ class AuthenticationUtilitiesTest {
177172 val tokenResponseWithoutIdScope = createTokenEndpointResponse(
178173 scope = " refresh_token" // Missing id scope
179174 )
180-
175+
181176 // Mock fetchUserIdentity to return null (simulating no id scope)
182177 coEvery { fetchUserIdentity.invoke(any()) } returns null
183178
@@ -188,7 +183,7 @@ class AuthenticationUtilitiesTest {
188183 .accountName(buildAccountName(null , tokenResponseWithoutIdScope.instanceUrl))
189184 .loginServer(" https://login.salesforce.com" )
190185 .clientId(" test_consumer_key" )
191- .nativeLogin(nativeLogin )
186+ .nativeLogin(false )
192187 .build()
193188
194189 // When
@@ -206,12 +201,167 @@ class AuthenticationUtilitiesTest {
206201 verify { startMainActivity.invoke() }
207202 verify { handleScreenLockPolicy.invoke(null , expectedAccount) }
208203 verify { handleBiometricAuthPolicy.invoke(null , expectedAccount) }
209-
204+
210205 // Verify that fetchUserIdentity was called but returned null
211206 coVerify(exactly = 1 ) { fetchUserIdentity.invoke(tokenResponseWithoutIdScope) }
212207 }
208+ @Test
209+ fun testOnAuthFlowComplete_withNativeLogin_shouldCallSuccess () = runTest {
210+ // Given
211+ val tokenResponseWithoutIdScope = createTokenEndpointResponse(
212+ scope = " refresh_token" // Missing id scope
213+ )
214+
215+ // Mock fetchUserIdentity to return null (simulating no id scope)
216+ coEvery { fetchUserIdentity.invoke(any()) } returns null
217+
218+ // Create the expected UserAccount object without IdServiceResponse population
219+ val expectedAccount = UserAccountBuilder .getInstance()
220+ .populateFromTokenEndpointResponse(tokenResponseWithoutIdScope)
221+ .populateFromIdServiceResponse(null ) // No identity service response
222+ .accountName(buildAccountName(null , tokenResponseWithoutIdScope.instanceUrl))
223+ .loginServer(" https://login.salesforce.com" )
224+ .clientId(" test_consumer_key" )
225+ .nativeLogin(true ) // Expect true
226+ .build()
227+
228+ // When
229+ callOnAuthFlowComplete(
230+ customTokenResponse = tokenResponseWithoutIdScope,
231+ nativeLogin = true ,
232+ )
233+
234+ // Then
235+ verify(exactly = 0 ) { onAuthFlowError.invoke(any(), any(), any()) }
236+ verify { onAuthFlowSuccess.invoke(expectedAccount) }
237+ verify { mockUserAccountManager.createAccount(expectedAccount) }
238+ verify { mockUserAccountManager.switchToUser(expectedAccount) }
239+ verify { setAdministratorPreferences.invoke(null , expectedAccount) }
240+ verify { handleDuplicateUserAccount.invoke(mockUserAccountManager, expectedAccount, null ) }
241+ verify { addAccount.invoke(expectedAccount) }
242+ verify { updateLoggingPrefs.invoke(expectedAccount) }
243+ verify { startMainActivity.invoke() }
244+ verify { handleScreenLockPolicy.invoke(null , expectedAccount) }
245+ verify { handleBiometricAuthPolicy.invoke(null , expectedAccount) }
246+
247+ // Verify that fetchUserIdentity was called but returned null
248+ coVerify(exactly = 1 ) { fetchUserIdentity.invoke(tokenResponseWithoutIdScope) }
249+ }
250+
251+
252+ // region Token Migration Tests
253+
254+ @Test
255+ fun testOnAuthFlowComplete_tokenMigration_shouldNotCallStartMainActivity () = runTest {
256+ // Given
257+ val userIdentity = createIdServiceResponse()
258+ coEvery { fetchUserIdentity.invoke(any()) } returns userIdentity
259+
260+ // When - tokenMigration is true
261+ callOnAuthFlowComplete(tokenMigration = true )
262+
263+ // Then - startMainActivity should NOT be called during token migration
264+ verify(exactly = 0 ) { startMainActivity.invoke() }
265+ // But onAuthFlowSuccess should still be called
266+ verify(exactly = 1 ) { onAuthFlowSuccess.invoke(any()) }
267+ }
268+
269+ @Test
270+ fun testOnAuthFlowComplete_tokenMigration_shouldCallSuccess () = runTest {
271+ // Given
272+ val tokenResponse = createTokenEndpointResponse()
273+ val userIdentity = createIdServiceResponse()
274+ coEvery { fetchUserIdentity.invoke(any()) } returns userIdentity
275+
276+ // Create the expected UserAccount object
277+ val expectedAccount = UserAccountBuilder .getInstance()
278+ .populateFromTokenEndpointResponse(tokenResponse)
279+ .populateFromIdServiceResponse(userIdentity)
280+ .accountName(buildAccountName(userIdentity.username, tokenResponse.instanceUrl))
281+ .loginServer(" https://login.salesforce.com" )
282+ .clientId(" test_consumer_key" )
283+ .nativeLogin(false )
284+ .build()
285+
286+ // When - tokenMigration is true
287+ callOnAuthFlowComplete(tokenMigration = true )
288+
289+ // Then - onAuthFlowSuccess should be called with the account
290+ verify(exactly = 1 ) { onAuthFlowSuccess.invoke(expectedAccount) }
291+ verify(exactly = 0 ) { onAuthFlowError.invoke(any(), any(), any()) }
292+ }
293+
294+ @Test
295+ fun testOnAuthFlowComplete_tokenMigration_shouldCreateAccount () = runTest {
296+ // Given
297+ val userIdentity = createIdServiceResponse()
298+ coEvery { fetchUserIdentity.invoke(any()) } returns userIdentity
299+
300+ // When - tokenMigration is true
301+ callOnAuthFlowComplete(tokenMigration = true )
302+
303+ // Then - account creation and user switch should still happen
304+ verify(exactly = 1 ) { mockUserAccountManager.createAccount(any()) }
305+ verify(exactly = 1 ) { mockUserAccountManager.switchToUser(any()) }
306+ verify(exactly = 1 ) { addAccount.invoke(any()) }
307+ }
308+
309+ @Test
310+ fun testOnAuthFlowComplete_tokenMigration_shouldHandleLockPolicies () = runTest {
311+ // Given
312+ val userIdentity = createIdServiceResponse()
313+ coEvery { fetchUserIdentity.invoke(any()) } returns userIdentity
314+
315+ // When - tokenMigration is true
316+ callOnAuthFlowComplete(tokenMigration = true )
317+
318+ // Then - policies should still be handled
319+ verify(exactly = 1 ) { handleScreenLockPolicy.invoke(any(), any()) }
320+ verify(exactly = 1 ) { handleBiometricAuthPolicy.invoke(any(), any()) }
321+ }
322+
323+ @Test
324+ fun testOnAuthFlowComplete_tokenMigration_withBlockedIntegrationUser_shouldCallError () = runTest {
325+ // When - tokenMigration is true but user is blocked
326+ callOnAuthFlowComplete(
327+ blockIntegrationUser = true ,
328+ tokenMigration = true ,
329+ )
330+
331+ // Then - error should still be called for blocked users
332+ verify { onAuthFlowError.invoke(" Error" , " Authentication error. Please try again." , null ) }
333+ verify(exactly = 0 ) { onAuthFlowSuccess.invoke(any()) }
334+ verify(exactly = 0 ) { startMainActivity.invoke() }
335+ }
336+
337+ @Test
338+ fun testOnAuthFlowComplete_tokenMigration_withManagedAppRequirement_shouldCallError () = runTest {
339+ // Given
340+ val userIdentityWithManagedAppRequirement = createIdServiceResponse(
341+ customPermissions = JSONObject ().apply {
342+ put(" must_be_managed_app" , true )
343+ }
344+ )
345+ coEvery { fetchUserIdentity.invoke(any()) } returns userIdentityWithManagedAppRequirement
346+ every { mockRuntimeConfig.isManagedApp } returns false
347+
348+ // When - tokenMigration is true but managed app required
349+ callOnAuthFlowComplete(tokenMigration = true )
350+
351+ // Then - error should still be called
352+ verify { onAuthFlowError.invoke(" Error" , " Authentication only allowed from managed device." , null ) }
353+ verify(exactly = 0 ) { onAuthFlowSuccess.invoke(any()) }
354+ verify(exactly = 0 ) { startMainActivity.invoke() }
355+ }
356+
357+ // endregion
213358
214- private suspend fun callOnAuthFlowComplete (customTokenResponse : OAuth2 .TokenEndpointResponse ? = null) {
359+ private suspend fun callOnAuthFlowComplete (
360+ customTokenResponse : OAuth2 .TokenEndpointResponse ? = null,
361+ nativeLogin : Boolean = false,
362+ tokenMigration : Boolean = false,
363+ blockIntegrationUser : Boolean = false,
364+ ) {
215365 onAuthFlowComplete(
216366 tokenResponse = customTokenResponse ? : createTokenEndpointResponse(),
217367 loginServer = " https://login.salesforce.com" ,
@@ -220,6 +370,7 @@ class AuthenticationUtilitiesTest {
220370 onAuthFlowSuccess = onAuthFlowSuccess,
221371 buildAccountName = buildAccountName,
222372 nativeLogin = nativeLogin,
373+ tokenMigration = tokenMigration,
223374 context = testContext,
224375 userAccountManager = mockUserAccountManager,
225376 blockIntegrationUser = blockIntegrationUser,
0 commit comments