Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Unified Diff: common/prpc/client_test.go

Issue 1605363002: common/prpc, tools/cmd/cproto: prpc client (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: rebased and addressed comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « common/prpc/client.go ('k') | common/prpc/codes.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: common/prpc/client_test.go
diff --git a/common/prpc/client_test.go b/common/prpc/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4f8e0b912434a6b9e6b47473ad3b58f501a1c7d9
--- /dev/null
+++ b/common/prpc/client_test.go
@@ -0,0 +1,200 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package prpc
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/golang/protobuf/proto"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+
+ "github.com/luci/luci-go/common/logging"
+ "github.com/luci/luci-go/common/logging/memlogger"
+ "github.com/luci/luci-go/common/retry"
+
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func sayHello(c C) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ c.So(r.Method, ShouldEqual, "POST")
+ c.So(r.URL.Path, ShouldEqual, "/prpc/prpc.Greeter/SayHello")
+ c.So(r.Header.Get("Accept"), ShouldEqual, "application/prpc")
+ c.So(r.Header.Get("Content-Type"), ShouldEqual, "application/prpc")
+ c.So(r.Header.Get("User-Agent"), ShouldEqual, "prpc-test")
+
+ reqBody, err := ioutil.ReadAll(r.Body)
+ c.So(err, ShouldBeNil)
+
+ var req HelloRequest
+ err = proto.Unmarshal(reqBody, &req)
+ c.So(err, ShouldBeNil)
+
+ res := HelloReply{"Hello " + req.Name}
+ buf, err := proto.Marshal(&res)
+ c.So(err, ShouldBeNil)
+
+ w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.OK)))
+ _, err = w.Write(buf)
+ c.So(err, ShouldBeNil)
+ }
+}
+
+func transientErrors(count int, then http.Handler) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if count > 0 {
+ count--
+ w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.Internal)))
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprintln(w, "Server misbehaved")
+ return
+ }
+ then.ServeHTTP(w, r)
+ }
+}
+
+func shouldHaveMessagesLike(actual interface{}, expected ...interface{}) string {
+ log := actual.(*memlogger.MemLogger)
+ msgs := log.Messages()
+
+ So(msgs, ShouldHaveLength, len(expected))
+ for i, actual := range msgs {
+ expected := expected[i].(memlogger.LogEntry)
+ So(actual.Level, ShouldEqual, expected.Level)
+ So(actual.Msg, ShouldContainSubstring, expected.Msg)
+ }
+ return ""
+}
+
+func TestClient(t *testing.T) {
+ t.Parallel()
+
+ Convey("Client", t, func() {
+ setUp := func(h http.HandlerFunc) (*Client, *httptest.Server) {
+ server := httptest.NewServer(h)
+ client := &Client{
+ Host: strings.TrimPrefix(server.URL, "http://"),
+ Options: &Options{
+ Retry: func() retry.Iterator {
+ return &retry.ExponentialBackoff{
+ Limited: retry.Limited{
+ Retries: 3,
+ Delay: time.Millisecond,
+ },
+ }
+ },
+ Insecure: true,
+ UserAgent: "prpc-test",
+ },
+ }
+ return client, server
+ }
+
+ ctx := memlogger.Use(context.Background())
+ log := logging.Get(ctx).(*memlogger.MemLogger)
+ expectedCallLogEntry := func(c *Client) memlogger.LogEntry {
+ return memlogger.LogEntry{
+ Level: logging.Debug,
+ Msg: fmt.Sprintf("RPC %s/prpc.Greeter.SayHello", c.Host),
+ }
+ }
+
+ req := &HelloRequest{"John"}
+ res := &HelloReply{}
+
+ Convey("Call", func() {
+ Convey("Works", func(c C) {
+ client, server := setUp(sayHello(c))
+ defer server.Close()
+
+ err := client.Call(ctx, "prpc.Greeter", "SayHello", req, res)
+ So(err, ShouldBeNil)
+ So(res.Message, ShouldEqual, "Hello John")
+
+ So(log, shouldHaveMessagesLike, expectedCallLogEntry(client))
+ })
+
+ Convey("HTTP 500 x2", func(c C) {
+ client, server := setUp(transientErrors(2, sayHello(c)))
+ defer server.Close()
+
+ err := client.Call(ctx, "prpc.Greeter", "SayHello", req, res)
+ So(err, ShouldBeNil)
+ So(res.Message, ShouldEqual, "Hello John")
+
+ So(log, shouldHaveMessagesLike,
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed transiently. Will retry in 1ms"},
+
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed transiently. Will retry in 2ms"},
+
+ expectedCallLogEntry(client),
+ )
+ })
+
+ Convey("HTTP 500 many", func(c C) {
+ client, server := setUp(transientErrors(10, sayHello(c)))
+ defer server.Close()
+
+ err := client.Call(ctx, "prpc.Greeter", "SayHello", req, res)
+ So(grpc.Code(err), ShouldEqual, codes.Internal)
+ So(grpc.ErrorDesc(err), ShouldEqual, "Server misbehaved")
+
+ So(log, shouldHaveMessagesLike,
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed transiently. Will retry in 1ms"},
+
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed transiently. Will retry in 2ms"},
+
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed transiently. Will retry in 4ms"},
+
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed permanently"},
+ )
+ })
+
+ Convey("Forbidden", func(c C) {
+ client, server := setUp(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.PermissionDenied)))
+ w.WriteHeader(http.StatusForbidden)
+ fmt.Fprintln(w, "Access denied")
+ })
+ defer server.Close()
+
+ err := client.Call(ctx, "prpc.Greeter", "SayHello", req, res)
+ So(grpc.Code(err), ShouldEqual, codes.PermissionDenied)
+ So(grpc.ErrorDesc(err), ShouldEqual, "Access denied")
+
+ So(log, shouldHaveMessagesLike,
+ expectedCallLogEntry(client),
+ memlogger.LogEntry{Level: logging.Warning, Msg: "RPC failed permanently"},
+ )
+ })
+
+ Convey(HeaderGRPCCode, func(c C) {
+ client, server := setUp(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.Canceled)))
+ w.WriteHeader(http.StatusBadRequest)
+ })
+ defer server.Close()
+
+ err := client.Call(ctx, "prpc.Greeter", "SayHello", req, res)
+ So(grpc.Code(err), ShouldEqual, codes.Canceled)
+ })
+ })
+ })
+}
« no previous file with comments | « common/prpc/client.go ('k') | common/prpc/codes.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698