https://kotlinlang.org logo
#http4k
Title
# http4k
a

Andrew O'Hara

03/21/2024, 8:07 PM
I'm having trouble using
http4k-testing-chaos
to test a retry loop. Basically I want to make it so the first request fails and then succeeds every time after. But what seems to happen is the
Behaviour
(yay for British and Canadian English overlap!) gets called once during the first request, and twice during the second request. So I can't properly test the number of times my server is called. Am I doing this wrong? Or is there a
http4k-testing-chaos
bug?
Copy code
@Test
    fun foo() {
        val server = object: ChaoticHttpHandler() {
            override val app: HttpHandler = { Response(Status.OK).body("foo") }
        }

        var requestCount = 0
        server.misbehave(object: Behaviour() {
            override fun invoke(next: HttpHandler): HttpHandler = {
                requestCount += 1
                if (requestCount == 1) {
                    Response(Status.INTERNAL_SERVER_ERROR)
                } else {
                    next(it)
                }
            }
        })

        var attempts = 0
        do {
            attempts += 1
            val response = server(Request(Method.GET, ""))
        } while(!response.status.successful)

        attempts shouldBe 2
        requestCount shouldBe 2
    }
The
attempts
test passes, but
requestCount
is actually 3
a

Andrew O'Hara

03/21/2024, 11:37 PM
Yes, that would give me the chaos behaviour I want, but not the observability that the custom behaviour gives me
f

fredrik.nordin

03/22/2024, 9:27 AM
@Andrew O'Hara I've had a similar requirement in one of my tests (I needed to assert attempts and total execution time) and this is the approach I used. It might help you out with a possible approach for your usecase.
Copy code
private inner class TestFixture(
    config: ChaosEngine.() -> ChaosEngine = { this }
) : HttpHandler {

    var callCount = 0
    var totalTimeMs = 0L

    private val handler =
        ResponseFilters.ReportHttpTransaction { totalTimeMs = it.duration.toMillis() }
            .then(retryFilter()) // This is the bit I wanted to test
            .then { next: HttpHandler ->
                {
                    callCount++
                    next(it)
                }
            }
            .then(config(ChaosEngine()))
            .then { Response(Status.OK).body("All is OK!") }

        override fun invoke(request: Request): Response = handler(request)
    }

// In the test
val throwsExceptionForOneRequest: ChaosEngineConfigurator = {
    enable(ThrowException(NoHttpResponseException("ka-boom!")).appliedWhen(Countdown(1).not()))
}
val fixture = TestFixture(config = throwsExceptionForOneRequest)

val response = fixture(Request(Method.GET, "/"))

assertThat(response, hasStatus(Status.OK))
 assertThat(fixture.callCount, equalTo(2))
I didn't use the
ChaoticHttpHandler
directly, but ended up writing my own test fixture that uses the Chaos Engine.
d

dave

03/22/2024, 9:43 AM
Fun fact for people that aren't as ancient as me. The ChaosEngine was christened after this game: https://en.wikipedia.org/wiki/The_Chaos_Engine
a

Andrew O'Hara

03/22/2024, 1:16 PM
Yeah, that game came before my time 🙃
Thanks @fredrik.nordin. I'll see about adapting that