YoshiyukiKato / grpc-mock

A simple mock gRPC server on Node.js
MIT License
85 stars 24 forks source link

multiple responses for unary request #12

Closed dbainbri-ciena closed 5 years ago

dbainbri-ciena commented 5 years ago

What is the proper response to specify multiple unary responses based on input pattern?

{
    method: "myMethod",
    input: { param: "one" },
    output: { resp: "two" },
},
{
    method: "myMethod",
    input: { param: "three" },
    output: { resp: "four" },
}

seems to make the server crash as well as return unexpected input pattern

/usr/local/lib/node_modules/grpc-mock/node_modules/grpc/src/server.js:69
  call.startBatch(error_batch, function(){});
       ^

Error: startBatch failed
    at handleError (/usr/local/lib/node_modules/grpc-mock/node_modules/grpc/src/server.js:69:8)
    at sendUnaryData (/usr/local/lib/node_modules/grpc-mock/node_modules/grpc/src/server.js:595:9)
    at HandlerFactory.<anonymous> (/usr/local/lib/node_modules/grpc-mock/index.js:143:15)
    at Object.DeleteDevice (/usr/local/lib/node_modules/grpc-mock/node_modules/grpc-kit/index.js:53:28)
    at /usr/local/lib/node_modules/grpc-mock/node_modules/grpc/src/server.js:590:13
dbainbri-ciena commented 5 years ago

Below is a quick and dirty patch the seems to enable the multiple unary responses based on multiple matches. The basic logic is don't send an unexpected input pattern until all possible matches are checked. Followed the logic pattern for unary and other stream types but did not test with them. Not submitting as a pull request because i think it is a bit of a hack, but will let you decide that.

--- /tmp/grpc-mock/index.js 2019-05-01 14:26:19.000000000 -0700
+++ index.js.save   2019-05-02 19:24:43.000000000 -0700
@@ -68,6 +68,10 @@
   generateHandler() {
     let interactions = [];
     const handler = function (call, callback) {
+      var last = {
+          unexpected: true,
+          streamType: ''
+      };
       for (const { streamType, stream, input, output, error } of this.rules) {
         if (streamType === 'client') {
           call.on('data', function (memo, data) {
@@ -84,6 +88,7 @@
             const matched = included && memo.length === stream.length;

             if (matched) {
+              last.unexpected = false
               if (error) {
                 callback(prepareMetadata(error));
               } else {
@@ -92,13 +97,14 @@
             } else if(included) {
               //nothing todo
             } else {
-              callback(prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+              last.streamType = 'client';
             }

           }.bind(null, []));
         } else if (streamType === 'server') {
           interactions.push(call.request);
           if (isMatched(call.request, input)) {
+            last.unexpected = false;
             if (error) {
               call.emit('error', prepareMetadata(error));
             } else {
@@ -108,8 +114,7 @@
             }
             call.end();
           } else {
-            call.emit('error', prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
-            call.end();
+            last.streamType = 'server';
           }
         } else if (streamType === 'mutual') {
           call.on('data', function (stream, memo, data) {
@@ -122,13 +127,13 @@
               const { output } = stream.shift();
               call.write(output);
             } else if (isMatched(memo[0], stream[0].input)) {
+              last.unexpected = false;
               memo.shift();
               const { output } = stream.shift();
               call.write(output);
             } else {
               //TODO: raise error
-              call.emit('error', prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
-              call.end();
+              last.streamType = 'mutual';
             }

             if (stream.length === 0) {
@@ -139,16 +144,30 @@
           interactions.push(call.request);

           if (isMatched(call.request, input)) {
+            last.unexpected = false
             if (error) {
               callback(prepareMetadata(error));
             } else {
               callback(null, output);
             }
           } else {
-            callback(prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+            last.streamType = '';
           }
         }
       }
+      if (last.unexpected) {
+          if (last.streamType == 'client') {
+              callback(prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+          } else if (last.streamType == 'server') {
+              call.emit('error', prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+              call.end();
+          } else if (last.streamType == 'mutual') {
+              call.emit('error', prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+              call.end();
+          } else {
+              callback(prepareMetadata(UNEXPECTED_INPUT_PATTERN_ERROR));
+          }
+      }
     }.bind(this);
     handler.interactions = interactions;
     return handler;
marcoEgger commented 5 years ago

Can we expect this to be implemented/merged at some point?

YoshiyukiKato commented 5 years ago

I merged #14. Really sorry too late response.