@@ -42,10 +42,10 @@ struct LambdaRuntimeClientTests {
42
42
. success( ( self . requestId, self . event) )
43
43
}
44
44
45
- func processResponse( requestId: String , response: String ? ) -> Result < Void , ProcessResponseError > {
45
+ func processResponse( requestId: String , response: String ? ) -> Result < String ? , ProcessResponseError > {
46
46
#expect( self . requestId == requestId)
47
47
#expect( self . event == response)
48
- return . success( ( ) )
48
+ return . success( nil )
49
49
}
50
50
51
51
func processError( requestId: String , error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
@@ -102,9 +102,9 @@ struct LambdaRuntimeClientTests {
102
102
. success( ( self . requestId, self . event) )
103
103
}
104
104
105
- func processResponse( requestId: String , response: String ? ) -> Result < Void , ProcessResponseError > {
105
+ func processResponse( requestId: String , response: String ? ) -> Result < String ? , ProcessResponseError > {
106
106
#expect( self . requestId == requestId)
107
- return . success( ( ) )
107
+ return . success( nil )
108
108
}
109
109
110
110
mutating func captureHeaders( _ headers: HTTPHeaders ) {
@@ -197,10 +197,10 @@ struct LambdaRuntimeClientTests {
197
197
. success( ( self . requestId, self . event) )
198
198
}
199
199
200
- func processResponse( requestId: String , response: String ? ) -> Result < Void , ProcessResponseError > {
200
+ func processResponse( requestId: String , response: String ? ) -> Result < String ? , ProcessResponseError > {
201
201
#expect( self . requestId == requestId)
202
202
#expect( self . event == response)
203
- return . success( ( ) )
203
+ return . success( nil )
204
204
}
205
205
206
206
func processError( requestId: String , error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
@@ -238,4 +238,91 @@ struct LambdaRuntimeClientTests {
238
238
}
239
239
}
240
240
}
241
+
242
+ struct DisconnectAfterSendingResponseBehavior : LambdaServerBehavior {
243
+ func getInvocation( ) -> GetInvocationResult {
244
+ . success( ( UUID ( ) . uuidString, " hello " ) )
245
+ }
246
+
247
+ func processResponse( requestId: String , response: String ? ) -> Result < String ? , ProcessResponseError > {
248
+ // Return "delayed-disconnect" to trigger server closing the connection
249
+ // after having accepted the first response
250
+ . success( " delayed-disconnect " )
251
+ }
252
+
253
+ func processError( requestId: String , error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
254
+ Issue . record ( " should not report error " )
255
+ return . failure( . internalServerError)
256
+ }
257
+
258
+ func processInitError( error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
259
+ Issue . record ( " should not report init error " )
260
+ return . failure( . internalServerError)
261
+ }
262
+ }
263
+
264
+ struct DisconnectBehavior : LambdaServerBehavior {
265
+ func getInvocation( ) -> GetInvocationResult {
266
+ . success( ( " disconnect " , " 0 " ) )
267
+ }
268
+
269
+ func processResponse( requestId: String , response: String ? ) -> Result < String ? , ProcessResponseError > {
270
+ . success( nil )
271
+ }
272
+
273
+ func processError( requestId: String , error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
274
+ Issue . record ( " should not report error " )
275
+ return . failure( . internalServerError)
276
+ }
277
+
278
+ func processInitError( error: ErrorResponse ) -> Result < Void , ProcessErrorError > {
279
+ Issue . record ( " should not report init error " )
280
+ return . failure( . internalServerError)
281
+ }
282
+ }
283
+
284
+ @Test (
285
+ " Server closing the connection when waiting for next invocation throws an error " ,
286
+ arguments: [ DisconnectBehavior ( ) , DisconnectAfterSendingResponseBehavior ( ) ] as [ any LambdaServerBehavior ]
287
+ )
288
+ func testChannelCloseFutureWithWaitingForNextInvocation( behavior: LambdaServerBehavior ) async throws {
289
+ try await withMockServer ( behaviour: behavior) { port in
290
+ let configuration = LambdaRuntimeClient . Configuration ( ip: " 127.0.0.1 " , port: port)
291
+
292
+ try await LambdaRuntimeClient . withRuntimeClient (
293
+ configuration: configuration,
294
+ eventLoop: NIOSingletons . posixEventLoopGroup. next ( ) ,
295
+ logger: self . logger
296
+ ) { runtimeClient in
297
+ do {
298
+
299
+ // simulate traffic until the server reports it has closed the connection
300
+ // or a timeout, whichever comes first
301
+ // result is ignored here, either there is a connection error or a timeout
302
+ let _ = try await timeout ( deadline: . seconds( 1 ) ) {
303
+ while true {
304
+ let ( _, writer) = try await runtimeClient. nextInvocation ( )
305
+ try await writer. writeAndFinish ( ByteBuffer ( string: " hello " ) )
306
+ }
307
+ }
308
+ // result is ignored here, we should never reach this line
309
+ Issue . record ( " Connection reset test did not throw an error " )
310
+
311
+ } catch is CancellationError {
312
+ Issue . record ( " Runtime client did not send connection closed error " )
313
+ } catch let error as LambdaRuntimeError {
314
+ logger. trace ( " LambdaRuntimeError - expected " )
315
+ #expect( error. code == . connectionToControlPlaneLost)
316
+ } catch let error as ChannelError {
317
+ logger. trace ( " ChannelError - expected " )
318
+ #expect( error == . ioOnClosedChannel)
319
+ } catch let error as IOError {
320
+ logger. trace ( " IOError - expected " )
321
+ #expect( error. errnoCode == ECONNRESET || error. errnoCode == EPIPE)
322
+ } catch {
323
+ Issue . record ( " Unexpected error type: \( error) " )
324
+ }
325
+ }
326
+ }
327
+ }
241
328
}
0 commit comments