Skip to content

Commit e893149

Browse files
committed
integration test fixes, use a test dispatcher and fix timing/order
1 parent 3fa4c2c commit e893149

File tree

1 file changed

+109
-36
lines changed

1 file changed

+109
-36
lines changed

android/src/test/java/com/amplitude/android/ResponseHandlerTest.kt

+109-36
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import io.mockk.every
1212
import io.mockk.mockkConstructor
1313
import io.mockk.mockkStatic
1414
import kotlinx.coroutines.ExperimentalCoroutinesApi
15+
import kotlinx.coroutines.test.StandardTestDispatcher
16+
import kotlinx.coroutines.test.TestCoroutineScheduler
17+
import kotlinx.coroutines.test.advanceUntilIdle
18+
import kotlinx.coroutines.test.runTest
1519
import okhttp3.mockwebserver.MockResponse
1620
import okhttp3.mockwebserver.MockWebServer
1721
import okhttp3.mockwebserver.RecordedRequest
@@ -26,12 +30,14 @@ import org.junit.runner.RunWith
2630
import org.robolectric.RobolectricTestRunner
2731
import java.util.concurrent.TimeUnit
2832

33+
private const val FLUSH_INTERVAL_IN_MS = 150
34+
35+
@ExperimentalCoroutinesApi
2936
@RunWith(RobolectricTestRunner::class)
3037
class ResponseHandlerTest {
3138
private lateinit var server: MockWebServer
3239
private lateinit var amplitude: Amplitude
3340

34-
@ExperimentalCoroutinesApi
3541
@Before
3642
fun setup() {
3743
server = MockWebServer()
@@ -46,7 +52,9 @@ class ResponseHandlerTest {
4652
context = context,
4753
serverUrl = server.url("/").toString(),
4854
autocapture = setOf(),
49-
flushIntervalMillis = 150,
55+
// required for [PayloadTooLargeResponse] test
56+
flushQueueSize = 2,
57+
flushIntervalMillis = FLUSH_INTERVAL_IN_MS,
5058
identifyBatchIntervalMillis = 1000,
5159
flushMaxRetries = 3,
5260
identityStorageProvider = IMIdentityStorageProvider(),
@@ -84,7 +92,8 @@ class ResponseHandlerTest {
8492
}
8593

8694
@Test
87-
fun `test handle on rate limit`() {
95+
fun `test handle on rate limit`() = runTest {
96+
setAmplitudeDispatchers(amplitude, testScheduler)
8897
val rateLimitBody = """
8998
{
9099
"code": 429,
@@ -95,18 +104,24 @@ class ResponseHandlerTest {
95104
for (i in 1..6) {
96105
server.enqueue(MockResponse().setBody(rateLimitBody).setResponseCode(429))
97106
}
107+
108+
amplitude.isBuilt.await()
98109
for (k in 1..4) {
99110
amplitude.track("test event $k")
100-
runRequest()
111+
advanceUntilIdle()
101112
}
102113
Thread.sleep(100)
114+
103115
// verify the total request count when reaching max retries
104116
assertEquals(6, server.requestCount)
105117
}
106118

107119
@Test
108120
fun `test handle payload too large with only one event`() {
109-
server.enqueue(MockResponse().setBody("{\"code\": \"413\", \"error\": \"payload too large\"}").setResponseCode(413))
121+
server.enqueue(
122+
MockResponse().setBody("{\"code\": \"413\", \"error\": \"payload too large\"}")
123+
.setResponseCode(413)
124+
)
110125
val options = EventOptions()
111126
var statusCode = 0
112127
var callFinished = false
@@ -128,40 +143,67 @@ class ResponseHandlerTest {
128143
}
129144

130145
@Test
131-
fun `test handle payload too large`() {
132-
server.enqueue(MockResponse().setBody("{\"code\": \"413\", \"error\": \"payload too large\"}").setResponseCode(413))
133-
server.enqueue(MockResponse().setBody("{\"code\": \"200\"}").setResponseCode(200))
134-
server.enqueue(MockResponse().setBody("{\"code\": \"200\"}").setResponseCode(200))
135-
server.enqueue(MockResponse().setBody("{\"code\": \"200\"}").setResponseCode(200))
146+
fun `test handle payload too large`() = runTest {
147+
setAmplitudeDispatchers(amplitude, testScheduler)
148+
val expectedSuccesEvents = 5
149+
server.enqueue(
150+
MockResponse().setBody("{\"code\": \"413\", \"error\": \"payload too large\"}")
151+
.setResponseCode(413)
152+
)
153+
repeat(expectedSuccesEvents) {
154+
server.enqueue(MockResponse().setBody("{\"code\": \"200\"}").setResponseCode(200))
155+
}
136156
var eventCompleteCount = 0
137157
val statusMap = mutableMapOf<Int, Int>()
138158
val options = EventOptions()
139159
options.callback = { _: BaseEvent, status: Int, _: String ->
140160
eventCompleteCount++
141-
statusMap.put(status, statusMap.getOrDefault(status, 0) + 1)
161+
statusMap[status] = statusMap.getOrDefault(status, 0) + 1
142162
}
163+
164+
// send 2 events so the event size will be greater than 1 and call split event file
165+
amplitude.isBuilt.await()
143166
amplitude.track("test event 1", options = options)
144167
amplitude.track("test event 2", options = options)
168+
advanceUntilIdle()
169+
170+
// verify first request that hit 413
171+
val request = runRequest()
172+
requireNotNull(request)
173+
val failedEvents = getEventsFromRequest(request)
174+
assertEquals(2, failedEvents.size)
175+
176+
// send succeeding events
145177
amplitude.track("test event 3", options = options)
146178
amplitude.track("test event 4", options = options)
147-
val request = runRequest()
148-
// verify the first request hit 413
149-
assertNotNull(request)
150-
val events = getEventsFromRequest(request!!)
151-
assertEquals(4, events.size)
152179
amplitude.track("test event 5", options = options)
153-
runRequest()
154-
runRequest()
155-
runRequest()
156-
Thread.sleep(150)
180+
advanceUntilIdle()
181+
182+
// verify next requests after split event file
183+
val splitRequest1 = runRequest()
184+
requireNotNull(splitRequest1)
185+
val splitEvents1 = getEventsFromRequest(splitRequest1)
186+
assertEquals(1, splitEvents1.size)
187+
val splitRequest2 = runRequest()
188+
requireNotNull(splitRequest2)
189+
val splitEvents2 = getEventsFromRequest(splitRequest2)
190+
assertEquals(1, splitEvents2.size)
191+
192+
// verify we completed processing for the events after file split
193+
val afterSplitRequest = runRequest()
194+
requireNotNull(afterSplitRequest)
195+
val afterSplitEvents = getEventsFromRequest(afterSplitRequest)
196+
assertEquals(3, afterSplitEvents.size)
197+
157198
// verify we completed processing for the events after file split
158199
assertEquals(4, server.requestCount)
159-
assertEquals(5, statusMap.get(200))
160-
assertEquals(5, eventCompleteCount)
200+
assertEquals(expectedSuccesEvents, statusMap[200])
201+
assertEquals(expectedSuccesEvents, eventCompleteCount)
161202
}
162203

163204
@Test
164-
fun `test handle bad request response`() {
205+
fun `test handle bad request response`() = runTest {
206+
setAmplitudeDispatchers(amplitude, testScheduler)
165207
val badRequestResponseBody = """
166208
{
167209
"code": 400,
@@ -185,27 +227,37 @@ class ResponseHandlerTest {
185227
val options = EventOptions()
186228
options.callback = { _: BaseEvent, status: Int, _: String ->
187229
eventCompleteCount++
188-
statusMap.put(status, statusMap.getOrDefault(status, 0) + 1)
230+
statusMap[status] = statusMap.getOrDefault(status, 0) + 1
189231
}
232+
233+
amplitude.isBuilt.await()
190234
amplitude.track("test event 1", options = options)
235+
advanceUntilIdle()
236+
237+
// verify first request that hit 400
238+
val request = runRequest()
239+
requireNotNull(request)
240+
val failedEvents = getEventsFromRequest(request)
241+
assertEquals(1, failedEvents.size)
242+
243+
// send succeeding events
191244
amplitude.track("test event 2", options = options)
192245
amplitude.track("test event 3", options = options)
193246
amplitude.track("test event 4", options = options)
194-
// verify first request take 4 events hit 400
195-
val request = runRequest()
196-
assertNotNull(request)
197-
val events = getEventsFromRequest(request!!)
198-
assertEquals(4, events.size)
199-
// verify second request take 2 events after removing 2 bad events
200-
val request2 = runRequest()
201-
assertNotNull(request2)
202-
val events2 = getEventsFromRequest(request2!!)
203-
assertEquals(2, events2.size)
247+
advanceUntilIdle()
248+
249+
// verify next request that the 3 events with 200
250+
val successRequest = runRequest()
251+
requireNotNull(successRequest)
252+
val successfulEvents = getEventsFromRequest(successRequest)
253+
assertEquals(3, successfulEvents.size)
254+
204255
assertEquals(2, server.requestCount)
205256
Thread.sleep(10)
257+
206258
// verify the processed status
207-
assertEquals(2, statusMap.get(400))
208-
assertEquals(2, statusMap.get(200))
259+
assertEquals(1, statusMap[400])
260+
assertEquals(3, statusMap[200])
209261
assertEquals(4, eventCompleteCount)
210262
}
211263

@@ -287,4 +339,25 @@ class ResponseHandlerTest {
287339
every { anyConstructed<AndroidContextProvider>().mostRecentLocation } returns null
288340
every { anyConstructed<AndroidContextProvider>().appSetId } returns ""
289341
}
342+
343+
companion object {
344+
private fun setAmplitudeDispatchers(
345+
amplitude: com.amplitude.core.Amplitude,
346+
testCoroutineScheduler: TestCoroutineScheduler,
347+
) {
348+
// inject these dispatcher fields with reflection, as the field is val (read-only)
349+
listOf(
350+
"amplitudeDispatcher",
351+
"networkIODispatcher",
352+
"storageIODispatcher",
353+
"retryDispatcher"
354+
).forEach { dispatcherField ->
355+
com.amplitude.core.Amplitude::class.java.getDeclaredField(dispatcherField)
356+
.apply {
357+
isAccessible = true
358+
set(amplitude, StandardTestDispatcher(testCoroutineScheduler))
359+
}
360+
}
361+
}
362+
}
290363
}

0 commit comments

Comments
 (0)