https://kotlinlang.org logo
#ktor
Title
# ktor
ł

Łukasz Bednarczyk

08/23/2021, 12:22 PM
[Solved] 🙂 I have a strange problem. I get:
Copy code
Response has already been sent
io.ktor.server.engine.BaseApplicationResponse$ResponseAlreadySentException: Response has already been sent
but only in tests and only in the situation when I call
Copy code
every { something, something } throws AnyException
Did you have similar problem?
Exceptions thrown by mocked objects should be handle by the StatusPages plugin.
a

Aleksei Tirman [JB]

08/23/2021, 1:40 PM
Could you please share a complete code snippet?
ł

Łukasz Bednarczyk

08/23/2021, 1:53 PM
Copy code
class AudioFilesHandlerTest : DescribeSpec(), KoinTest {

    private val deviceAuthenticationService = mockk<DeviceAuthenticationService>()

    init {
        startKoin {
            modules(
                module {
                    single { deviceAuthenticationService }
                })
        }

        describe("AudioFilesHandler") {
            val audioFilesUri = "/api/audio-files"

            describe(audioFilesUri) {
                describe("when device auth params are missing") {
                    it("should returns status 400") {
                        withApplication(apiTestEnv) {
                            val call = handleRequest(uri = audioFilesUri, method = <http://HttpMethod.Post|HttpMethod.Post>) {
                                val boundary = "WebAppBoundary"
                                addHeader(ContentType, FormData.withParameter("boundary", boundary).toString())
                            }

                            call.response.status() shouldBe HttpStatusCode.BadRequest
                        }
                    }
                }

                describe("when device is unregistered") {
                    val product = createProduct()
                    val device = createDevice(product = product)
                    every {
                        deviceAuthenticationService.challenge(product.productId, device.deviceId)
                    } throws DeviceAuthenticationException()

                    it("should returns 401") {
                        withApplication(apiTestEnv) {
                            val call = handleRequest(uri = audioFilesUri, method = <http://HttpMethod.Post|HttpMethod.Post>) {
                                val boundary = "WebAppBoundary"
                                addHeader(ContentType, FormData.withParameter("boundary", boundary).toString())
                                addHeader("productId", product.productId)
                                addHeader("deviceId", device.deviceId)
                            }

                            call.response.status() shouldBe HttpStatusCode.Unauthorized
                        }
                    }
                }
            }
        }
    }
}
@Aleksei Tirman [JB]
Copy code
install(StatusPages) {
        exception<DeviceAuthenticationException> {
            call.respond(HttpStatusCode.Unauthorized)
        }
        exception<UnsupportedDeviceException> {
            call.respond(HttpStatusCode.Forbidden)
        }
    }
a

Aleksei Tirman [JB]

08/23/2021, 2:23 PM
Please, also, share a full stack trace for the
ResponseAlreadySentException
.
ł

Łukasz Bednarczyk

08/23/2021, 2:28 PM
ok, ok
Copy code
Response has already been sent
io.ktor.server.engine.BaseApplicationResponse$ResponseAlreadySentException: Response has already been sent
	at io.ktor.server.engine.BaseApplicationResponse.commitHeaders(BaseApplicationResponse.kt:47)
	at io.ktor.server.engine.BaseApplicationResponse.respondOutgoingContent$suspendImpl(BaseApplicationResponse.kt:143)
	at io.ktor.server.engine.BaseApplicationResponse.respondOutgoingContent(BaseApplicationResponse.kt)
	at io.ktor.server.testing.TestApplicationResponse.respondOutgoingContent(TestApplicationResponse.kt:113)
	at io.ktor.server.engine.BaseApplicationResponse$Companion$setupSendPipeline$1.invokeSuspend(BaseApplicationResponse.kt:320)
	at io.ktor.server.engine.BaseApplicationResponse$Companion$setupSendPipeline$1.invoke(BaseApplicationResponse.kt)
	at io.ktor.server.engine.BaseApplicationResponse$Companion$setupSendPipeline$1.invoke(BaseApplicationResponse.kt)
	at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:248)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:126)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invokeSuspend(DefaultTransform.kt:35)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invoke(DefaultTransform.kt)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invoke(DefaultTransform.kt)
	at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:248)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(SuspendFunctionGun.kt:126)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invokeSuspend(DefaultTransform.kt:35)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invoke(DefaultTransform.kt)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invoke(DefaultTransform.kt)
	at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:248)
	at io.ktor.util.pipeline.SuspendFunctionGun.proceed(SuspendFunctionGun.kt:116)
	at io.ktor.util.pipeline.SuspendFunctionGun.execute(SuspendFunctionGun.kt:136)
	at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:79)
	at com.vascoelectronics.ApiKt$configureAPI$4$1.invokeSuspend(Api.kt:112)
	at com.vascoelectronics.ApiKt$configureAPI$4$1.invoke(Api.kt)
	at com.vascoelectronics.ApiKt$configureAPI$4$1.invoke(Api.kt)
	at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:107)
	at io.ktor.features.StatusPages.access$interceptCall(StatusPages.kt:18)
	at io.ktor.features.StatusPages$interceptCall$1.invokeSuspend(StatusPages.kt)
	(Coroutine boundary)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invokeSuspend(DefaultTransform.kt:35)
	at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$1.invokeSuspend(DefaultTransform.kt:35)
	at com.vascoelectronics.ApiKt$configureAPI$4$1.invokeSuspend(Api.kt:112)
	at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:107)
	at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:142)
	at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:139)
	at io.ktor.server.testing.TestApplicationEngine$callInterceptor$1.invokeSuspend(TestApplicationEngine.kt:296)
	at io.ktor.server.testing.TestApplicationEngine$2.invokeSuspend(TestApplicationEngine.kt:50)
	at io.ktor.server.testing.TestApplicationEngine$handleRequest$pipelineJob$1.invokeSuspend(TestApplicationEngine.kt:295)
	at io.ktor.server.testing.TestApplicationEngine$handleRequest$1.invokeSuspend(TestApplicationEngine.kt:153)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1$3$1$1.invokeSuspend(TestCaseExecutor.kt:234)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1$3$1.invokeSuspend(TestCaseExecutor.kt:233)
	at io.kotest.engine.ExecutorExecutionContext$executeWithTimeoutInterruption$4.invokeSuspend(ExecutorExecutionContext.kt:77)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1$3.invokeSuspend(TestCaseExecutor.kt:232)
	at io.kotest.mpp.ReplayKt.replay(replay.kt:18)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1.invokeSuspend(TestCaseExecutor.kt:227)
	at io.kotest.engine.ExecutorExecutionContext$executeWithTimeoutInterruption$4.invokeSuspend(ExecutorExecutionContext.kt:77)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1.invokeSuspend(TestCaseExecutor.kt:220)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4.invokeSuspend(TestCaseExecutor.kt:219)
	at io.kotest.core.internal.TestCaseExecutor.invokeTestCase(TestCaseExecutor.kt:184)
	at io.kotest.core.internal.TestCaseExecutor.executeActiveTest(TestCaseExecutor.kt:153)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.executeIfEnabled(TestCaseExecutor.kt:117)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.execute(TestCaseExecutor.kt:69)
	at io.kotest.engine.spec.runners.SingleInstanceSpecRunner.runTest(SingleInstanceSpecRunner.kt:104)
	at com.vascoelectronics.api.handlers.AudioFilesHandlerTest$2$1$2.invokeSuspend(AudioFilesHandlerTest.kt:63)
	at io.kotest.core.spec.style.scopes.DescribeSpecContainerContext$containerTest$2.invokeSuspend(DescribeSpecContainerContext.kt:99)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2$1.invokeSuspend(executions.kt:13)
	at io.kotest.core.internal.ExecutionsKt.wrapTestWithGlobalAssert(executions.kt:39)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2.invokeSuspend(executions.kt:12)
	at io.kotest.core.internal.TestCaseExecutor$executeInScope$2.invokeSuspend(TestCaseExecutor.kt:268)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1.invokeSuspend(TestCaseExecutor.kt:223)
	at io.kotest.engine.ExecutorExecutionContext$executeWithTimeoutInterruption$4.invokeSuspend(ExecutorExecutionContext.kt:77)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1.invokeSuspend(TestCaseExecutor.kt:220)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4.invokeSuspend(TestCaseExecutor.kt:219)
	at io.kotest.core.internal.TestCaseExecutor.invokeTestCase(TestCaseExecutor.kt:184)
	at io.kotest.core.internal.TestCaseExecutor.executeActiveTest(TestCaseExecutor.kt:153)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.executeIfEnabled(TestCaseExecutor.kt:117)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.execute(TestCaseExecutor.kt:69)
	at io.kotest.engine.spec.runners.SingleInstanceSpecRunner.runTest(SingleInstanceSpecRunner.kt:104)
	at com.vascoelectronics.api.handlers.AudioFilesHandlerTest$2$1.invokeSuspend(AudioFilesHandlerTest.kt:56)
	at io.kotest.core.spec.style.scopes.DescribeSpecContainerContext$containerTest$2.invokeSuspend(DescribeSpecContainerContext.kt:99)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2$1.invokeSuspend(executions.kt:13)
	at io.kotest.core.internal.ExecutionsKt.wrapTestWithGlobalAssert(executions.kt:39)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2.invokeSuspend(executions.kt:12)
	at io.kotest.core.internal.TestCaseExecutor$executeInScope$2.invokeSuspend(TestCaseExecutor.kt:268)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1.invokeSuspend(TestCaseExecutor.kt:223)
	at io.kotest.engine.ExecutorExecutionContext$executeWithTimeoutInterruption$4.invokeSuspend(ExecutorExecutionContext.kt:77)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1.invokeSuspend(TestCaseExecutor.kt:220)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4.invokeSuspend(TestCaseExecutor.kt:219)
	at io.kotest.core.internal.TestCaseExecutor.invokeTestCase(TestCaseExecutor.kt:184)
	at io.kotest.core.internal.TestCaseExecutor.executeActiveTest(TestCaseExecutor.kt:153)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.executeIfEnabled(TestCaseExecutor.kt:117)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.execute(TestCaseExecutor.kt:69)
	at io.kotest.engine.spec.runners.SingleInstanceSpecRunner.runTest(SingleInstanceSpecRunner.kt:104)
	at com.vascoelectronics.api.handlers.AudioFilesHandlerTest$2.invokeSuspend(AudioFilesHandlerTest.kt:42)
	at io.kotest.core.spec.style.scopes.DescribeSpecRootContext$test$1.invokeSuspend(DescribeSpecRootContext.kt:71)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2$1.invokeSuspend(executions.kt:13)
	at io.kotest.core.internal.ExecutionsKt.wrapTestWithGlobalAssert(executions.kt:39)
	at io.kotest.core.internal.ExecutionsKt$executeWithBehaviours$2.invokeSuspend(executions.kt:12)
	at io.kotest.core.internal.TestCaseExecutor$executeInScope$2.invokeSuspend(TestCaseExecutor.kt:268)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1$1.invokeSuspend(TestCaseExecutor.kt:223)
	at io.kotest.engine.ExecutorExecutionContext$executeWithTimeoutInterruption$4.invokeSuspend(ExecutorExecutionContext.kt:77)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4$1.invokeSuspend(TestCaseExecutor.kt:220)
	at io.kotest.core.internal.TestCaseExecutor$executeAndWait$4.invokeSuspend(TestCaseExecutor.kt:219)
	at io.kotest.core.internal.TestCaseExecutor.invokeTestCase(TestCaseExecutor.kt:184)
	at io.kotest.core.internal.TestCaseExecutor.executeActiveTest(TestCaseExecutor.kt:153)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.executeIfEnabled(TestCaseExecutor.kt:117)
	at io.kotest.core.internal.TestCaseExecutor$intercept$innerExecute$1.invokeSuspend(TestCaseExecutor.kt:89)
	at io.kotest.core.internal.TestCaseExecutor.execute(TestCaseExecutor.kt:69)
	at io.kotest.engine.spec.runners.SingleInstanceSpecRunner.runTest(SingleInstanceSpecRunner.kt:104)
	at io.kotest.engine.spec.runners.SingleInstanceSpecRunner$execute$interceptAndRun$2$4.invokeSuspend(SingleInstanceSpecRunner.kt:57)
	at io.kotest.engine.launchers.SequentialTestLauncher$launch$3$1$1.invokeSuspend(SequentialTestLauncher.kt:22)
@Aleksei Tirman [JB] do you need also Apki.kt file?
a

Aleksei Tirman [JB]

08/23/2021, 2:43 PM
It's hard to investigate it without being able to reproduce it. Could you please share a project?
ł

Łukasz Bednarczyk

08/23/2021, 3:07 PM
Ok
I’ve wrote to you
FYI: The problem is that the test environment in the
withApplication(apiTestEnv)
calls are shared. In the Ktor code base
withTestApplication
is called without parameters so a test environment is created for each call.
6 Views