Skip to content

Commit 40a2075

Browse files
authored
chore(all): Re-enabled previously ignored tests (#3081)
1 parent d031a85 commit 40a2075

File tree

3 files changed

+331
-0
lines changed

3 files changed

+331
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.apollo.appsync.util
17+
18+
import io.kotest.matchers.shouldBe
19+
import io.mockk.every
20+
import io.mockk.mockkObject
21+
import java.util.Date
22+
import java.util.TimeZone
23+
import org.junit.Test
24+
25+
class Iso8601TimestampTest {
26+
27+
@Test
28+
fun `returns expected timestamp format`() {
29+
mockkObject(Iso8601Timestamp) {
30+
// Wed Jun 25 2025 14:00:00.000 UTC
31+
val utcMillis = 1750860000000L
32+
33+
// Adjust for system timezone to get equivalent UTC time
34+
// This allows us to compare the result string, regardless of the timezone of the system clock
35+
val systemOffset = TimeZone.getDefault().getOffset(utcMillis)
36+
val adjustedMillis = utcMillis - systemOffset
37+
38+
every { Iso8601Timestamp.currentDate() } returns Date(adjustedMillis)
39+
40+
val result = Iso8601Timestamp.now()
41+
result shouldBe "20250625T140000Z"
42+
}
43+
}
44+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package com.amazonaws.sdk.appsync.core.util
16+
17+
import io.kotest.matchers.shouldBe
18+
import io.mockk.every
19+
import io.mockk.mockkObject
20+
import java.util.Date
21+
import java.util.TimeZone
22+
import org.junit.Test
23+
24+
class Iso8601TimestampTest {
25+
26+
@Test
27+
fun `returns expected timestamp format`() {
28+
mockkObject(Iso8601Timestamp) {
29+
// Wed Jun 25 2025 14:00:00.000 UTC
30+
val utcMillis = 1750860000000L
31+
32+
// Adjust for system timezone to get equivalent UTC time
33+
// This allows us to compare the result string, regardless of the timezone of the system clock
34+
val systemOffset = TimeZone.getDefault().getOffset(utcMillis)
35+
val adjustedMillis = utcMillis - systemOffset
36+
37+
every { Iso8601Timestamp.currentDate() } returns Date(adjustedMillis)
38+
39+
val result = Iso8601Timestamp.now()
40+
result shouldBe "20250625T140000Z"
41+
}
42+
}
43+
}
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.auth.cognito
17+
18+
import aws.sdk.kotlin.services.cognitoidentity.CognitoIdentityClient
19+
import aws.sdk.kotlin.services.cognitoidentityprovider.CognitoIdentityProviderClient
20+
import com.amplifyframework.auth.cognito.featuretest.AuthAPI
21+
import com.amplifyframework.auth.cognito.featuretest.ExpectationShapes
22+
import com.amplifyframework.auth.cognito.featuretest.ExpectationShapes.Cognito
23+
import com.amplifyframework.auth.cognito.featuretest.ExpectationShapes.Cognito.CognitoIdentity
24+
import com.amplifyframework.auth.cognito.featuretest.ExpectationShapes.Cognito.CognitoIdentityProvider
25+
import com.amplifyframework.auth.cognito.featuretest.FeatureTestCase
26+
import com.amplifyframework.auth.cognito.featuretest.generators.toJsonElement
27+
import com.amplifyframework.auth.cognito.featuretest.serializers.deserializeToAuthState
28+
import com.amplifyframework.auth.cognito.helpers.AuthHelper
29+
import com.amplifyframework.auth.cognito.usecases.AuthUseCaseFactory
30+
import com.amplifyframework.logging.Logger
31+
import com.amplifyframework.statemachine.codegen.data.AmplifyCredential
32+
import com.amplifyframework.statemachine.codegen.data.CredentialType
33+
import com.amplifyframework.statemachine.codegen.data.DeviceMetadata
34+
import com.amplifyframework.statemachine.codegen.states.AuthState
35+
import featureTest.utilities.CognitoMockFactory
36+
import featureTest.utilities.CognitoRequestFactory
37+
import featureTest.utilities.TimeZoneRule
38+
import featureTest.utilities.apiExecutor
39+
import io.kotest.assertions.json.shouldEqualJson
40+
import io.mockk.clearAllMocks
41+
import io.mockk.coEvery
42+
import io.mockk.coVerify
43+
import io.mockk.every
44+
import io.mockk.mockk
45+
import io.mockk.mockkObject
46+
import io.mockk.mockkStatic
47+
import io.mockk.slot
48+
import java.io.File
49+
import java.util.TimeZone
50+
import java.util.concurrent.CountDownLatch
51+
import java.util.concurrent.TimeUnit
52+
import kotlin.reflect.full.callSuspend
53+
import kotlin.reflect.full.declaredFunctions
54+
import kotlin.test.assertEquals
55+
import kotlinx.coroutines.Dispatchers
56+
import kotlinx.coroutines.newSingleThreadContext
57+
import kotlinx.coroutines.test.resetMain
58+
import kotlinx.coroutines.test.setMain
59+
import kotlinx.serialization.json.Json
60+
import org.json.JSONObject
61+
import org.junit.After
62+
import org.junit.Before
63+
import org.junit.Rule
64+
import org.junit.Test
65+
import org.junit.runner.RunWith
66+
import org.junit.runners.Parameterized
67+
68+
@RunWith(Parameterized::class)
69+
class AWSCognitoAuthPluginFeatureTest(private val testCase: FeatureTestCase) {
70+
71+
@Rule @JvmField
72+
val timeZoneRule = TimeZoneRule(TimeZone.getTimeZone("US/Pacific"))
73+
74+
lateinit var feature: FeatureTestCase
75+
private var apiExecutionResult: Any? = null
76+
77+
private val sut = AWSCognitoAuthPlugin()
78+
private lateinit var authStateMachine: AuthStateMachine
79+
80+
private val mockCognitoIPClient = mockk<CognitoIdentityProviderClient>(relaxed = true)
81+
private val mockCognitoIdClient = mockk<CognitoIdentityClient>()
82+
private val cognitoMockFactory = CognitoMockFactory(mockCognitoIPClient, mockCognitoIdClient)
83+
84+
// Used to execute a test in situations where the platform Main dispatcher is not available
85+
// see [https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/]
86+
private val mainThreadSurrogate = newSingleThreadContext("Main thread")
87+
88+
@After
89+
fun tearDown() {
90+
Dispatchers.resetMain()
91+
clearAllMocks()
92+
}
93+
94+
companion object {
95+
private const val TEST_SUITE_BASE_PATH = "/feature-test/testsuites"
96+
private const val STATES_FILE_BASE_PATH = "/feature-test/states"
97+
private const val CONFIGURATION_FILES_BASE_PATH = "/feature-test/configuration"
98+
99+
private val apisToSkip: List<AuthAPI> = listOf()
100+
101+
@JvmStatic
102+
@Parameterized.Parameters(name = "{0}")
103+
fun data(): Collection<FeatureTestCase> {
104+
val resourceDir = File(this::class.java.getResource(TEST_SUITE_BASE_PATH)?.file!!)
105+
assert(resourceDir.isDirectory)
106+
107+
return resourceDir.walkTopDown()
108+
.filterNot { it.isDirectory }.map {
109+
it.toRelativeString(resourceDir)
110+
}.map {
111+
readTestFeature(it)
112+
}.toList().filterNot {
113+
it.api.name in apisToSkip
114+
}
115+
}
116+
117+
private fun readTestFeature(fileName: String): FeatureTestCase {
118+
val testCaseFile = this::class.java.getResource("$TEST_SUITE_BASE_PATH/$fileName")
119+
return Json.decodeFromString(File(testCaseFile!!.toURI()).readText())
120+
}
121+
}
122+
123+
@Before
124+
fun setUp() {
125+
// set timezone to be same as generated json from JsonGenerator
126+
Dispatchers.setMain(mainThreadSurrogate)
127+
feature = testCase
128+
readConfiguration(feature.preConditions.`amplify-configuration`).let {
129+
sut.realPlugin = it.first
130+
sut.useCaseFactory = it.second
131+
}
132+
}
133+
134+
@Test
135+
fun api_feature_test() {
136+
// GIVEN
137+
mockAndroidAPIs()
138+
feature.preConditions.mockedResponses.forEach(cognitoMockFactory::mock)
139+
140+
// WHEN
141+
apiExecutionResult = apiExecutor(sut, feature.api)
142+
143+
// THEN
144+
feature.validations.forEach(this::verify)
145+
}
146+
147+
/**
148+
* Mock Android APIs as per need basis.
149+
* This is cheaper than using Robolectric.
150+
*/
151+
private fun mockAndroidAPIs() {
152+
mockkObject(AuthHelper)
153+
coEvery { AuthHelper.getSecretHash(any(), any(), any()) } returns "a hash"
154+
}
155+
156+
private fun readConfiguration(configuration: String): Pair<RealAWSCognitoAuthPlugin, AuthUseCaseFactory> {
157+
val configFileUrl = this::class.java.getResource("$CONFIGURATION_FILES_BASE_PATH/$configuration")
158+
val configJSONObject =
159+
JSONObject(File(configFileUrl!!.file).readText())
160+
.getJSONObject("auth")
161+
.getJSONObject("plugins")
162+
.getJSONObject("awsCognitoAuthPlugin")
163+
val authConfiguration = AuthConfiguration.fromJson(configJSONObject)
164+
165+
val authService = mockk<AWSCognitoAuthService> {
166+
every { cognitoIdentityProviderClient } returns mockCognitoIPClient
167+
every { cognitoIdentityClient } returns mockCognitoIdClient
168+
}
169+
170+
/**
171+
* Always consider amplify credential is valid. This will need to be mocked otherwise
172+
* when we test the expiration based test cases.
173+
*/
174+
mockkStatic("com.amplifyframework.auth.cognito.AWSCognitoAuthSessionKt")
175+
every { any<AmplifyCredential>().isValid() } returns true
176+
177+
val credentialStoreClient = mockk<CredentialStoreClient>(relaxed = true)
178+
coEvery { credentialStoreClient.loadCredentials(capture(slot<CredentialType.Device>())) } coAnswers {
179+
AmplifyCredential.DeviceData(DeviceMetadata.Empty)
180+
}
181+
182+
val logger = mockk<Logger>(relaxed = true)
183+
184+
val authEnvironment = AuthEnvironment(
185+
mockk(),
186+
authConfiguration,
187+
authService,
188+
credentialStoreClient,
189+
null,
190+
null,
191+
logger
192+
)
193+
194+
authStateMachine = AuthStateMachine(authEnvironment, getState(feature.preConditions.state))
195+
196+
val realPlugin = RealAWSCognitoAuthPlugin(authConfiguration, authEnvironment, authStateMachine, logger)
197+
return Pair(
198+
RealAWSCognitoAuthPlugin(authConfiguration, authEnvironment, authStateMachine, logger),
199+
AuthUseCaseFactory(realPlugin, authEnvironment, authStateMachine)
200+
)
201+
}
202+
203+
private fun verify(validation: ExpectationShapes) {
204+
when (validation) {
205+
is Cognito -> verifyCognito(validation)
206+
207+
is ExpectationShapes.Amplify -> {
208+
val expected = validation.response.toString()
209+
val actual = apiExecutionResult.toJsonElement().toString()
210+
actual shouldEqualJson expected
211+
}
212+
is ExpectationShapes.State -> {
213+
val getStateLatch = CountDownLatch(1)
214+
var authState: AuthState? = null
215+
authStateMachine.getCurrentState {
216+
authState = it
217+
getStateLatch.countDown()
218+
}
219+
getStateLatch.await(10, TimeUnit.SECONDS)
220+
assertEquals(getState(validation.expectedState), authState)
221+
}
222+
}
223+
}
224+
225+
private fun getState(state: String): AuthState {
226+
val stateFileUrl = this::class.java.getResource("$STATES_FILE_BASE_PATH/$state")
227+
return File(stateFileUrl!!.file).readText().deserializeToAuthState()
228+
}
229+
230+
private fun verifyCognito(validation: Cognito) {
231+
val expectedRequest = CognitoRequestFactory.getExpectedRequestFor(validation)
232+
233+
coVerify {
234+
when (validation) {
235+
is CognitoIdentity -> mockCognitoIdClient to mockCognitoIdClient::class
236+
is CognitoIdentityProvider -> mockCognitoIPClient to mockCognitoIPClient::class
237+
}.apply {
238+
second.declaredFunctions.first {
239+
it.name == validation.apiName
240+
}.callSuspend(first, expectedRequest)
241+
}
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)