Skip to content

Commit 1304062

Browse files
@W-21933885: [MSDK Android] App Attestation Implementation (Automated Implementation Of New Tests For fetchMobileAppAttestationChallenge)
1 parent f984d45 commit 1304062

1 file changed

Lines changed: 108 additions & 0 deletions

File tree

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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@ import com.google.android.play.core.integrity.StandardIntegrityManager.StandardI
3535
import com.google.android.play.core.integrity.StandardIntegrityManager.StandardIntegrityTokenProvider
3636
import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode.INTEGRITY_TOKEN_PROVIDER_INVALID
3737
import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode.INTERNAL_ERROR
38+
import com.salesforce.androidsdk.rest.AppAttestationChallengeApiException
3839
import com.salesforce.androidsdk.rest.RestClient
40+
import com.salesforce.androidsdk.rest.RestRequest
3941
import com.salesforce.androidsdk.rest.RestResponse
42+
import io.mockk.CapturingSlot
4043
import io.mockk.coEvery
4144
import io.mockk.every
4245
import io.mockk.mockk
4346
import io.mockk.mockkStatic
47+
import io.mockk.slot
4448
import io.mockk.verify
4549
import kotlinx.coroutines.ExperimentalCoroutinesApi
4650
import kotlinx.coroutines.tasks.await
@@ -49,6 +53,8 @@ import kotlinx.coroutines.test.runTest
4953
import kotlinx.serialization.json.Json
5054
import org.junit.Assert.assertEquals
5155
import org.junit.Assert.assertNull
56+
import org.junit.Assert.assertThrows
57+
import org.junit.Assert.assertTrue
5258
import org.junit.Test
5359
import org.junit.runner.RunWith
5460

@@ -465,6 +471,61 @@ class AppAttestationClientTest {
465471
assertEquals("eyJhdHRlc3RhdGlvbklkIjoiMTIzNDU2IiwiYXR0ZXN0YXRpb25EYXRhIjoiWDE5VVJWTlVYMGxPVkVWSFVrbFVXVjlVVDB0RlRsOWYifQ", result)
466472
}
467473

474+
@Test
475+
fun appAttestationClient_fetchMobileAppAttestationChallenge_OnSuccess_ReturnsChallenge() {
476+
477+
val requestSlot = slot<RestRequest>()
478+
val restClient = createRestClientReturning(
479+
restResponse = createRestResponse(body = TEST_CHALLENGE_VALUE, success = true),
480+
requestSlot = requestSlot,
481+
)
482+
val appAttestationClient = createAppAttestationClientForTest(restClient = restClient)
483+
484+
val result = appAttestationClient.fetchMobileAppAttestationChallenge()
485+
486+
assertEquals(TEST_CHALLENGE_VALUE, result)
487+
val requestedPath = requestSlot.captured.path
488+
assertTrue(
489+
"Request URL should target the attestation challenge endpoint at '$TEST_API_HOST_NAME' but was '$requestedPath'.",
490+
requestedPath.startsWith("https://$TEST_API_HOST_NAME/mobile/attest/challenge"),
491+
)
492+
assertTrue(
493+
"Request URL should contain 'attestationId=$TEST_DEVICE_ID' but was '$requestedPath'.",
494+
requestedPath.contains("attestationId=$TEST_DEVICE_ID"),
495+
)
496+
assertTrue(
497+
"Request URL should contain 'consumerKey=$TEST_REMOTE_ACCESS_CONSUMER_KEY' but was '$requestedPath'.",
498+
requestedPath.contains("consumerKey=$TEST_REMOTE_ACCESS_CONSUMER_KEY"),
499+
)
500+
verify(exactly = 1) { restClient.sendSync(any()) }
501+
}
502+
503+
@Test
504+
fun appAttestationClient_fetchMobileAppAttestationChallenge_OnFailureResponse_ThrowsException() {
505+
506+
val restClient = createRestClientReturning(
507+
restResponse = createRestResponse(body = "__ERROR_BODY__", success = false),
508+
)
509+
val appAttestationClient = createAppAttestationClientForTest(restClient = restClient)
510+
511+
assertThrows(AppAttestationChallengeApiException::class.java) {
512+
appAttestationClient.fetchMobileAppAttestationChallenge()
513+
}
514+
}
515+
516+
@Test
517+
fun appAttestationClient_fetchMobileAppAttestationChallenge_OnNullResponseBody_ThrowsException() {
518+
519+
val restClient = createRestClientReturning(
520+
restResponse = createRestResponse(body = null, success = true),
521+
)
522+
val appAttestationClient = createAppAttestationClientForTest(restClient = restClient)
523+
524+
assertThrows(AppAttestationChallengeApiException::class.java) {
525+
appAttestationClient.fetchMobileAppAttestationChallenge()
526+
}
527+
}
528+
468529
@Test
469530
fun oAuthAuthorizationAttestation_encode_returnsSuccessfully() {
470531

@@ -494,4 +555,51 @@ class AppAttestationClientTest {
494555
fun oAuthAuthorizationAttestation_serializerDescriptor_hasCorrectElementCount() {
495556
assertEquals(2, OAuthAuthorizationAttestation.serializer().descriptor.elementsCount)
496557
}
558+
559+
// region Helpers
560+
561+
private fun createAppAttestationClientForTest(
562+
restClient: RestClient,
563+
): AppAttestationClient {
564+
val integrityTokenProviderTask = mockk<Task<StandardIntegrityTokenProvider>>(relaxed = true)
565+
every { integrityTokenProviderTask.addOnSuccessListener(any()) } returns integrityTokenProviderTask
566+
every { integrityTokenProviderTask.addOnFailureListener(any()) } returns integrityTokenProviderTask
567+
val integrityManager = mockk<StandardIntegrityManager>(relaxed = true)
568+
every { integrityManager.prepareIntegrityToken(any()) } returns integrityTokenProviderTask
569+
570+
return AppAttestationClient(
571+
apiHostName = TEST_API_HOST_NAME,
572+
context = mockk<Context>(relaxed = true),
573+
deviceId = TEST_DEVICE_ID,
574+
googleCloudProjectId = TEST_GOOGLE_CLOUD_PROJECT_ID,
575+
integrityManager = integrityManager,
576+
remoteAccessConsumerKey = TEST_REMOTE_ACCESS_CONSUMER_KEY,
577+
restClient = restClient,
578+
)
579+
}
580+
581+
private fun createRestResponse(
582+
body: String?,
583+
success: Boolean,
584+
): RestResponse = mockk<RestResponse>(relaxed = true).also { response ->
585+
every { response.asString() } returns body
586+
every { response.isSuccess } returns success
587+
}
588+
589+
private fun createRestClientReturning(
590+
restResponse: RestResponse,
591+
requestSlot: CapturingSlot<RestRequest> = slot(),
592+
): RestClient = mockk<RestClient>(relaxed = true).also { client ->
593+
every { client.sendSync(capture(requestSlot)) } returns restResponse
594+
}
595+
596+
// endregion Helpers
597+
598+
private companion object {
599+
const val TEST_API_HOST_NAME = "login.example.com"
600+
const val TEST_DEVICE_ID = "123456"
601+
const val TEST_GOOGLE_CLOUD_PROJECT_ID = 654321L
602+
const val TEST_REMOTE_ACCESS_CONSUMER_KEY = "13579"
603+
const val TEST_CHALLENGE_VALUE = "__TEST_CHALLENGE_VALUE__"
604+
}
497605
}

0 commit comments

Comments
 (0)