@@ -11,7 +11,8 @@ import dev.mokkery.verify.VerifyMode.Companion.exactly
11
11
import dev.mokkery.verify.VerifyMode.Companion.not
12
12
import dev.mokkery.verify.VerifyMode.Companion.order
13
13
import dev.mokkery.verifySuspend
14
- import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.*
14
+ import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.BadRequestException
15
+ import io.github.trueangle.knative.lambda.runtime.LambdaEnvironmentException.NonRecoverableStateException
15
16
import io.github.trueangle.knative.lambda.runtime.LambdaRuntimeException.Invocation.EventBodyParseException
16
17
import io.github.trueangle.knative.lambda.runtime.LambdaRuntimeException.Invocation.HandlerException
17
18
import io.github.trueangle.knative.lambda.runtime.ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_FUNCTION_NAME
@@ -33,12 +34,8 @@ import io.ktor.client.engine.mock.MockRequestHandleScope
33
34
import io.ktor.client.engine.mock.respond
34
35
import io.ktor.client.engine.mock.respondBadRequest
35
36
import io.ktor.client.engine.mock.respondError
36
- import io.ktor.client.engine.mock.respondOk
37
- import io.ktor.client.request.HttpRequestData
38
37
import io.ktor.http.HttpHeaders
39
38
import io.ktor.http.HttpStatusCode
40
- import io.ktor.http.content.ChannelWriterContent
41
- import io.ktor.http.content.OutgoingContent
42
39
import io.ktor.http.headers
43
40
import io.ktor.http.headersOf
44
41
import io.ktor.util.reflect.typeInfo
@@ -48,16 +45,15 @@ import io.ktor.utils.io.copyTo
48
45
import kotlinx.cinterop.ExperimentalForeignApi
49
46
import kotlinx.cinterop.toKString
50
47
import kotlinx.coroutines.test.runTest
51
- import kotlinx.io.Buffer
52
- import kotlinx.io.RawSource
53
- import kotlinx.io.Source
54
48
import kotlinx.serialization.json.Json
55
49
import platform.posix.getenv
56
50
import platform.posix.setenv
51
+ import kotlin.experimental.ExperimentalNativeApi
52
+ import kotlin.native.runtime.GC
53
+ import kotlin.native.runtime.NativeRuntimeApi
57
54
import kotlin.test.BeforeTest
58
55
import kotlin.test.Test
59
56
import kotlin.test.assertFailsWith
60
- import kotlin.test.assertTrue
61
57
62
58
internal const val RESOURCES_PATH = " src/nativeTest/resources"
63
59
@@ -197,10 +193,7 @@ class LambdaRuntimeTest {
197
193
val lambdaRunner = createRunner(MockEngine { request ->
198
194
val path = request.url.encodedPath
199
195
when {
200
- path.contains(" /invocation/next" ) -> {
201
- respondError(HttpStatusCode .InternalServerError )
202
- }
203
-
196
+ path.contains(" /invocation/next" ) -> respondError(HttpStatusCode .InternalServerError )
204
197
else -> respondError(HttpStatusCode .Forbidden )
205
198
}
206
199
})
@@ -368,6 +361,68 @@ class LambdaRuntimeTest {
368
361
verify(not ) { lambdaRunner.env.terminate() }
369
362
}
370
363
364
+ @OptIn(NativeRuntimeApi ::class , ExperimentalStdlibApi ::class , ExperimentalNativeApi ::class )
365
+ @Test
366
+ fun `Validate leaks` () = runTest {
367
+ val invocationCount = 3
368
+ var invocationIndex = 0
369
+ val events = buildList(invocationCount) {
370
+ repeat(invocationCount) { add(" Hello world" ) }
371
+ }
372
+
373
+ val lambdaRunner = createRunner(MockEngine { request ->
374
+ val path = request.url.encodedPath
375
+ when {
376
+ path.contains(" invocation/next" ) -> {
377
+ if (invocationIndex >= invocationCount) {
378
+ respondError(HttpStatusCode .InternalServerError , headers = headers {
379
+ append(HttpHeaders .ContentType , " application/json" )
380
+ append(" Lambda-Runtime-Aws-Request-Id" , context.awsRequestId)
381
+ append(" Lambda-Runtime-Deadline-Ms" , context.deadlineTimeInMs.toString())
382
+ append(" Lambda-Runtime-Invoked-Function-Arn" , context.invokedFunctionArn)
383
+ })
384
+ } else {
385
+ respondNextEventSuccess(events[invocationIndex++ ])
386
+ }
387
+ }
388
+
389
+ path.contains(" /invocation/${context.awsRequestId} /response" ) -> respond(
390
+ content = ByteReadChannel (" Ok" ),
391
+ status = HttpStatusCode .Accepted ,
392
+ headers = headersOf(HttpHeaders .ContentType , " application/json" )
393
+ )
394
+
395
+ else -> respondBadRequest()
396
+ }
397
+ })
398
+
399
+ val handler = object : LambdaBufferedHandler <String , String > {
400
+ override suspend fun handleRequest (input : String , context : Context ): String = " Hello"
401
+ }
402
+
403
+ assertFailsWith<TerminateException > {
404
+ lambdaRunner.run { handler }
405
+ }
406
+
407
+ GC .collect()
408
+ GC .lastGCInfo?.let {gcInfo->
409
+ println (
410
+ " Heap Size Before: ${
411
+ gcInfo.memoryUsageBefore.map {
412
+ it.key + " - " + it.value.totalObjectsSizeBytes / 1024 / 1024
413
+ }
414
+ } "
415
+ )
416
+ println (
417
+ " Heap Size After: ${
418
+ gcInfo.memoryUsageAfter.map {
419
+ it.key + " - " + it.value.totalObjectsSizeBytes / 1024 / 1024
420
+ }
421
+ } "
422
+ )
423
+ }
424
+ }
425
+
371
426
@OptIn(ExperimentalForeignApi ::class )
372
427
private fun mockEnvironment () {
373
428
if (getenv(AWS_LAMBDA_FUNCTION_NAME )?.toKString().isNullOrEmpty()) {
0 commit comments