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

Side by Side Diff: common/prpc/client_test.go

Issue 1637193002: common/prpc, tools/cmd/cproto: prpc client (Closed) Base URL: https://github.com/luci/luci-go@prpc-server
Patch Set: Add Context-derived timeout for Client. Created 4 years, 10 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package prpc
6
7 import (
8 "fmt"
9 "io/ioutil"
10 "net/http"
11 "net/http/httptest"
12 "strconv"
13 "strings"
14 "testing"
15 "time"
16
17 "github.com/golang/protobuf/proto"
18 "golang.org/x/net/context"
19 "google.golang.org/grpc"
20 "google.golang.org/grpc/codes"
21
22 "github.com/luci/luci-go/common/clock"
23 "github.com/luci/luci-go/common/clock/testclock"
24 "github.com/luci/luci-go/common/logging"
25 "github.com/luci/luci-go/common/logging/memlogger"
26 "github.com/luci/luci-go/common/retry"
27
28 . "github.com/smartystreets/goconvey/convey"
29 )
30
31 func sayHello(c C) http.HandlerFunc {
32 return func(w http.ResponseWriter, r *http.Request) {
33 c.So(r.Method, ShouldEqual, "POST")
34 c.So(r.URL.Path, ShouldEqual, "/prpc/prpc.Greeter/SayHello")
35 c.So(r.Header.Get("Accept"), ShouldEqual, "application/prpc")
36 c.So(r.Header.Get("Content-Type"), ShouldEqual, "application/prp c")
37 c.So(r.Header.Get("User-Agent"), ShouldEqual, "prpc-test")
38
39 if timeout := r.Header.Get(HeaderTimeout); timeout != "" {
40 c.So(timeout, ShouldEqual, "10000000u")
41 }
42
43 reqBody, err := ioutil.ReadAll(r.Body)
44 c.So(err, ShouldBeNil)
45
46 var req HelloRequest
47 err = proto.Unmarshal(reqBody, &req)
48 c.So(err, ShouldBeNil)
49
50 res := HelloReply{"Hello " + req.Name}
51 buf, err := proto.Marshal(&res)
52 c.So(err, ShouldBeNil)
53
54 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.OK)))
55 _, err = w.Write(buf)
56 c.So(err, ShouldBeNil)
57 }
58 }
59
60 func doPanicHandler(w http.ResponseWriter, r *http.Request) {
61 panic("test panic")
62 }
63
64 func transientErrors(count int, then http.Handler) http.HandlerFunc {
65 return func(w http.ResponseWriter, r *http.Request) {
66 if count > 0 {
67 count--
68 w.Header().Set(HeaderGRPCCode, strconv.Itoa(int(codes.In ternal)))
69 w.WriteHeader(http.StatusInternalServerError)
70 fmt.Fprintln(w, "Server misbehaved")
71 return
72 }
73 then.ServeHTTP(w, r)
74 }
75 }
76
77 func shouldHaveMessagesLike(actual interface{}, expected ...interface{}) string {
78 log := actual.(*memlogger.MemLogger)
79 msgs := log.Messages()
80
81 So(msgs, ShouldHaveLength, len(expected))
82 for i, actual := range msgs {
83 expected := expected[i].(memlogger.LogEntry)
84 So(actual.Level, ShouldEqual, expected.Level)
85 So(actual.Msg, ShouldContainSubstring, expected.Msg)
86 }
87 return ""
88 }
89
90 func TestClient(t *testing.T) {
91 t.Parallel()
92
93 Convey("Client", t, func() {
94 setUp := func(h http.HandlerFunc) (*Client, *httptest.Server) {
95 server := httptest.NewServer(h)
96 client := &Client{
97 Host: strings.TrimPrefix(server.URL, "http://"),
98 Options: &Options{
99 Retry: func() retry.Iterator {
100 return &retry.Limited{
101 Retries: 3,
102 Delay: 0,
103 }
104 },
105 Insecure: true,
106 UserAgent: "prpc-test",
107 },
108 }
109 return client, server
110 }
111
112 ctx, _ := testclock.UseTime(context.Background(), testclock.Test TimeLocal)
113 ctx = memlogger.Use(ctx)
114 log := logging.Get(ctx).(*memlogger.MemLogger)
115 expectedCallLogEntry := func(c *Client) memlogger.LogEntry {
116 return memlogger.LogEntry{
117 Level: logging.Debug,
118 Msg: fmt.Sprintf("RPC %s/prpc.Greeter.SayHello ", c.Host),
119 }
120 }
121
122 req := &HelloRequest{"John"}
123 res := &HelloReply{}
124
125 Convey("Call", func() {
126 Convey("Works", func(c C) {
127 client, server := setUp(sayHello(c))
128 defer server.Close()
129
130 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
131 So(err, ShouldBeNil)
132 So(res.Message, ShouldEqual, "Hello John")
133
134 So(log, shouldHaveMessagesLike, expectedCallLogE ntry(client))
135 })
136
137 Convey("With a deadline <= now, does not execute.", func (c C) {
138 client, server := setUp(doPanicHandler)
139 defer server.Close()
140
141 ctx, _ = context.WithDeadline(ctx, clock.Now(ctx ))
142 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
143 So(err, ShouldEqual, context.DeadlineExceeded)
144 })
145
146 Convey("With a deadline in the future, sets the deadline header.", func(c C) {
147 client, server := setUp(sayHello(c))
148 defer server.Close()
149
150 ctx, _ = clock.WithDeadline(ctx, clock.Now(ctx). Add(10*time.Second))
151 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
152 So(err, ShouldBeNil)
153 So(res.Message, ShouldEqual, "Hello John")
154
155 So(log, shouldHaveMessagesLike, expectedCallLogE ntry(client))
156 })
157
158 Convey("HTTP 500 x2", func(c C) {
159 client, server := setUp(transientErrors(2, sayHe llo(c)))
160 defer server.Close()
161
162 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
163 So(err, ShouldBeNil)
164 So(res.Message, ShouldEqual, "Hello John")
165
166 So(log, shouldHaveMessagesLike,
167 expectedCallLogEntry(client),
168 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed transiently. Will retry in 0"},
169
170 expectedCallLogEntry(client),
171 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed transiently. Will retry in 0"},
172
173 expectedCallLogEntry(client),
174 )
175 })
176
177 Convey("HTTP 500 many", func(c C) {
178 client, server := setUp(transientErrors(10, sayH ello(c)))
179 defer server.Close()
180
181 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
182 So(grpc.Code(err), ShouldEqual, codes.Internal)
183 So(grpc.ErrorDesc(err), ShouldEqual, "Server mis behaved")
184
185 So(log, shouldHaveMessagesLike,
186 expectedCallLogEntry(client),
187 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed transiently. Will retry in 0"},
188
189 expectedCallLogEntry(client),
190 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed transiently. Will retry in 0"},
191
192 expectedCallLogEntry(client),
193 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed transiently. Will retry in 0"},
194
195 expectedCallLogEntry(client),
196 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed permanently"},
197 )
198 })
199
200 Convey("Forbidden", func(c C) {
201 client, server := setUp(func(w http.ResponseWrit er, r *http.Request) {
202 w.Header().Set(HeaderGRPCCode, strconv.I toa(int(codes.PermissionDenied)))
203 w.WriteHeader(http.StatusForbidden)
204 fmt.Fprintln(w, "Access denied")
205 })
206 defer server.Close()
207
208 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
209 So(grpc.Code(err), ShouldEqual, codes.Permission Denied)
210 So(grpc.ErrorDesc(err), ShouldEqual, "Access den ied")
211
212 So(log, shouldHaveMessagesLike,
213 expectedCallLogEntry(client),
214 memlogger.LogEntry{Level: logging.Warnin g, Msg: "RPC failed permanently"},
215 )
216 })
217
218 Convey(HeaderGRPCCode, func(c C) {
219 client, server := setUp(func(w http.ResponseWrit er, r *http.Request) {
220 w.Header().Set(HeaderGRPCCode, strconv.I toa(int(codes.Canceled)))
221 w.WriteHeader(http.StatusBadRequest)
222 })
223 defer server.Close()
224
225 err := client.Call(ctx, "prpc.Greeter", "SayHell o", req, res)
226 So(grpc.Code(err), ShouldEqual, codes.Canceled)
227 })
228 })
229 })
230 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698