@@ -6,12 +6,73 @@ import * as sinon from 'sinon';
6
6
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc' ;
7
7
import 'mocha' ;
8
8
import { load } from 'grpc' ;
9
+ import { worker } from 'cluster' ;
9
10
10
11
describe ( 'WorkerChannel' , ( ) => {
11
12
var channel : WorkerChannel ;
12
13
var stream : TestEventStream ;
13
14
var loader : sinon . SinonStubbedInstance < FunctionLoader > ;
14
15
var functions ;
16
+
17
+ const runInvokedFunction = ( ) => {
18
+ const triggerDataMock : { [ k : string ] : rpc . ITypedData } = {
19
+ "Headers" : {
20
+ json : JSON . stringify ( { Connection : 'Keep-Alive' } )
21
+ } ,
22
+ "Sys" : {
23
+ json : JSON . stringify ( { MethodName : 'test-js' , UtcNow : '2018' , RandGuid : '3212' } )
24
+ }
25
+ } ;
26
+
27
+ const inputDataValue = {
28
+ name : "req" ,
29
+ data : {
30
+ data : "http" ,
31
+ http :
32
+ {
33
+ body :
34
+ {
35
+ data : "string" ,
36
+ body : "blahh"
37
+ } ,
38
+ rawBody :
39
+ {
40
+ data : "string" ,
41
+ body : "blahh"
42
+ }
43
+ }
44
+ }
45
+ } ;
46
+
47
+ const actualInvocationRequest : rpc . IInvocationRequest = < rpc . IInvocationRequest > {
48
+ functionId : 'id' ,
49
+ invocationId : '1' ,
50
+ inputData : [ inputDataValue ] ,
51
+ triggerMetadata : triggerDataMock ,
52
+ } ;
53
+
54
+ stream . addTestMessage ( {
55
+ invocationRequest : actualInvocationRequest
56
+ } ) ;
57
+
58
+ return [ inputDataValue , actualInvocationRequest ] ;
59
+ }
60
+
61
+ const assertInvokedFunction = ( inputDataValue , actualInvocationRequest ) => {
62
+ sinon . assert . calledWithMatch ( stream . written , < rpc . IStreamingMessage > {
63
+ invocationResponse : {
64
+ invocationId : '1' ,
65
+ result : {
66
+ status : rpc . StatusResult . Status . Success
67
+ } ,
68
+ outputData : [ ]
69
+ }
70
+ } ) ;
71
+
72
+ // triggerMedata will be augmented with inpuDataValue since "RpcHttpTriggerMetadataRemoved" capability is set to true and therefore not populated by the host.
73
+ expect ( JSON . stringify ( actualInvocationRequest . triggerMetadata ! . $request ) ) . to . equal ( JSON . stringify ( inputDataValue . data ) ) ;
74
+ expect ( JSON . stringify ( actualInvocationRequest . triggerMetadata ! . req ) ) . to . equal ( JSON . stringify ( inputDataValue . data ) ) ;
75
+ }
15
76
16
77
beforeEach ( ( ) => {
17
78
stream = new TestEventStream ( ) ;
@@ -226,67 +287,131 @@ describe('WorkerChannel', () => {
226
287
workerStatusResponse : { }
227
288
} ) ;
228
289
} ) ;
290
+
291
+ describe ( '#invocationRequestBefore, #invocationRequestAfter' , ( ) => {
292
+ afterEach ( ( ) => {
293
+ channel [ '_invocationRequestAfter' ] = [ ] ;
294
+ channel [ '_invocationRequestBefore' ] = [ ] ;
295
+ } ) ;
229
296
230
- it ( 'invokes function' , ( ) => {
231
- loader . getFunc . returns ( ( context ) => context . done ( ) ) ;
232
- loader . getInfo . returns ( {
233
- name : 'test' ,
234
- outputBindings : { }
235
- } )
297
+ it ( "should apply hook before user function is executed" , ( ) => {
298
+ channel . registerBeforeInvocationRequest ( ( context , userFunction ) => {
299
+ context [ 'magic_flag' ] = 'magic value' ;
300
+ return userFunction . bind ( { __wrapped : true } ) ;
301
+ } ) ;
302
+
303
+ channel . registerBeforeInvocationRequest ( ( context , userFunction ) => {
304
+ context [ "secondary_flag" ] = 'magic value' ;
305
+ return userFunction ;
306
+ } ) ;
307
+
308
+ loader . getFunc . returns ( function ( this : any , context ) {
309
+ expect ( context [ 'magic_flag' ] ) . to . equal ( 'magic value' ) ;
310
+ expect ( context [ 'secondary_flag' ] ) . to . equal ( 'magic value' ) ;
311
+ expect ( this . __wrapped ) . to . equal ( true ) ;
312
+ expect ( channel [ '_invocationRequestBefore' ] . length ) . to . equal ( 2 ) ;
313
+ expect ( channel [ '_invocationRequestAfter' ] . length ) . to . equal ( 0 ) ;
314
+ context . done ( ) ;
315
+ } ) ;
316
+ loader . getInfo . returns ( {
317
+ name : 'test' ,
318
+ outputBindings : { }
319
+ } ) ;
320
+
321
+ const [ inputDataValue , actualInvocationRequest ] = runInvokedFunction ( ) ;
322
+ assertInvokedFunction ( inputDataValue , actualInvocationRequest ) ;
323
+ } ) ;
236
324
237
- var triggerDataMock : { [ k : string ] : rpc . ITypedData } = {
238
- "Headers" : {
239
- json : JSON . stringify ( { Connection : 'Keep-Alive' } )
240
- } ,
241
- "Sys" : {
242
- json : JSON . stringify ( { MethodName : 'test-js' , UtcNow : '2018' , RandGuid : '3212' } )
243
- }
244
- } ;
325
+ it ( 'should apply hook after user function is executed (callback)' , ( done ) => {
326
+ let finished = false ;
327
+ let count = 0 ;
328
+ channel . registerAfterInvocationRequest ( ( context ) => {
329
+ expect ( finished ) . to . equal ( true ) ;
330
+ count += 1 ;
331
+ } ) ;
245
332
246
- var inputDataValue = {
247
- name : "req" ,
248
- data : {
249
- data : "http" ,
250
- http :
251
- {
252
- body :
253
- {
254
- data : "string" ,
255
- body : "blahh"
256
- } ,
257
- rawBody :
258
- {
259
- data : "string" ,
260
- body : "blahh"
261
- }
262
- }
263
- }
264
- } ;
333
+ loader . getFunc . returns ( function ( this : any , context ) {
334
+ finished = true ;
335
+ expect ( channel [ '_invocationRequestBefore' ] . length ) . to . equal ( 0 ) ;
336
+ expect ( channel [ '_invocationRequestAfter' ] . length ) . to . equal ( 1 ) ;
337
+ expect ( count ) . to . equal ( 0 ) ;
338
+ context . done ( ) ;
339
+ expect ( count ) . to . equal ( 1 ) ;
340
+ done ( ) ;
341
+ } ) ;
342
+ loader . getInfo . returns ( {
343
+ name : 'test' ,
344
+ outputBindings : { }
345
+ } ) ;
265
346
266
- var actualInvocationRequest : rpc . IInvocationRequest = < rpc . IInvocationRequest > {
267
- functionId : 'id' ,
268
- invocationId : '1' ,
269
- inputData : [ inputDataValue ] ,
270
- triggerMetadata : triggerDataMock ,
271
- } ;
347
+ const [ inputDataValue , actualInvocationRequest ] = runInvokedFunction ( ) ;
348
+ assertInvokedFunction ( inputDataValue , actualInvocationRequest ) ;
349
+ } ) ;
350
+
351
+ it ( 'should apply hook after user function resolves (promise)' , ( done ) => {
352
+ let finished = false ;
353
+ let count = 0 ;
354
+ let inputDataValue , actualInvocationRequest ;
355
+ channel . registerAfterInvocationRequest ( ( context ) => {
356
+ expect ( finished ) . to . equal ( true ) ;
357
+ count += 1 ;
358
+ expect ( count ) . to . equal ( 1 ) ;
359
+ assertInvokedFunction ( inputDataValue , actualInvocationRequest ) ;
360
+ done ( ) ;
361
+ } ) ;
272
362
273
- stream . addTestMessage ( {
274
- invocationRequest : actualInvocationRequest
363
+ loader . getFunc . returns ( ( ) => new Promise ( ( resolve ) => {
364
+ finished = true ;
365
+ expect ( channel [ '_invocationRequestBefore' ] . length ) . to . equal ( 0 ) ;
366
+ expect ( channel [ '_invocationRequestAfter' ] . length ) . to . equal ( 1 ) ;
367
+ expect ( count ) . to . equal ( 0 ) ;
368
+ resolve ( ) ;
369
+ } ) ) ;
370
+ loader . getInfo . returns ( {
371
+ name : 'test' ,
372
+ outputBindings : { }
373
+ } ) ;
374
+
375
+ [ inputDataValue , actualInvocationRequest ] = runInvokedFunction ( ) ;
275
376
} ) ;
377
+
378
+
379
+ it ( 'should apply hook after user function rejects (promise)' , ( done ) => {
380
+ let finished = false ;
381
+ let count = 0 ;
382
+ channel . registerAfterInvocationRequest ( ( context ) => {
383
+ expect ( finished ) . to . equal ( true ) ;
384
+ count += 1 ;
385
+ expect ( count ) . to . equal ( 1 ) ;
386
+ assertInvokedFunction ( inputDataValue , actualInvocationRequest ) ;
387
+ done ( ) ;
388
+ } ) ;
276
389
277
- sinon . assert . calledWithMatch ( stream . written , < rpc . IStreamingMessage > {
278
- invocationResponse : {
279
- invocationId : '1' ,
280
- result : {
281
- status : rpc . StatusResult . Status . Success
282
- } ,
283
- outputData : [ ]
284
- }
390
+ loader . getFunc . returns ( ( context ) => new Promise ( ( _ , reject ) => {
391
+ finished = true ;
392
+ expect ( channel [ '_invocationRequestBefore' ] . length ) . to . equal ( 0 ) ;
393
+ expect ( channel [ '_invocationRequestAfter' ] . length ) . to . equal ( 1 ) ;
394
+ expect ( count ) . to . equal ( 0 ) ;
395
+ reject ( ) ;
396
+ } ) ) ;
397
+ loader . getInfo . returns ( {
398
+ name : 'test' ,
399
+ outputBindings : { }
400
+ } ) ;
401
+
402
+ const [ inputDataValue , actualInvocationRequest ] = runInvokedFunction ( ) ;
285
403
} ) ;
404
+ } ) ;
286
405
287
- // triggerMedata will be augmented with inpuDataValue since "RpcHttpTriggerMetadataRemoved" capability is set to true and therefore not populated by the host.
288
- expect ( JSON . stringify ( actualInvocationRequest . triggerMetadata ! . $request ) ) . to . equal ( JSON . stringify ( inputDataValue . data ) ) ;
289
- expect ( JSON . stringify ( actualInvocationRequest . triggerMetadata ! . req ) ) . to . equal ( JSON . stringify ( inputDataValue . data ) ) ;
406
+ it ( 'invokes function' , ( ) => {
407
+ loader . getFunc . returns ( ( context ) => context . done ( ) ) ;
408
+ loader . getInfo . returns ( {
409
+ name : 'test' ,
410
+ outputBindings : { }
411
+ } )
412
+
413
+ const [ inputDataValue , actualInvocationRequest ] = runInvokedFunction ( ) ;
414
+ assertInvokedFunction ( inputDataValue , actualInvocationRequest ) ;
290
415
} ) ;
291
416
292
417
it ( 'throws for malformed messages' , ( ) => {
0 commit comments