Skip to content

Commit

Permalink
test: initialization timeout hit when eb stalls (#215)
Browse files Browse the repository at this point in the history
* test: initialization timeout when eb stalls

* fix lints

* fix test

* lints

* fix imports

* add mock webserver to test
  • Loading branch information
tore-statsig authored Apr 25, 2024
1 parent cd19142 commit 247c7cf
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ dependencies {
testImplementation "io.mockk:mockk:1.12.0"
testImplementation 'com.github.tomakehurst:wiremock:2.27.2'
testImplementation "org.slf4j:slf4j-simple:1.8.0-beta4"
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.0'
}

artifacts {
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/statsig/androidsdk/ErrorBoundary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ internal class ErrorBoundary() {
this.apiKey = apiKey
}

fun getUrl(): String {
return urlString
}

fun setMetadata(statsigMetadata: StatsigMetadata) {
this.statsigMetadata = statsigMetadata
}
Expand Down Expand Up @@ -103,7 +107,7 @@ internal class ErrorBoundary() {
seen.add(name)

val metadata = statsigMetadata ?: StatsigMetadata("")
val url = URL(urlString)
val url = URL(getUrl())
val body = mapOf(
"exception" to name,
"info" to RuntimeException(exception).stackTraceToString(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.statsig.androidsdk

import android.app.Application
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import kotlinx.coroutines.*
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

class StatsigInitializationTimeoutTest {

private var app: Application = mockk()
private lateinit var client: StatsigClient
private lateinit var network: StatsigNetwork
private lateinit var errorBoundary: ErrorBoundary
private lateinit var mockWebServer: MockWebServer
private val hitErrorBoundary = CountDownLatch(1)

@Before
fun setup() {
mockWebServer = MockWebServer()
val dispatcher = object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse {
return if (request.path!!.contains("sdk_exception")) {
hitErrorBoundary.countDown()
runBlocking {
delay(1000)
}
MockResponse()
.setBody("{\"result\":\"error logged\"}")
.setResponseCode(200)
} else {
MockResponse().setResponseCode(404)
}
}
}
mockWebServer.dispatcher = dispatcher
mockWebServer.start()
client = spyk(StatsigClient(), recordPrivateCalls = true)
client.errorBoundary = spyk(client.errorBoundary)
errorBoundary = client.errorBoundary
network = TestUtil.mockNetwork()

TestUtil.mockDispatchers()
TestUtil.stubAppFunctions(app)

coEvery {
network.initialize(any(), any(), any(), any(), any(), any(), any(), any(), any())
} coAnswers {
TestUtil.makeInitializeResponse()
}

// Lets get a successful network response, and then trigger error boundary
// so we can test that eb does not block the initialization beyond the init timeout
every {
println("causing exception")
client["pollForUpdates"]()
} throws(Exception("trigger the error boundary"))

every {
errorBoundary.getUrl()
} returns mockWebServer.url("/v1/sdk_exception").toString()

client.statsigNetwork = network
client.errorBoundary = errorBoundary
}

@After
fun tearDown() {
mockWebServer.shutdown()
}

@Test
fun testInitializeAsyncWithSlowErrorBoundary() = runBlocking {
var initializationDetails: InitializationDetails? = null
var initTimeout = 500L
runBlocking {
initializationDetails = client.initialize(app, "client-key", StatsigUser("test_user"), StatsigOptions(initTimeoutMs = initTimeout))
}
// initialize timeout was hit, we got a value back and we are considered initialized
assert(initializationDetails != null)
assert(client.isInitialized())

// error boundary was hit, but has not completed at this point, so the initialization timeout worked
assertTrue(hitErrorBoundary.await(1, TimeUnit.SECONDS))
assertTrue(
"initialization time ${initializationDetails!!.duration} not less than initTimeout $initTimeout",
initializationDetails!!.duration < initTimeout + 100L,
)

// error boundary was hit, but has not completed at this point, so the initialization timeout worked
assert(hitErrorBoundary.await(1, TimeUnit.SECONDS))
}
}

0 comments on commit 247c7cf

Please sign in to comment.