Skip to content

Commit cade307

Browse files
authored
chore(auth): Improve auth integration test stability and traceability (#3143)
1 parent ef37f18 commit cade307

18 files changed

+731
-890
lines changed

aws-auth-cognito/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ dependencies {
7373
androidTestImplementation(libs.test.kotlin.coroutines)
7474
androidTestImplementation(libs.test.kotlin.kotlinTest)
7575
androidTestImplementation(libs.test.totp)
76+
androidTestImplementation(libs.test.kotest.assertions)
7677

7778
androidTestImplementation(project(":aws-api"))
7879
androidTestImplementation(project(":aws-api-appsync"))

aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AWSCognitoAuthPluginEmailMFATests.kt

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,24 @@ package com.amplifyframework.auth.cognito
1818
import android.content.Context
1919
import androidx.test.core.app.ApplicationProvider
2020
import com.amplifyframework.api.aws.AWSApiPlugin
21-
import com.amplifyframework.api.graphql.GraphQLOperation
22-
import com.amplifyframework.api.graphql.SimpleGraphQLRequest
2321
import com.amplifyframework.auth.AuthUserAttribute
2422
import com.amplifyframework.auth.AuthUserAttributeKey
2523
import com.amplifyframework.auth.MFAType
2624
import com.amplifyframework.auth.cognito.exceptions.service.CodeMismatchException
2725
import com.amplifyframework.auth.cognito.test.R
26+
import com.amplifyframework.auth.cognito.testutils.blockForCode
27+
import com.amplifyframework.auth.cognito.testutils.blockUntilEstablished
28+
import com.amplifyframework.auth.cognito.testutils.createMfaSubscription
2829
import com.amplifyframework.auth.options.AuthSignUpOptions
2930
import com.amplifyframework.auth.result.AuthSignUpResult
3031
import com.amplifyframework.auth.result.step.AuthSignInStep
3132
import com.amplifyframework.core.configuration.AmplifyOutputs
3233
import com.amplifyframework.core.configuration.AmplifyOutputsData
3334
import com.amplifyframework.datastore.generated.model.MfaInfo
34-
import com.amplifyframework.testutils.Assets
35+
import com.amplifyframework.testutils.api.SubscriptionHolder
3536
import com.amplifyframework.testutils.sync.SynchronousAuth
3637
import java.util.Random
3738
import java.util.UUID
38-
import java.util.concurrent.CountDownLatch
39-
import java.util.concurrent.TimeUnit
4039
import kotlin.test.assertEquals
4140
import kotlin.test.assertFailsWith
4241
import org.junit.After
@@ -46,15 +45,13 @@ import org.junit.Test
4645
class AWSCognitoAuthPluginEmailMFATests {
4746

4847
private val password = "${UUID.randomUUID()}BleepBloop1234!"
49-
private val userName = "test${Random().nextInt()}"
50-
private val email = "$userName@amplify-swift-gamma.awsapps.com"
48+
private val username = "test${Random().nextInt()}"
49+
private val email = "$username@amplify-swift-gamma.awsapps.com"
5150

5251
private var authPlugin = AWSCognitoAuthPlugin()
5352
private var apiPlugin = AWSApiPlugin()
5453
private lateinit var synchronousAuth: SynchronousAuth
55-
private var subscription: GraphQLOperation<MfaInfo>? = null
56-
private var mfaCode = ""
57-
private var latch: CountDownLatch? = null
54+
private lateinit var subscription: SubscriptionHolder<MfaInfo>
5855

5956
@Before
6057
fun initializePlugin() {
@@ -66,29 +63,12 @@ class AWSCognitoAuthPluginEmailMFATests {
6663
apiPlugin.configure(config, context)
6764
synchronousAuth = SynchronousAuth.delegatingTo(authPlugin)
6865

69-
subscription = apiPlugin.subscribe(
70-
SimpleGraphQLRequest(
71-
Assets.readAsString("create-mfa-subscription.graphql"),
72-
MfaInfo::class.java,
73-
null
74-
),
75-
{ println("====== Subscription Established ======") },
76-
{
77-
println("====== Received some MFA Info ======")
78-
if (it.data.username == userName) {
79-
mfaCode = it.data.code
80-
latch?.countDown()
81-
}
82-
},
83-
{ println("====== Subscription Failed $it ======") },
84-
{ }
85-
)
66+
subscription = apiPlugin.createMfaSubscription()
8667
}
8768

8869
@After
8970
fun tearDown() {
90-
subscription?.cancel()
91-
mfaCode = ""
71+
subscription.cancel()
9272
synchronousAuth.deleteUser()
9373
}
9474

@@ -98,7 +78,7 @@ class AWSCognitoAuthPluginEmailMFATests {
9878
signUpNewUser()
9979

10080
// Step 2: Attempt to sign in with the newly created user
101-
var signInResult = synchronousAuth.signIn(userName, password)
81+
var signInResult = synchronousAuth.signIn(username, password)
10282

10383
// Validation 1: Validate that the next step is MFA Setup Selection
10484
assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION, signInResult.nextStep.signInStep)
@@ -112,15 +92,17 @@ class AWSCognitoAuthPluginEmailMFATests {
11292
// Validation 2: Validate that the next step is to input the user's email address
11393
assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_EMAIL_MFA_SETUP, signInResult.nextStep.signInStep)
11494

95+
// Ensure subscription is ready to receive MFA code
96+
subscription.blockUntilEstablished()
97+
11598
// Step 4: Input the email address to send the code to then wait for the MFA code
116-
latch = CountDownLatch(1)
11799
signInResult = synchronousAuth.confirmSignIn(email)
118100

119101
// Validation 3: Validate that the next step is to confirm the emailed MFA code
120102
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)
121103

122104
// Wait until the MFA code has been received
123-
latch?.await(20, TimeUnit.SECONDS)
105+
val mfaCode = subscription.blockForCode(username)
124106

125107
// Step 5: Input the emailed MFA code for confirmation
126108
signInResult = synchronousAuth.confirmSignIn(mfaCode)
@@ -134,15 +116,17 @@ class AWSCognitoAuthPluginEmailMFATests {
134116
// Step 1: Sign up a new user with an existing email address
135117
signUpNewUser(email)
136118

119+
// Ensure subscription is ready to receive MFA code
120+
subscription.blockUntilEstablished()
121+
137122
// Step 2: Attempt to sign in with the newly created user
138-
latch = CountDownLatch(1)
139-
var signInResult = synchronousAuth.signIn(userName, password)
123+
var signInResult = synchronousAuth.signIn(username, password)
140124

141125
// Validation 1: Validate that the next step is to confirm the emailed MFA code
142126
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)
143127

144128
// Wait until the MFA code has been received
145-
latch?.await(20, TimeUnit.SECONDS)
129+
val mfaCode = subscription.blockForCode(username)
146130

147131
// Step 4: Input the emailed MFA code for confirmation
148132
signInResult = synchronousAuth.confirmSignIn(mfaCode)
@@ -156,20 +140,22 @@ class AWSCognitoAuthPluginEmailMFATests {
156140
// Step 1: Sign up a new user with an existing email address
157141
signUpNewUser(email)
158142

143+
// Ensure subscription is ready to receive MFA code
144+
subscription.blockUntilEstablished()
145+
159146
// Step 2: Attempt to sign in with the newly created user
160-
latch = CountDownLatch(1)
161-
var signInResult = synchronousAuth.signIn(userName, password)
147+
var signInResult = synchronousAuth.signIn(username, password)
162148

163149
// Validation 1: Validate that the next step is to confirm the emailed MFA code
164150
assertEquals(AuthSignInStep.CONFIRM_SIGN_IN_WITH_OTP, signInResult.nextStep.signInStep)
165151

166152
// Wait until the MFA code has been received
167-
latch?.await(20, TimeUnit.SECONDS)
153+
val mfaCode = subscription.blockForCode(username)
168154

169155
// Step 4: Input the an incorrect MFA code
170156
// Validation 2: Validate that an incorrect MFA code throws a CodeMismatchException
171157
assertFailsWith<CodeMismatchException> {
172-
signInResult = synchronousAuth.confirmSignIn(mfaCode.reversed())
158+
synchronousAuth.confirmSignIn(mfaCode.reversed())
173159
}
174160

175161
// Step 5: Input the correct MFA code for validation
@@ -189,6 +175,6 @@ class AWSCognitoAuthPluginEmailMFATests {
189175
.userAttributes(
190176
attributes
191177
).build()
192-
return synchronousAuth.signUp(userName, password, options)
178+
return synchronousAuth.signUp(username, password, options)
193179
}
194180
}

aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AWSCognitoAuthPluginInstrumentationTests.kt

Lines changed: 15 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ import android.util.Log
2020
import androidx.test.core.app.ApplicationProvider
2121
import androidx.test.ext.junit.runners.AndroidJUnit4
2222
import com.amplifyframework.auth.AuthChannelEventName
23-
import com.amplifyframework.auth.AuthSession
2423
import com.amplifyframework.auth.cognito.testutils.Credentials
2524
import com.amplifyframework.core.Amplify
2625
import com.amplifyframework.core.InitializationStatus
2726
import com.amplifyframework.hub.HubChannel
2827
import com.amplifyframework.testutils.HubAccumulator
28+
import com.amplifyframework.testutils.assertAwait
2929
import com.amplifyframework.testutils.await
30+
import com.amplifyframework.testutils.sync.SynchronousAuth
3031
import java.util.concurrent.CountDownLatch
3132
import java.util.concurrent.TimeUnit
3233
import kotlin.time.Duration.Companion.seconds
@@ -43,7 +44,8 @@ import org.junit.runner.RunWith
4344
class AWSCognitoAuthPluginInstrumentationTests {
4445

4546
companion object {
46-
var auth = AWSCognitoAuthPlugin()
47+
val auth = AWSCognitoAuthPlugin()
48+
val syncAuth = SynchronousAuth.delegatingTo(auth)
4749

4850
@BeforeClass
4951
@JvmStatic
@@ -60,7 +62,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
6062
latch.countDown()
6163
}
6264
}
63-
latch.await(20, TimeUnit.SECONDS)
65+
latch.assertAwait(20, TimeUnit.SECONDS)
6466
} catch (ex: Exception) {
6567
Log.i("AWSCognitoAuthPluginInstrumentationTests", "Error initializing", ex)
6668
}
@@ -70,7 +72,6 @@ class AWSCognitoAuthPluginInstrumentationTests {
7072
@Before
7173
fun setup() {
7274
signOut()
73-
Thread.sleep(1000) // ensure signout has time to complete
7475
}
7576

7677
@Test
@@ -118,19 +119,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
118119
fun fetchAuthSession_can_pull_session_when_signed_in() {
119120
signInWithCognito()
120121

121-
lateinit var session: AuthSession
122-
val latch = CountDownLatch(1)
123-
124-
auth.fetchAuthSession(
125-
{
126-
session = it
127-
latch.countDown()
128-
},
129-
{
130-
latch.countDown()
131-
}
132-
)
133-
latch.await(10.seconds)
122+
val session = syncAuth.fetchAuthSession()
134123

135124
assertTrue(session.isSignedIn)
136125
with(session as AWSCognitoAuthSession) {
@@ -145,20 +134,7 @@ class AWSCognitoAuthPluginInstrumentationTests {
145134
fun fetchAuthSession_does_not_throw_error_even_when_signed_out() {
146135
signOut()
147136

148-
lateinit var session: AuthSession
149-
150-
val latch = CountDownLatch(1)
151-
152-
auth.fetchAuthSession(
153-
{
154-
session = it
155-
latch.countDown()
156-
},
157-
{
158-
latch.countDown()
159-
}
160-
)
161-
latch.await(10.seconds)
137+
val session = syncAuth.fetchAuthSession()
162138

163139
assertFalse(session.isSignedIn)
164140
with(session as AWSCognitoAuthSession) {
@@ -173,80 +149,24 @@ class AWSCognitoAuthPluginInstrumentationTests {
173149
fun rememberDevice_succeeds_after_signIn_and_signOut() {
174150
signInWithCognito()
175151

176-
val rememberLatch = CountDownLatch(1)
177-
178-
auth.rememberDevice(
179-
{
180-
rememberLatch.countDown()
181-
},
182-
{
183-
rememberLatch.countDown()
184-
assertTrue(false)
185-
}
186-
)
187-
188-
rememberLatch.await(10.seconds)
189-
190-
val forgetLatch = CountDownLatch(1)
191-
192-
auth.forgetDevice(
193-
{
194-
forgetLatch.countDown()
195-
},
196-
{
197-
forgetLatch.countDown()
198-
assertTrue(false)
199-
}
200-
)
201-
202-
forgetLatch.await(10.seconds)
152+
syncAuth.rememberDevice()
153+
syncAuth.forgetDevice()
203154

204155
signOut()
205156
signInWithCognito()
206157

207-
val rememberLatch2 = CountDownLatch(1)
208-
209-
auth.rememberDevice(
210-
{
211-
rememberLatch2.countDown()
212-
},
213-
{
214-
assertTrue(false)
215-
rememberLatch2.countDown()
216-
}
217-
)
218-
219-
rememberLatch2.await(10.seconds)
220-
221-
val forgetLatch2 = CountDownLatch(1)
222-
223-
auth.forgetDevice(
224-
{
225-
forgetLatch2.countDown()
226-
},
227-
{
228-
assertTrue(false)
229-
forgetLatch2.countDown()
230-
}
231-
)
232-
233-
forgetLatch2.await(10.seconds)
158+
syncAuth.rememberDevice()
159+
syncAuth.forgetDevice()
234160
}
235161

236-
private fun signInWithCognito(synchronous: Boolean = true) {
162+
private fun signInWithCognito() {
237163
val context = ApplicationProvider.getApplicationContext<Context>()
238164
val (username, password) = Credentials.load(context)
239-
240-
val latch = CountDownLatch(1)
241-
auth.signIn(username, password, { latch.countDown() }, { latch.countDown() })
242-
243-
if (synchronous) latch.await()
165+
syncAuth.signIn(username, password)
244166
}
245167

246-
private fun signOut(synchronous: Boolean = true) {
247-
val latch = CountDownLatch(1)
248-
auth.signOut { latch.countDown() }
249-
if (synchronous) latch.await()
168+
private fun signOut() {
169+
syncAuth.signOut()
250170
}
251171

252172
// Creates and starts a HubAccumulator, runs the supplied block, and then stops the accumulator

aws-auth-cognito/src/androidTest/java/com/amplifyframework/auth/cognito/AWSCognitoAuthPluginTOTPTests.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.amplifyframework.auth.AuthUserAttributeKey
2222
import com.amplifyframework.auth.MFAType
2323
import com.amplifyframework.auth.cognito.helpers.value
2424
import com.amplifyframework.auth.cognito.test.R
25+
import com.amplifyframework.auth.cognito.testutils.blockForCompletion
2526
import com.amplifyframework.auth.options.AuthSignUpOptions
2627
import com.amplifyframework.auth.result.step.AuthSignInStep
2728
import com.amplifyframework.core.AmplifyConfiguration
@@ -32,8 +33,7 @@ import dev.robinohs.totpkt.otp.totp.TotpGenerator
3233
import dev.robinohs.totpkt.otp.totp.timesupport.generateCode
3334
import java.util.Random
3435
import java.util.UUID
35-
import java.util.concurrent.CountDownLatch
36-
import java.util.concurrent.TimeUnit
36+
import kotlin.time.Duration.Companion.seconds
3737
import org.junit.After
3838
import org.junit.Assert
3939
import org.junit.Before
@@ -143,7 +143,7 @@ class AWSCognitoAuthPluginTOTPTests {
143143
)
144144
synchronousAuth.confirmSignIn(otp)
145145
synchronousAuth.updateUserAttribute(AuthUserAttribute(AuthUserAttributeKey.phoneNumber(), "+19876543210"))
146-
updateMFAPreference(MFAPreference.ENABLED, MFAPreference.ENABLED, MFAPreference.ENABLED)
146+
updateMFAPreference(MFAPreference.ENABLED, MFAPreference.ENABLED, MFAPreference.DISABLED)
147147
synchronousAuth.signOut()
148148
val signInResult = synchronousAuth.signIn(userName, password)
149149
Assert.assertEquals(AuthSignInStep.CONTINUE_SIGN_IN_WITH_MFA_SELECTION, signInResult.nextStep.signInStep)
@@ -169,8 +169,8 @@ class AWSCognitoAuthPluginTOTPTests {
169169
}
170170

171171
private fun updateMFAPreference(sms: MFAPreference, totp: MFAPreference, email: MFAPreference) {
172-
val latch = CountDownLatch(1)
173-
authPlugin.updateMFAPreference(sms, totp, email, { latch.countDown() }, { latch.countDown() })
174-
latch.await(5, TimeUnit.SECONDS)
172+
blockForCompletion(5.seconds) { onSuccess, onError ->
173+
authPlugin.updateMFAPreference(sms, totp, email, onSuccess, onError)
174+
}
175175
}
176176
}

0 commit comments

Comments
 (0)