diff --git a/Readme.md b/Readme.md index 3678a6d6..bbabbab4 100644 --- a/Readme.md +++ b/Readme.md @@ -77,6 +77,7 @@ Stub Format is JSON text format. It has a skeleton as follows: // put result fields here }, "error":"" // Optional. if you want to return error instead. + "code":"" // Optional. Grpc response code. if code !=0 return error instead. } } ``` diff --git a/example/simple/client/main.go b/example/simple/client/main.go index e9272946..f2338133 100644 --- a/example/simple/client/main.go +++ b/example/simple/client/main.go @@ -40,4 +40,16 @@ func main() { log.Fatalf("error from grpc: %v", err) } log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) + name = "error" + r, err = c.SayHello(context.Background(), &pb.Request{Name: name}) + if err == nil { + log.Fatalf("Expected error, but return %d", r.ReturnCode) + } + log.Printf("Greeting error: %s", err) + name = "error_code" + r, err = c.SayHello(context.Background(), &pb.Request{Name: name}) + if err == nil { + log.Fatalf("Expected error, but return %d", r.ReturnCode) + } + log.Printf("Greeting error: %s", err) } diff --git a/example/simple/stub/simple.json b/example/simple/stub/simple.json index 2438a3c9..67d957df 100644 --- a/example/simple/stub/simple.json +++ b/example/simple/stub/simple.json @@ -28,5 +28,30 @@ "return_code": 1 } } + }, + { + "service": "Gripmock", + "method": "SayHello", + "input": { + "equals": { + "name": "error" + } + }, + "output": { + "error": "test_error" + } + }, + { + "service": "Gripmock", + "method": "SayHello", + "input": { + "equals": { + "name": "error_code" + } + }, + "output": { + "error": "test_error_code", + "code": 3 + } } ] \ No newline at end of file diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index 13531f71..25377e9e 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -14,6 +14,8 @@ import ( "github.com/golang/protobuf/jsonpb" "golang.org/x/net/context" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/grpc/reflection" "google.golang.org/protobuf/runtime/protoiface" ) @@ -144,6 +146,7 @@ type payload struct { type response struct { Data interface{} `json:"data"` Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` } func findStub(service, method string, in, out protoiface.MessageV1) error { @@ -174,8 +177,14 @@ func findStub(service, method string, in, out protoiface.MessageV1) error { return fmt.Errorf("decoding json response %v",err) } - if respRPC.Error != "" { - return fmt.Errorf(respRPC.Error) + if respRPC.Error != "" || respRPC.Code != nil { + if respRPC.Code == nil { + abortedCode := codes.Aborted + respRPC.Code = &abortedCode + } + if *respRPC.Code != codes.OK { + return status.Error(*respRPC.Code, respRPC.Error) + } } data, _ := json.Marshal(respRPC.Data) diff --git a/stub/stub.go b/stub/stub.go index 819b317f..5509e7ea 100644 --- a/stub/stub.go +++ b/stub/stub.go @@ -3,11 +3,12 @@ package stub import ( "encoding/json" "fmt" + "google.golang.org/grpc/codes" "io/ioutil" "log" "net/http" "strings" - + "github.com/go-chi/chi" ) @@ -62,6 +63,7 @@ type Input struct { type Output struct { Data map[string]interface{} `json:"data"` Error string `json:"error"` + Code *codes.Code `json:"code,omitempty"` } func addStub(w http.ResponseWriter, r *http.Request) { @@ -106,7 +108,7 @@ func validateStub(stub *Stub) error { if stub.Method == "" { return fmt.Errorf("Method name can't be emtpy") } - + // due to golang implementation // method name must capital stub.Method = strings.Title(stub.Method) @@ -124,7 +126,7 @@ func validateStub(stub *Stub) error { // TODO: validate all input case - if stub.Output.Error == "" && stub.Output.Data == nil { + if stub.Output.Error == "" && stub.Output.Data == nil && stub.Output.Code == nil { return fmt.Errorf("Output can't be empty") } return nil @@ -143,11 +145,11 @@ func handleFindStub(w http.ResponseWriter, r *http.Request) { responseError(err, w) return } - + // due to golang implementation // method name must capital stub.Method = strings.Title(stub.Method) - + output, err := findStub(stub) if err != nil { log.Println(err) diff --git a/stub/stub_test.go b/stub/stub_test.go index 0f3b8e5c..4225abb3 100644 --- a/stub/stub_test.go +++ b/stub/stub_test.go @@ -166,6 +166,102 @@ func TestStub(t *testing.T) { handler: addStub, expect: `Success add stub`, }, + { + name: "add error stub with result code contains", + mock: func() *http.Request { + payload := `{ + "service": "ErrorStabWithCode", + "method":"TestMethod", + "input":{ + "contains":{ + "key": "value", + "greetings": { + "hola": "mundo", + "merhaba": "dunya" + }, + "cities": ["Istanbul", "Jakarta"] + } + }, + "output":{ + "error":"error msg", + "code": 3 + } + }` + return httptest.NewRequest("POST", "/add", bytes.NewReader([]byte(payload))) + }, + handler: addStub, + expect: `Success add stub`, + }, + { + name: "find error stub with result code contains", + mock: func() *http.Request { + payload := `{ + "service": "ErrorStabWithCode", + "method":"TestMethod", + "data":{ + "key": "value", + "anotherKey": "anotherValue", + "greetings": { + "hola": "mundo", + "merhaba": "dunya", + "hello": "world" + }, + "cities": ["Istanbul", "Jakarta", "Winterfell"] + } + }` + return httptest.NewRequest("GET", "/find", bytes.NewReader([]byte(payload))) + }, + handler: handleFindStub, + expect: "{\"data\":null,\"error\":\"error msg\",\"code\":3}\n", + }, + + { + name: "add error stub without result code contains", + mock: func() *http.Request { + payload := `{ + "service": "ErrorStab", + "method":"TestMethod", + "input":{ + "contains":{ + "key": "value", + "greetings": { + "hola": "mundo", + "merhaba": "dunya" + }, + "cities": ["Istanbul", "Jakarta"] + } + }, + "output":{ + "error":"error msg" + } + }` + return httptest.NewRequest("POST", "/add", bytes.NewReader([]byte(payload))) + }, + handler: addStub, + expect: `Success add stub`, + }, + { + name: "find error stub without result code contains", + mock: func() *http.Request { + payload := `{ + "service": "ErrorStab", + "method":"TestMethod", + "data":{ + "key": "value", + "anotherKey": "anotherValue", + "greetings": { + "hola": "mundo", + "merhaba": "dunya", + "hello": "world" + }, + "cities": ["Istanbul", "Jakarta", "Winterfell"] + } + }` + return httptest.NewRequest("GET", "/find", bytes.NewReader([]byte(payload))) + }, + handler: handleFindStub, + expect: "{\"data\":null,\"error\":\"error msg\"}\n", + }, { name: "find nested stub contains", mock: func() *http.Request {