Skip to content

Commit ecdd9d2

Browse files
@W-21933885: [MSDK Android] App Attestation Implementation (Fix LoginViewModel Line 473 Coverage By Eliminating Null Branch And Add Comprehensive Test)
1 parent 30d3ca8 commit ecdd9d2

2 files changed

Lines changed: 99 additions & 51 deletions

File tree

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ open class LoginViewModel(val bootConfig: BootConfig) : ViewModel() {
459459

460460
val jwtFlow = !jwt.isNullOrBlank() && !authCodeForJwtFlow.isNullOrBlank()
461461
val additionalParams = when {
462-
jwtFlow -> null
462+
jwtFlow -> mutableMapOf()
463463
else -> additionalParameters
464464
}
465465

@@ -470,7 +470,7 @@ open class LoginViewModel(val bootConfig: BootConfig) : ViewModel() {
470470
sdkManager.appAttestationClient?.run {
471471
val challenge = fetchMobileAppAttestationChallenge()
472472
val attestation = createAppAttestation(challenge) ?: return@run
473-
additionalParams?.put(ATTESTATION, attestation)
473+
additionalParams[ATTESTATION] = attestation
474474
}
475475

476476
val authorizationUrl = OAuth2.getAuthorizationUrl(

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginViewModelTest.kt

Lines changed: 97 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,14 @@ class LoginViewModelTest {
275275

276276
// Verify the URL contains the boot config values
277277
val loginUrl = viewModel.loginUrl.value!!
278-
assertTrue("URL should contain boot config consumer key",
279-
loginUrl.contains(bootConfig.remoteAccessConsumerKey))
280-
assertTrue("URL should contain boot config redirect URI",
281-
loginUrl.contains("redirect_uri=${bootConfig.oauthRedirectURI}"))
278+
assertTrue(
279+
"URL should contain boot config consumer key",
280+
loginUrl.contains(bootConfig.remoteAccessConsumerKey)
281+
)
282+
assertTrue(
283+
"URL should contain boot config redirect URI",
284+
loginUrl.contains("redirect_uri=${bootConfig.oauthRedirectURI}")
285+
)
282286
}
283287

284288
@Test
@@ -309,8 +313,10 @@ class LoginViewModelTest {
309313
// Verify the URL contains the custom app config values
310314
val loginUrl = viewModel.loginUrl.value!!
311315
assertTrue("URL should contain app config consumer key", loginUrl.contains(customConsumerKey))
312-
assertTrue("URL should contain app config redirect URI",
313-
loginUrl.contains("redirect_uri=appconfig://redirect"))
316+
assertTrue(
317+
"URL should contain app config redirect URI",
318+
loginUrl.contains("redirect_uri=appconfig://redirect")
319+
)
314320
assertTrue("URL should contain app config scope", loginUrl.contains("app_config_scope"))
315321
} finally {
316322
sdkManager.appConfigForLoginHost = originalAppConfigForLoginHost
@@ -349,17 +355,25 @@ class LoginViewModelTest {
349355

350356
// Verify the URL contains the debug override values, not app config values
351357
val loginUrl = viewModel.loginUrl.value!!
352-
assertTrue("URL should contain debug override consumer key",
353-
loginUrl.contains(debugConsumerKey))
354-
assertTrue("URL should contain debug override redirect URI",
355-
loginUrl.contains("redirect_uri=debug://redirect"))
358+
assertTrue(
359+
"URL should contain debug override consumer key",
360+
loginUrl.contains(debugConsumerKey)
361+
)
362+
assertTrue(
363+
"URL should contain debug override redirect URI",
364+
loginUrl.contains("redirect_uri=debug://redirect")
365+
)
356366
assertTrue("URL should contain debug scope", loginUrl.contains("debug_scope"))
357367

358368
// Verify app config values are NOT in the URL
359-
assertFalse("URL should NOT contain app config consumer key",
360-
loginUrl.contains(appConfigConsumerKey))
361-
assertFalse("URL should NOT contain app config redirect URI",
362-
loginUrl.contains("should_not_be_used"))
369+
assertFalse(
370+
"URL should NOT contain app config consumer key",
371+
loginUrl.contains(appConfigConsumerKey)
372+
)
373+
assertFalse(
374+
"URL should NOT contain app config redirect URI",
375+
loginUrl.contains("should_not_be_used")
376+
)
363377
} finally {
364378
sdkManager.appConfigForLoginHost = originalAppConfigForLoginHost
365379
}
@@ -487,11 +501,13 @@ class LoginViewModelTest {
487501
redirectUri = "test://redirect",
488502
scopes = listOf("api", "test_scope"),
489503
)
504+
490505
server.contains("login.salesforce.com") -> OAuthConfig(
491506
consumerKey = "prod_consumer_key",
492507
redirectUri = "prod://redirect",
493508
scopes = listOf("api", "prod_scope"),
494509
)
510+
495511
else -> OAuthConfig(bootConfig)
496512
}
497513
}
@@ -500,23 +516,35 @@ class LoginViewModelTest {
500516
viewModel.selectedServer.value = "https://test.salesforce.com"
501517
Thread.sleep(200)
502518
var loginUrl = viewModel.loginUrl.value!!
503-
assertTrue("URL should contain test consumer key. URL: $loginUrl",
504-
loginUrl.contains("test_consumer_key"))
505-
assertTrue("URL should contain test redirect URI. URL: $loginUrl",
506-
loginUrl.contains("redirect_uri=test://redirect"))
507-
assertTrue("URL should contain test scope. URL: $loginUrl",
508-
loginUrl.contains("test_scope"))
519+
assertTrue(
520+
"URL should contain test consumer key. URL: $loginUrl",
521+
loginUrl.contains("test_consumer_key")
522+
)
523+
assertTrue(
524+
"URL should contain test redirect URI. URL: $loginUrl",
525+
loginUrl.contains("redirect_uri=test://redirect")
526+
)
527+
assertTrue(
528+
"URL should contain test scope. URL: $loginUrl",
529+
loginUrl.contains("test_scope")
530+
)
509531

510532
// Test with production server
511533
viewModel.selectedServer.value = "https://login.salesforce.com"
512534
Thread.sleep(200)
513535
loginUrl = viewModel.loginUrl.value!!
514-
assertTrue("URL should contain prod consumer key. URL: $loginUrl",
515-
loginUrl.contains("prod_consumer_key"))
516-
assertTrue("URL should contain prod redirect URI. URL: $loginUrl",
517-
loginUrl.contains("redirect_uri=prod://redirect"))
518-
assertTrue("URL should contain prod scope. URL: $loginUrl",
519-
loginUrl.contains("prod_scope"))
536+
assertTrue(
537+
"URL should contain prod consumer key. URL: $loginUrl",
538+
loginUrl.contains("prod_consumer_key")
539+
)
540+
assertTrue(
541+
"URL should contain prod redirect URI. URL: $loginUrl",
542+
loginUrl.contains("redirect_uri=prod://redirect")
543+
)
544+
assertTrue(
545+
"URL should contain prod scope. URL: $loginUrl",
546+
loginUrl.contains("prod_scope")
547+
)
520548
} finally {
521549
sdkManager.appConfigForLoginHost = originalAppConfigForLoginHost
522550
}
@@ -540,11 +568,15 @@ class LoginViewModelTest {
540568
// Verify the URL is generated correctly without scopes
541569
val loginUrl = viewModel.loginUrl.value!!
542570
assertTrue("URL should contain custom consumer key", loginUrl.contains(customConsumerKey))
543-
assertTrue("URL should contain custom redirect URI",
544-
loginUrl.contains("redirect_uri=noscopes://redirect"))
571+
assertTrue(
572+
"URL should contain custom redirect URI",
573+
loginUrl.contains("redirect_uri=noscopes://redirect")
574+
)
545575
// URL should still be valid even without explicit scopes
546-
assertTrue("URL should be a valid OAuth URL",
547-
loginUrl.contains("/services/oauth2/authorize"))
576+
assertTrue(
577+
"URL should be a valid OAuth URL",
578+
loginUrl.contains("/services/oauth2/authorize")
579+
)
548580
}
549581

550582
@Test
@@ -584,8 +616,10 @@ class LoginViewModelTest {
584616

585617
// Verify URL was set to ABOUT_BLANK for User Agent Flow
586618
// NOTE: If this is flaky we should use Turbine to test the actual state changes.
587-
assertEquals("loginUrl should be set to ABOUT_BLANK for User Agent Flow",
588-
ABOUT_BLANK, viewModel.loginUrl.value)
619+
assertEquals(
620+
"loginUrl should be set to ABOUT_BLANK for User Agent Flow",
621+
ABOUT_BLANK, viewModel.loginUrl.value
622+
)
589623

590624
// Wait for the new authorization URL to be generated
591625
Thread.sleep(200)
@@ -617,8 +651,10 @@ class LoginViewModelTest {
617651
Thread.sleep(50)
618652

619653
// Verify URL was NOT set to ABOUT_BLANK for Web Server Flow
620-
assertNotEquals("loginUrl should NOT be ABOUT_BLANK for Web Server Flow",
621-
ABOUT_BLANK, viewModel.loginUrl.value)
654+
assertNotEquals(
655+
"loginUrl should NOT be ABOUT_BLANK for Web Server Flow",
656+
ABOUT_BLANK, viewModel.loginUrl.value
657+
)
622658

623659
// Wait for the new authorization URL to be generated
624660
Thread.sleep(200)
@@ -627,8 +663,10 @@ class LoginViewModelTest {
627663
val newUrl = viewModel.loginUrl.value
628664
assertNotNull("New URL should not be null", newUrl)
629665
assertNotEquals("New URL should not be ABOUT_BLANK", ABOUT_BLANK, newUrl)
630-
assertNotEquals("New URL should be different from initial (different code challenge)",
631-
initialUrl, newUrl)
666+
assertNotEquals(
667+
"New URL should be different from initial (different code challenge)",
668+
initialUrl, newUrl
669+
)
632670
}
633671

634672
@Test
@@ -645,8 +683,10 @@ class LoginViewModelTest {
645683
Thread.sleep(200)
646684

647685
// Verify URL did not change
648-
assertEquals("loginUrl should not change when selectedServer is null",
649-
initialUrl, viewModel.loginUrl.value)
686+
assertEquals(
687+
"loginUrl should not change when selectedServer is null",
688+
initialUrl, viewModel.loginUrl.value
689+
)
650690
}
651691

652692
@Test
@@ -667,15 +707,21 @@ class LoginViewModelTest {
667707

668708
// Verify the URL contains the boot config values (fallback)
669709
val loginUrl = viewModel.loginUrl.value!!
670-
assertTrue("URL should contain boot config consumer key when appConfigForLoginHost returns null",
671-
loginUrl.contains(bootConfig.remoteAccessConsumerKey))
672-
assertTrue("URL should contain boot config redirect URI when appConfigForLoginHost returns null",
673-
loginUrl.contains("redirect_uri=${bootConfig.oauthRedirectURI}"))
710+
assertTrue(
711+
"URL should contain boot config consumer key when appConfigForLoginHost returns null",
712+
loginUrl.contains(bootConfig.remoteAccessConsumerKey)
713+
)
714+
assertTrue(
715+
"URL should contain boot config redirect URI when appConfigForLoginHost returns null",
716+
loginUrl.contains("redirect_uri=${bootConfig.oauthRedirectURI}")
717+
)
674718

675719
// Verify boot config scopes are present
676720
bootConfig.oauthScopes.forEach { scope ->
677-
assertTrue("URL should contain boot config scope '$scope' when appConfigForLoginHost returns null",
678-
loginUrl.contains(scope))
721+
assertTrue(
722+
"URL should contain boot config scope '$scope' when appConfigForLoginHost returns null",
723+
loginUrl.contains(scope)
724+
)
679725
}
680726
} finally {
681727
sdkManager.appConfigForLoginHost = originalAppConfigForLoginHost
@@ -1173,11 +1219,13 @@ class LoginViewModelTest {
11731219

11741220
advanceUntilIdle()
11751221

1176-
coVerify(exactly = 1) { viewModel.getAuthorizationUrl(
1177-
value,
1178-
any(),
1179-
any(),
1180-
) }
1222+
coVerify(exactly = 1) {
1223+
viewModel.getAuthorizationUrl(
1224+
value,
1225+
any(),
1226+
any(),
1227+
)
1228+
}
11811229
}
11821230

11831231
@Test

0 commit comments

Comments
 (0)