diff --git a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTest.kt b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTest.kt index 0058fb5c0..9021590bf 100644 --- a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTest.kt +++ b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTest.kt @@ -23,20 +23,29 @@ import com.amplifyframework.api.aws.AWSApiPlugin import com.amplifyframework.api.rest.RestOptions import com.amplifyframework.auth.AuthUserAttribute import com.amplifyframework.auth.AuthUserAttributeKey +import com.amplifyframework.auth.cognito.exceptions.service.UserNotFoundException import com.amplifyframework.auth.cognito.options.AWSCognitoAuthSignInOptions import com.amplifyframework.auth.cognito.options.AuthFlowType import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult import com.amplifyframework.auth.cognito.testutils.Credentials +import com.amplifyframework.auth.cognito.testutils.callAmplify +import com.amplifyframework.auth.cognito.testutils.invokeAmplify +import com.amplifyframework.auth.exceptions.InvalidStateException +import com.amplifyframework.auth.exceptions.SignedOutException import com.amplifyframework.auth.options.AuthFetchSessionOptions import com.amplifyframework.auth.options.AuthSignOutOptions import com.amplifyframework.auth.options.AuthSignUpOptions import com.amplifyframework.core.Amplify +import com.amplifyframework.testutils.coroutines.runBlockingWithTimeout import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import kotlin.test.assertFailsWith +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert.assertTrue @@ -104,142 +113,98 @@ class AuthCanaryTest { } @Test - fun signUp() { - val latch = CountDownLatch(1) + fun signUp() = runTest(timeout = TIMEOUT_S.seconds) { val options = AuthSignUpOptions.builder() .userAttribute(AuthUserAttributeKey.email(), "my@email.com") .build() - Amplify.Auth.signUp( - tempUsername, - tempPassword, - options, - { - signedUpNewUser = true - latch.countDown() - }, - { fail("Sign up failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val signUpResult = callAmplify { onSuccess, onFailure -> + signUp(tempUsername, tempPassword, options, onSuccess, onFailure) + } + signedUpNewUser = true } // Test requires confirmation code, testing onError call. @Test fun confirmSignUp() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmSignUp( - "username", - "the code you received via email", - { fail("Confirm sign up completed successfully, expected confirm sign up to fail") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + val result = + callAmplify { onSuccess, onFailure -> + confirmSignUp("username", "code", onSuccess, onFailure) + } + } + } } @Test - fun signIn() { - val latch = CountDownLatch(1) + fun signIn() = runTest(timeout = TIMEOUT_S.seconds) { val options = AWSCognitoAuthSignInOptions.builder().authFlowType(AuthFlowType.USER_SRP_AUTH).build() - Amplify.Auth.signIn( - username, - password, - options, - { result -> - if (result.isSignedIn) { - latch.countDown() - } else { - fail("Sign in not complete") - } - }, - { fail("Failed to sign in: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val signInResult = callAmplify { onSuccess, onFailure -> + signIn(username, password, options, onSuccess, onFailure) + } + assertTrue(signInResult.isSignedIn) } // Test requires confirmation code, testing onError call @Test fun confirmSignIn() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmSignIn( - "confirmation code", - { fail("Confirm sign in completed successfully, expected confirm sign in to fail") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + val result = + callAmplify { onSuccess, onFailure -> + confirmSignIn("confirmation code", onSuccess, onFailure) + } + } + } } @Test - fun fetchAuthSession() { + fun fetchAuthSession() = runTest(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchAuthSession( - { latch.countDown() }, - { fail("Failed to fetch session: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } @Test - fun fetchAuthSessionWithRefresh() { + fun fetchAuthSessionWithRefresh() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) val option = AuthFetchSessionOptions.builder().forceRefresh(true).build() - Amplify.Auth.fetchAuthSession( - option, - { latch.countDown() }, - { fail("Failed to fetch session: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(option, onSuccess, onFailure) } } @Test - fun rememberDevice() { + fun rememberDevice() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.rememberDevice( - { latch.countDown() }, - { fail("Remember device failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> rememberDevice(onSuccess, onFailure) } } @Test - fun forgetDevice() { + fun forgetDevice() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.forgetDevice( - { latch.countDown() }, - { fail("Forget device failed with error: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> forgetDevice(onSuccess, onFailure) } } @Test - fun fetchDevices() { + fun fetchDevices() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchDevices( - { latch.countDown() }, - { fail("Fetch devices failed with error: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val devices = callAmplify { onSuccess, onFailure -> fetchDevices(onSuccess, onFailure) } } // Test requires confirmation code, testing onError call @Test fun confirmResetPassword() { - val latch = CountDownLatch(1) - try { - Amplify.Auth.confirmResetPassword( - "username", - "NewPassword123", - "confirmation code", - { fail("New password confirmed, expected confirm reset password to fail") }, - { latch.countDown() } - ) - } catch (e: Exception) { - fail(e.toString()) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + invokeAmplify { onSuccess, onFailure -> + confirmResetPassword( + "username", + "NewPassword123", + "code", + onSuccess, + onFailure + ) + } + } } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test @@ -258,131 +223,101 @@ class AuthCanaryTest { } @Test - fun fetchUserAttributes() { + fun fetchUserAttributes() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchUserAttributes( - { latch.countDown() }, - { fail("Failed to fetch user attributes: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> fetchUserAttributes(onSuccess, onFailure) } } @Test - fun updateUserAttribute() { + fun updateUserAttribute() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.updateUserAttribute( - AuthUserAttribute(AuthUserAttributeKey.name(), "apitest"), - { latch.countDown() }, - { fail("Failed to update user attribute: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> + updateUserAttribute(AuthUserAttribute(AuthUserAttributeKey.name(), "apitest"), onSuccess, onFailure) + } } @Test - fun updateUserAttributes() { + fun updateUserAttributes() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.updateUserAttributes( - attributes, // attributes is a list of AuthUserAttribute - { latch.countDown() }, - { fail("Failed to update user attributes: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> updateUserAttributes(attributes, onSuccess, onFailure) } } // Test requires confirmation code, testing onError call @Test fun confirmUserAttribute() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmUserAttribute( - AuthUserAttributeKey.email(), - "344299", - { fail("Confirmed user attribute with incorrect code, expected confirm user attribute to fail.") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + invokeAmplify { onSuccess, onFailure -> + confirmUserAttribute( + AuthUserAttributeKey.email(), + "344299", + onSuccess, + onFailure + ) + } + } + } } @Test - fun getCurrentUser() { + fun getCurrentUser() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.getCurrentUser( - { latch.countDown() }, - { fail("Get current user failed with an exception: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val user = callAmplify { onSuccess, onFailure -> getCurrentUser(onSuccess, onFailure) } } @Test - fun signOut() { + fun signOut() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.signOut { signOutResult -> - when (signOutResult) { - is AWSCognitoAuthSignOutResult.CompleteSignOut -> { - // Sign Out completed fully and without errors. - latch.countDown() - } - is AWSCognitoAuthSignOutResult.PartialSignOut -> { - // Sign Out completed with some errors. User is signed out of the device. - signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } - signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } - signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } - } - is AWSCognitoAuthSignOutResult.FailedSignOut -> { - // Sign Out failed with an exception, leaving the user signed in. - fail("Sign out failed: ${signOutResult.exception}") - } + val signOutResult = callAmplify { onSuccess, _ -> signOut(onSuccess) } + val result = when (signOutResult) { + is AWSCognitoAuthSignOutResult.CompleteSignOut -> { + // Sign Out completed fully and without errors. + } + is AWSCognitoAuthSignOutResult.PartialSignOut -> { + // Sign Out completed with some errors. User is signed out of the device. + signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } + signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } + signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } + } + is AWSCognitoAuthSignOutResult.FailedSignOut -> { + // Sign Out failed with an exception, leaving the user signed in. + fail("Sign out failed: ${signOutResult.exception}") } + else -> fail("Unexpected sign out result occurred") } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test - fun globalSignOut() { + fun globalSignOut() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) val options = AuthSignOutOptions.builder() .globalSignOut(true) .build() - Amplify.Auth.signOut(options) { signOutResult -> - when (signOutResult) { - is AWSCognitoAuthSignOutResult.CompleteSignOut -> { - // Sign Out completed fully and without errors. - latch.countDown() - } - is AWSCognitoAuthSignOutResult.PartialSignOut -> { - // Sign Out completed with some errors. User is signed out of the device. - signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } - signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } - signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } - } - is AWSCognitoAuthSignOutResult.FailedSignOut -> { - // Sign Out failed with an exception, leaving the user signed in. - fail("Sign out failed: ${signOutResult.exception}") - } + val signOutResult = callAmplify { onSuccess, _ -> signOut(options, onSuccess) } + val result = when (signOutResult) { + is AWSCognitoAuthSignOutResult.CompleteSignOut -> { + // Sign Out completed fully and without errors. + } + is AWSCognitoAuthSignOutResult.PartialSignOut -> { + // Sign Out completed with some errors. User is signed out of the device. + signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } + signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } + signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } } + is AWSCognitoAuthSignOutResult.FailedSignOut -> { + // Sign Out failed with an exception, leaving the user signed in. + fail("Sign out failed: ${signOutResult.exception}") + } + else -> fail("Unexpected sign out result occurred") } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test - fun deleteUser() { + fun deleteUser() = runBlockingWithTimeout(TIMEOUT_S.seconds) { signUpUser(tempUsername, tempPassword) confirmTemporaryUserSignUp(tempUsername) signInUser(tempUsername, tempPassword) - val latch = CountDownLatch(1) - Amplify.Auth.deleteUser( - { - signedUpNewUser = false - latch.countDown() - }, - { fail("Delete user failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> deleteUser(onSuccess, onFailure) } } private fun signUpUser(user: String, pass: String) { diff --git a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTestGen2.kt b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTestGen2.kt index 4bdf413ee..b1949dded 100644 --- a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTestGen2.kt +++ b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthCanaryTestGen2.kt @@ -23,23 +23,32 @@ import com.amplifyframework.api.aws.AWSApiPlugin import com.amplifyframework.api.rest.RestOptions import com.amplifyframework.auth.AuthUserAttribute import com.amplifyframework.auth.AuthUserAttributeKey +import com.amplifyframework.auth.cognito.exceptions.service.UserNotFoundException import com.amplifyframework.auth.cognito.options.AWSCognitoAuthSignInOptions import com.amplifyframework.auth.cognito.options.AuthFlowType import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult import com.amplifyframework.auth.cognito.test.R import com.amplifyframework.auth.cognito.testutils.Credentials +import com.amplifyframework.auth.cognito.testutils.callAmplify +import com.amplifyframework.auth.cognito.testutils.invokeAmplify +import com.amplifyframework.auth.exceptions.InvalidStateException +import com.amplifyframework.auth.exceptions.SignedOutException import com.amplifyframework.auth.options.AuthFetchSessionOptions import com.amplifyframework.auth.options.AuthSignOutOptions import com.amplifyframework.auth.options.AuthSignUpOptions import com.amplifyframework.core.Amplify import com.amplifyframework.core.AmplifyConfiguration import com.amplifyframework.core.configuration.AmplifyOutputs +import com.amplifyframework.testutils.coroutines.runBlockingWithTimeout import java.util.UUID import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import kotlin.test.assertFailsWith +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Assert.assertTrue @@ -114,285 +123,211 @@ class AuthCanaryTestGen2 { } @Test - fun signUp() { - val latch = CountDownLatch(1) + fun signUp() = runTest(timeout = TIMEOUT_S.seconds) { val options = AuthSignUpOptions.builder() .userAttribute(AuthUserAttributeKey.email(), "my@email.com") .build() - Amplify.Auth.signUp( - tempUsername, - tempPassword, - options, - { - signedUpNewUser = true - latch.countDown() - }, - { fail("Sign up failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val signUpResult = callAmplify { onSuccess, onFailure -> + signUp(tempUsername, tempPassword, options, onSuccess, onFailure) + } + signedUpNewUser = true } // Test requires confirmation code, testing onError call. @Test fun confirmSignUp() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmSignUp( - "username", - "the code you received via email", - { fail("Confirm sign up completed successfully, expected confirm sign up to fail") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + val result = + callAmplify { onSuccess, onFailure -> + confirmSignUp("username", "code", onSuccess, onFailure) + } + } + } } @Test - fun signIn() { - val latch = CountDownLatch(1) + fun signIn() = runTest(timeout = TIMEOUT_S.seconds) { val options = AWSCognitoAuthSignInOptions.builder().authFlowType(AuthFlowType.USER_SRP_AUTH).build() - Amplify.Auth.signIn( - username, - password, - options, - { result -> - if (result.isSignedIn) { - latch.countDown() - } else { - fail("Sign in not complete") - } - }, - { fail("Failed to sign in: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val signInResult = callAmplify { onSuccess, onFailure -> + signIn(username, password, options, onSuccess, onFailure) + } + assertTrue(signInResult.isSignedIn) } // Test requires confirmation code, testing onError call @Test fun confirmSignIn() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmSignIn( - "confirmation code", - { fail("Confirm sign in completed successfully, expected confirm sign in to fail") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + val result = + callAmplify { onSuccess, onFailure -> + confirmSignIn("code", onSuccess, onFailure) + } + } + } } @Test - fun fetchAuthSession() { + fun fetchAuthSession() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchAuthSession( - { latch.countDown() }, - { fail("Failed to fetch session: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } @Test - fun fetchAuthSessionWithRefresh() { + fun fetchAuthSessionWithRefresh() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) val option = AuthFetchSessionOptions.builder().forceRefresh(true).build() - Amplify.Auth.fetchAuthSession( - option, - { latch.countDown() }, - { fail("Failed to fetch session: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(option, onSuccess, onFailure) } } @Test - fun rememberDevice() { + fun rememberDevice() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.rememberDevice( - { latch.countDown() }, - { fail("Remember device failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> rememberDevice(onSuccess, onFailure) } } @Test - fun forgetDevice() { + fun forgetDevice() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.forgetDevice( - { latch.countDown() }, - { fail("Forget device failed with error: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> forgetDevice(onSuccess, onFailure) } } @Test - fun fetchDevices() { + fun fetchDevices() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchDevices( - { latch.countDown() }, - { fail("Fetch devices failed with error: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val devices = callAmplify { onSuccess, onFailure -> fetchDevices(onSuccess, onFailure) } } // Test requires confirmation code, testing onError call @Test fun confirmResetPassword() { - val latch = CountDownLatch(1) - try { - Amplify.Auth.confirmResetPassword( - "username", - "NewPassword123", - "confirmation code", - { fail("New password confirmed, expected confirm reset password to fail") }, - { latch.countDown() } - ) - } catch (e: Exception) { - fail(e.toString()) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + invokeAmplify { onSuccess, onFailure -> + confirmResetPassword( + "username", + "NewPassword123", + "code", + onSuccess, + onFailure + ) + } + } } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test - fun updatePassword() { + fun updatePassword() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signUpUser(tempUsername, tempPassword) confirmTemporaryUserSignUp(tempUsername) signInUser(tempUsername, tempPassword) - val latch = CountDownLatch(1) - Amplify.Auth.updatePassword( - tempPassword, - tempPassword + "1", - { latch.countDown() }, - { fail("Password update failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> + updatePassword(tempPassword, tempPassword + "1", onSuccess, onFailure) + } } @Test - fun fetchUserAttributes() { + fun fetchUserAttributes() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.fetchUserAttributes( - { latch.countDown() }, - { fail("Failed to fetch user attributes: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> fetchUserAttributes(onSuccess, onFailure) } } @Test - fun updateUserAttribute() { + fun updateUserAttribute() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.updateUserAttribute( - AuthUserAttribute(AuthUserAttributeKey.name(), "apitest"), - { latch.countDown() }, - { fail("Failed to update user attribute: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> + updateUserAttribute(AuthUserAttribute(AuthUserAttributeKey.name(), "apitest"), onSuccess, onFailure) + } } @Test - fun updateUserAttributes() { + fun updateUserAttributes() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.updateUserAttributes( - attributes, // attributes is a list of AuthUserAttribute - { latch.countDown() }, - { fail("Failed to update user attributes: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val attributes = callAmplify { onSuccess, onFailure -> + updateUserAttributes(attributes, onSuccess, onFailure) + } } // Test requires confirmation code, testing onError call @Test fun confirmUserAttribute() { - val latch = CountDownLatch(1) - Amplify.Auth.confirmUserAttribute( - AuthUserAttributeKey.email(), - "344299", - { fail("Confirmed user attribute with incorrect code, expected confirm user attribute to fail.") }, - { latch.countDown() } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + assertFailsWith { + runBlockingWithTimeout(TIMEOUT_S.seconds) { + invokeAmplify { onSuccess, onFailure -> + confirmUserAttribute( + AuthUserAttributeKey.email(), + "344299", + onSuccess, + onFailure + ) + } + } + } } @Test - fun getCurrentUser() { + fun getCurrentUser() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.getCurrentUser( - { latch.countDown() }, - { fail("Get current user failed with an exception: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + val user = callAmplify { onSuccess, onFailure -> + getCurrentUser(onSuccess, onFailure) + } } @Test - fun signOut() { + fun signOut(): Unit = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) - Amplify.Auth.signOut { signOutResult -> - when (signOutResult) { - is AWSCognitoAuthSignOutResult.CompleteSignOut -> { - // Sign Out completed fully and without errors. - latch.countDown() - } - is AWSCognitoAuthSignOutResult.PartialSignOut -> { - // Sign Out completed with some errors. User is signed out of the device. - signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } - signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } - signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } - } - is AWSCognitoAuthSignOutResult.FailedSignOut -> { - // Sign Out failed with an exception, leaving the user signed in. - fail("Sign out failed: ${signOutResult.exception}") - } + val signOutResult = callAmplify { onSuccess, _ -> signOut(onSuccess) } + when (signOutResult) { + is AWSCognitoAuthSignOutResult.CompleteSignOut -> { + // Sign Out completed fully and without errors. + } + is AWSCognitoAuthSignOutResult.PartialSignOut -> { + // Sign Out completed with some errors. User is signed out of the device. + signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } + signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } + signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } + } + is AWSCognitoAuthSignOutResult.FailedSignOut -> { + // Sign Out failed with an exception, leaving the user signed in. + fail("Sign out failed: ${signOutResult.exception}") } + else -> fail("Unexpected sign out result occurred") } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test - fun globalSignOut() { + fun globalSignOut(): Unit = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signInUser(username, password) - val latch = CountDownLatch(1) val options = AuthSignOutOptions.builder() .globalSignOut(true) .build() - Amplify.Auth.signOut(options) { signOutResult -> - when (signOutResult) { - is AWSCognitoAuthSignOutResult.CompleteSignOut -> { - // Sign Out completed fully and without errors. - latch.countDown() - } - is AWSCognitoAuthSignOutResult.PartialSignOut -> { - // Sign Out completed with some errors. User is signed out of the device. - signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } - signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } - signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } - } - is AWSCognitoAuthSignOutResult.FailedSignOut -> { - // Sign Out failed with an exception, leaving the user signed in. - fail("Sign out failed: ${signOutResult.exception}") - } + val signOutResult = callAmplify { onSuccess, _ -> signOut(options, onSuccess) } + when (signOutResult) { + is AWSCognitoAuthSignOutResult.CompleteSignOut -> { + // Sign Out completed fully and without errors. + } + is AWSCognitoAuthSignOutResult.PartialSignOut -> { + // Sign Out completed with some errors. User is signed out of the device. + signOutResult.hostedUIError?.let { fail("HostedUIError while signing out: $it") } + signOutResult.globalSignOutError?.let { fail("GlobalSignOutError while signing out: $it") } + signOutResult.revokeTokenError?.let { fail("RevokeTokenError: $it") } } + is AWSCognitoAuthSignOutResult.FailedSignOut -> { + // Sign Out failed with an exception, leaving the user signed in. + fail("Sign out failed: ${signOutResult.exception}") + } + else -> fail("Unexpected sign out result occurred") } - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } @Test - fun deleteUser() { + fun deleteUser() = runBlockingWithTimeout(timeout = TIMEOUT_S.seconds) { signUpUser(tempUsername, tempPassword) confirmTemporaryUserSignUp(tempUsername) signInUser(tempUsername, tempPassword) - val latch = CountDownLatch(1) - Amplify.Auth.deleteUser( - { - signedUpNewUser = false - latch.countDown() - }, - { fail("Delete user failed: $it") } - ) - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + invokeAmplify { onSuccess, onFailure -> deleteUser(onSuccess, onFailure) } + signedUpNewUser = false } private fun signUpUser(user: String, pass: String) { diff --git a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthStressTests.kt b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthStressTests.kt index bcc06b7a1..c1c4f2bca 100644 --- a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthStressTests.kt +++ b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AuthStressTests.kt @@ -21,12 +21,21 @@ import androidx.test.core.app.ApplicationProvider import com.amplifyframework.AmplifyException import com.amplifyframework.auth.AuthUserAttribute import com.amplifyframework.auth.AuthUserAttributeKey +import com.amplifyframework.auth.cognito.exceptions.invalidstate.SignedInException import com.amplifyframework.auth.cognito.result.AWSCognitoAuthSignOutResult import com.amplifyframework.auth.cognito.testutils.Credentials +import com.amplifyframework.auth.cognito.testutils.callAmplify import com.amplifyframework.auth.options.AuthFetchSessionOptions import com.amplifyframework.core.Amplify +import com.amplifyframework.testutils.coroutines.runBlockingWithTimeout import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit +import kotlin.random.Random +import kotlin.test.assertFailsWith +import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Before @@ -79,142 +88,90 @@ class AuthStressTests { * Calls Auth.signIn 50 times */ @Test - fun testMultipleSignIn() { - val successLatch = CountDownLatch(1) - val errorLatch = CountDownLatch(49) - - repeat(50) { - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) successLatch.countDown() else fail() }, - { errorLatch.countDown() } - ) + fun testMultipleSignIn() = runStressTest( + times = 50, + timeout = 20.seconds, + setup = { signInUser(username, password) } + ) { + assertFailsWith { + val result = callAmplify { onSuccess, onFailure -> signIn(username, password, onSuccess, onFailure) } } - - assertTrue(successLatch.await(TIMEOUT_S, TimeUnit.SECONDS)) - assertTrue(errorLatch.await(TIMEOUT_S, TimeUnit.SECONDS)) } /** * Calls Auth.signOut 50 times */ @Test - fun testMultipleSignOut() { - val latch = CountDownLatch(50) - - repeat(50) { - Amplify.Auth.signOut { - if ((it as AWSCognitoAuthSignOutResult).signedOutLocally) latch.countDown() else fail() - } - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + fun testMultipleSignOut() = runStressTest( + times = 50 + ) { + signOutUser() } /** * Calls Auth.fetchAuthSession 100 times when signed out */ @Test - fun testMultipleFAS_WhenSignedOut() { - val latch = CountDownLatch(100) - - repeat(100) { - Amplify.Auth.fetchAuthSession({ if (!it.isSignedIn) latch.countDown() else fail() }, { fail() }) - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + fun testMultipleFAS_WhenSignedOut() = runStressTest( + times = 100, + timeout = 20.seconds + ) { + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } /** * Calls Auth.signIn, then calls Auth.fetchAuthSession 100 times */ @Test - fun testMultipleFAS_AfterSignIn() { - val latch = CountDownLatch(101) - - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - - repeat(100) { - Amplify.Auth.fetchAuthSession({ if (it.isSignedIn) latch.countDown() else fail() }, { fail() }) - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + fun testMultipleFAS_AfterSignIn() = runStressTest( + times = 100, + timeout = 1.minutes, + setup = { signInUser(username, password) } + ) { + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } /** * Calls Auth.signIn, then calls Auth.signOut */ @Test - fun testSignOut_AfterSignIn() { - val latch = CountDownLatch(2) - - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - - Amplify.Auth.signOut { if ((it as AWSCognitoAuthSignOutResult).signedOutLocally) latch.countDown() else fail() } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) - } + fun testSignOut_AfterSignIn() = runStressTest( + times = 0, + timeout = 1.minutes, + setup = { signInUser(username, password) }, + finally = { signOutUser() } + ) { } /** * Calls Auth.signIn, calls Auth.fetchAuthSession 100 times, then calls Auth.signOut */ @Test - fun testSignIn_multipleFAS_SignOut() { - val latch = CountDownLatch(102) - - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - - repeat(100) { - Amplify.Auth.fetchAuthSession({ latch.countDown() }, { fail() }) - } - - Amplify.Auth.signOut { if ((it as AWSCognitoAuthSignOutResult).signedOutLocally) latch.countDown() else fail() } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) + fun testSignIn_multipleFAS_SignOut() = runStressTest( + times = 100, + timeout = 1.minutes, + setup = { signInUser(username, password) }, + finally = { signOutUser() } + ) { + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } /** * Calls Auth.signIn, then calls Auth.fetchAuthSession 100 times. Randomly calls Auth.signOut within those 100 calls. */ @Test - fun testSignIn_multipleFAS_withSignOut() { - val latch = CountDownLatch(102) - - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - + fun testSignIn_multipleFAS_withSignOut() = runStressTest( + times = 100, + timeout = 3.minutes, + setup = { signInUser(username, password) } + ) { val random = (Math.random() * 100).toInt() repeat(100) { idx -> if (idx == random) { - Amplify.Auth.signOut { - if ((it as AWSCognitoAuthSignOutResult).signedOutLocally) latch.countDown() else fail() - } + signOutUser() } - Amplify.Auth.fetchAuthSession({ latch.countDown() }, { fail() }) + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.SECONDS)) } /** @@ -222,129 +179,115 @@ class AuthStressTests { * forceRefresh() within those 100 calls. */ @Test - fun testSignIn_multipleFAS_withRefresh() { - val latch = CountDownLatch(101) - - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() }, - { fail() } - ) - - val random = List(2) { (Math.random() * 100).toInt() } - repeat(100) { idx -> - val options = AuthFetchSessionOptions.builder().forceRefresh(random.contains(idx)).build() - - Amplify.Auth.fetchAuthSession( - options, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.MINUTES)) + fun testSignIn_multipleFAS_withRefresh() = runStressTest( + times = 100, + timeout = 2.minutes, + setup = { signInUser(username, password) } + ) { + val options = AuthFetchSessionOptions.builder().forceRefresh(Random.nextInt(2) == 0).build() + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(options, onSuccess, onFailure) } + assertTrue(session.isSignedIn) } /** * Randomly calls Auth.fetchAuthSession, Auth.signIn, Auth.fetchAuthSession with forceRefresh(), and Auth.signOut 20 times. */ @Test - fun testRandomMultipleAPIs() { - val latch = CountDownLatch(20) - + fun testRandomMultipleAPIs() = runBlockingWithTimeout(timeout = 1.minutes) { val random = List(20) { (1..4).random() } random.forEach { idx -> when (idx) { - 1 -> Amplify.Auth.fetchAuthSession({ latch.countDown() }, { fail() }) + 1 -> { + val session = callAmplify { onSuccess, onFailure -> fetchAuthSession(onSuccess, onFailure) } + } 2 -> { - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() }, - { latch.countDown() } - ) + try { + val result = + callAmplify { onSuccess, onFailure -> signIn(username, password, onSuccess, onFailure) } + } catch (exception: SignedInException) { + // catch and ignore this exception as it's expected if sign in is called twice without a sign out + } } 3 -> { val options = AuthFetchSessionOptions.builder().forceRefresh(true).build() - Amplify.Auth.fetchAuthSession(options, { latch.countDown() }, { fail() }) + val session = callAmplify { onSuccess, onFailure -> + fetchAuthSession(options, onSuccess, onFailure) + } } - 4 -> Amplify.Auth.signOut { - if ((it as AWSCognitoAuthSignOutResult).signedOutLocally) latch.countDown() else fail() + 4 -> { + signOutUser() } } } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.MINUTES)) } /** * Calls Auth.getCurrentUser 100 times */ @Test - fun testSignIn_GetCurrentUser() { - val latch = CountDownLatch(101) - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - - repeat(100) { - Amplify.Auth.getCurrentUser( - { if (it.username == username) latch.countDown() else fail() }, - { fail() } - ) - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.MINUTES)) + fun testSignIn_GetCurrentUser() = runStressTest( + times = 100, + timeout = 1.minutes, + setup = { signInUser(username, password) } + ) { + val user = callAmplify { onSuccess, onFailure -> getCurrentUser(onSuccess, onFailure) } } /** * Calls Auth.fetchUserAttributes 100 times */ @Test - fun testSignIn_FetchAttributes() { - val latch = CountDownLatch(101) - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) - - repeat(100) { - Amplify.Auth.fetchUserAttributes( - { latch.countDown() }, - { fail() } - ) - } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.MINUTES)) + fun testSignIn_FetchAttributes() = runStressTest( + times = 100, + timeout = 1.minutes, + setup = { signInUser(username, password) } + ) { + val attributes = callAmplify { onSuccess, onFailure -> fetchUserAttributes(onSuccess, onFailure) } } /** * Calls Auth.updateUserAttributes 100 times */ @Test - fun testSignIn_UpdateAttributes() { - val latch = CountDownLatch(101) - Amplify.Auth.signIn( - username, - password, - { if (it.isSignedIn) latch.countDown() else fail() }, - { fail() } - ) + fun testSignIn_UpdateAttributes() = runStressTest( + times = 100, + timeout = 2.minutes, + setup = { signInUser(username, password) } + ) { + val updated = callAmplify { onSuccess, onFailure -> updateUserAttributes(attributes, onSuccess, onFailure) } + } - repeat(100) { - Amplify.Auth.updateUserAttributes( - attributes, - { latch.countDown() }, - { fail() } - ) + private fun runStressTest( + times: Int = 50, + timeout: Duration = 10.seconds, + setup: suspend () -> Unit = {}, + finally: suspend () -> Unit = {}, + body: suspend (Int) -> Unit + ) { + try { + runBlockingWithTimeout(timeout) { + setup() + repeat(times) { + // This delay is to try and slow the amount of requests per second we make without inflating the + // test execution time by too much. If we still find that these tests are flaky, remove this + // and investigate further + delay(20) + body(it) + } + finally() + } + } catch (e: Exception) { + fail("Test failed with exception: $e") } - - assertTrue(latch.await(TIMEOUT_S, TimeUnit.MINUTES)) } } + +private suspend fun signInUser(username: String, password: String) { + val result = callAmplify { onSuccess, onFailure -> signIn(username, password, onSuccess, onFailure) } + assertTrue(result.isSignedIn) +} + +private suspend fun signOutUser() { + val signedOut = callAmplify { onSuccess, _ -> signOut(onSuccess) } + assertTrue((signedOut as AWSCognitoAuthSignOutResult).signedOutLocally) +} diff --git a/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/testutils/AuthTestUtil.kt b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/testutils/AuthTestUtil.kt new file mode 100644 index 000000000..44da674d0 --- /dev/null +++ b/aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/testutils/AuthTestUtil.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amplifyframework.auth.cognito.testutils + +import com.amplifyframework.auth.AuthCategoryBehavior +import com.amplifyframework.core.Amplify +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend fun invokeAmplify(block: AuthCategoryBehavior.(onSuccess: () -> Unit, onFailure: (Exception) -> Unit) -> Unit) = + suspendCoroutine { continuation -> + Amplify.Auth.block( + { continuation.resume(Unit) }, + { continuation.resumeWithException(it) } + ) + } + +suspend fun callAmplify( + block: AuthCategoryBehavior.(onSuccess: (T) -> Unit, onFailure: (Exception) -> Unit) -> Unit +) = suspendCoroutine { continuation -> + Amplify.Auth.block( + { continuation.resume(it) }, + { continuation.resumeWithException(it) } + ) +}