Mokksy

Mokksy is a mock HTTP server built with Kotlin and Ktor.

Why? Wiremock does not support true SSE and streaming responses. Mokksy is here to address those limitations. It's particularly useful for integration testing LLM clients.

 1// Create a Mokksy instance
 2val mokksy = Mokksy()
 3
 4// Configure a response for a GET request
 5mokksy.get {
 6  path = beEqual("/ping")
 7} respondsWith {
 8  body = """{"response": "Pong"}"""
 9}
10
11// Start the server
12mokksy.start()
13
14// Use the server URL in your client
15val serverUrl = mokksy.serverUrl
16
17// Stop the server when done
18mokksy.stop()
 1// given
 2val expectedResponse =
 3    // language=json
 4    """
 5    {
 6        "response": "Pong"
 7    }
 8    """.trimIndent()
 9
10mokksy.get {
11    path = beEqual("/ping")
12    containsHeader("Foo", "bar")
13} respondsWith {
14    body = expectedResponse
15}
16
17// when
18val result = client.get("/ping") { 
19    headers.append("Foo", "bar")
20}
21
22// then
23assertThat(result.status).isEqualTo(HttpStatusCode.OK)
24assertThat(result.bodyAsText()).isEqualTo(expectedResponse)

POST Request

 1// given
 2val id = Random.nextInt()
 3val expectedResponse =
 4    // language=json
 5    """
 6    {
 7        "id": "$id",
 8        "name": "thing-$id"
 9    }
10    """.trimIndent()
11
12mokksy.post {
13    path = beEqual("/things")
14    bodyContains("\"$id\"")
15} respondsWith {
16    body = expectedResponse
17    httpStatus = HttpStatusCode.Created
18    headers {
19        // type-safe builder style
20        append(HttpHeaders.Location, "/things/$id")
21    }
22    headers += "Foo" to "bar" // list style
23}
24
25// when
26val result =
27    client.post("/things") {
28        headers.append("Content-Type", "application/json")
29        setBody(
30            // language=json
31            """
32            {
33                "id": "$id"
34            }
35            """.trimIndent(),
36        )
37    }
38
39// then
40assertThat(result.status).isEqualTo(HttpStatusCode.Created)
41assertThat(result.bodyAsText()).isEqualTo(expectedResponse)
42assertThat(result.headers["Location"]).isEqualTo("/things/$id")
43assertThat(result.headers["Foo"]).isEqualTo("bar")

Server-Side Events (SSE) is a technology that allows a server to push updates to the client over a single, long-lived HTTP connection. This enables real-time updates without requiring the client to continuously poll the server for new data.

SSE streams events in a standardized format, making it easy for clients to consume the data and handle events as they arrive. It's lightweight and efficient, particularly well-suited for applications requiring real-time updates like live notifications or feed updates.

 1mokksy.post {
 2    path = beEqual("/sse")
 3} respondsWithSseStream {
 4    flow =
 5        flow {
 6            delay(200.milliseconds)
 7            emit(
 8                ServerSentEvent(
 9                    data = "One",
10                ),
11            )
12            delay(50.milliseconds)
13            emit(
14                ServerSentEvent(
15                    data = "Two",
16                ),
17            )
18        }
19}
20
21// when
22val result = client.post("/sse")
23
24// then
25assertThat(result.status)
26    .isEqualTo(HttpStatusCode.OK)
27assertThat(result.contentType())
28    .isEqualTo(ContentType.Text.EventStream.withCharsetIfNeeded(Charsets.UTF_8))
29assertThat(result.bodyAsText())
30    .isEqualTo("data: One\r\ndata: Two\r\n")