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

Side by Side Diff: go/src/infra/libs/epclient/epclient_test.go

Issue 1153473008: A client/server helper wrapper for endpoints in Go. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: rename to better package Created 5 years, 6 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 2015 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 epclient
6
7 import (
8 "bytes"
9 "fmt"
10 "golang.org/x/net/context"
11 "io/ioutil"
12 "net/http"
13 "net/http/httptest"
14 "testing"
15 "time"
16
17 . "github.com/smartystreets/goconvey/convey"
18
19 "infra/gae/libs/endpoints"
20 )
21
22 func init() {
23 backoffSlot = time.Millisecond // doesn't matter, but it should be short .
24 }
25
26 func TestBackoff(t *testing.T) {
27 t.Parallel()
28
29 cases := []struct {
30 name string
31 failures uint
32 mult time.Duration // multiplier for slot
33 }{
34 {"zero", 0, 1},
35 {"one", 1, 2},
36 {"five", 5, 2 * 2 * 2 * 2 * 2},
37 {"lots", 500, 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2},
38 }
39
40 Convey("Backoff", t, func() {
41 for _, tc := range cases {
42 Convey(tc.name, func() {
43 So(Backoff(tc.failures), ShouldResemble, tc.mult *backoffSlot)
44 })
45 }
46 })
47 }
48
49 func TestDoer(t *testing.T) {
50 t.Parallel()
51
52 Convey("Test mkHttpDoer", t, func() {
53 serv := httptest.NewServer(http.HandlerFunc(func(rsp http.Respon seWriter, r *http.Request) {
54 fmt.Fprintf(rsp, "sup fool: %s", r.Method)
55 }))
56 defer serv.Close()
57
58 doer, closer := mkHttpDoer()
59 defer closer()
60
61 req, err := http.NewRequest("GET", serv.URL, nil)
62 So(err, ShouldBeNil)
63
64 rsp, err := doer(req)
65 So(err, ShouldBeNil)
66 defer rsp.Body.Close()
67
68 data, err := ioutil.ReadAll(rsp.Body)
69 So(err, ShouldBeNil)
70 So(data, ShouldResemble, []byte("sup fool: GET"))
71 })
72 }
73
74 type FakeService struct{}
75
76 var FakeServiceMethodInfoMap = endpoints.MethodInfoMap{
77 "Get": {HTTPMethod: "GET"},
78 }
79
80 type CurResp struct{ Amt int }
81
82 type AddReq struct {
83 CounterName string `endpoints:"required"`
84 Amt int
85 }
86
87 type GetReq struct {
88 CounterName string `endpoints:"required"`
89 Limit int
90 Flavor string
91 }
92
93 func (FakeService) Add(*http.Request, *AddReq) (*CurResp, error) { return nil, n il }
94 func (FakeService) Get(*http.Request, *GetReq) (*CurResp, error) { return nil, n il }
95
96 type fakeBody struct{ *bytes.Buffer }
97
98 func (fakeBody) Close() error { return nil }
99
100 type rsplet struct {
101 body string
102 statusCode int
103 }
104
105 func fakeDoer(retBody map[string][]interface{}) func() (func(*http.Request) (*ht tp.Response, error), func()) {
106 return func() (func(*http.Request) (*http.Response, error), func()) {
107 return func(req *http.Request) (*http.Response, error) {
108 lookup := fmt.Sprintf("%s %s", req.Method, req.URL)
109 vals := retBody[lookup]
110 val := interface{}(nil)
111 if len(vals) > 0 {
112 val = vals[0]
113 retBody[lookup] = vals[1:]
114 }
115 if f, ok := val.(func() interface{}); ok {
116 val = f()
117 }
118 switch x := val.(type) {
119 case string:
120 return &http.Response{
121 Body: fakeBody{bytes.NewBufferStri ng(x)},
122 StatusCode: 200,
123 }, nil
124 case rsplet:
125 return &http.Response{
126 Body: fakeBody{bytes.NewBufferStri ng(x.body)},
127 StatusCode: x.statusCode,
128 }, nil
129 case error:
130 return nil, x
131 case *http.Response:
132 return x, nil
133 default:
134 return &http.Response{
135 Body: fakeBody{bytes.NewBufferString(
136 fmt.Sprintf(`{"error": {"message ": "Not Found: %s"}}`, req.URL))},
137 StatusCode: 404,
138 }, nil
139 }
140 }, func() {}
141 }
142 }
143
144 func TestClient(t *testing.T) {
145 t.Parallel()
146
147 Convey("Test clients", t, func() {
148 srv, err := endpoints.Register(FakeService{}, nil, FakeServiceMe thodInfoMap)
149 So(err, ShouldBeNil)
150 ctx, cancel := context.WithCancel(context.Background())
151 c := NewClient(ctx, "", srv)
152 So(c, ShouldNotBeNil)
153
154 Convey("essentially works", func() {
155 sc, err := c.ForService("FakeService")
156 So(err, ShouldBeNil)
157
158 c.(*client).mkHttpDoer = fakeDoer(map[string][]interface {}{
159 "POST /_ah/api/fakeservice/v1/add/foocounter": {
160 `{"Amt": 18}`,
161 rsplet{``, 500},
162 `{"Amt": 19}`,
163 },
164 // TODO(riannucci): verify that the quotes on ca ts is correct
165 `GET /_ah/api/fakeservice/v1/get/derpcounter?Lim it=1&Flavor="cats"`: {
166 `{"Amt": 9001}`,
167 },
168 })
169
170 rsp := &CurResp{}
171 So(sc.DoWithRetries("Add", &AddReq{"foocounter", 19}, rs p, 1), ShouldBeNil)
172 So(rsp.Amt, ShouldEqual, 18)
173
174 So(sc.DoWithRetries("Get", &GetReq{"derpcounter", 1, "ca ts"}, rsp, 1), ShouldBeNil)
175 So(rsp.Amt, ShouldEqual, 9001)
176
177 // swallows the error and retries
178 So(sc.DoWithRetries("Add", &AddReq{"foocounter", 20}, rs p, 2), ShouldBeNil)
179 So(rsp.Amt, ShouldEqual, 19)
180 })
181
182 Convey("doesn't work for missing services", func() {
183 _, err := c.ForService("Wat")
184 So(err.Error(), ShouldContainSubstring, "no such service ")
185
186 err = c.DoWithRetries("Wat", "fleem", nil, nil, 1)
187 So(err.Error(), ShouldContainSubstring, "no such service ")
188 })
189
190 Convey("doesn't work for missing methods", func() {
191 sc, err := c.ForService("FakeService")
192 So(err, ShouldBeNil)
193
194 err = sc.DoWithRetries("fleem", nil, nil, 1)
195 So(err.Error(), ShouldContainSubstring, "no such method" )
196 })
197
198 Convey("detects bad types", func() {
199 sc, err := c.ForService("FakeService")
200 So(err, ShouldBeNil)
201
202 err = sc.DoWithRetries("Add", "", "", 1)
203 So(err.Error(), ShouldContainSubstring, "mismatch for in put")
204
205 err = sc.DoWithRetries("Add", &AddReq{}, "", 1)
206 So(err.Error(), ShouldContainSubstring, "mismatch for ou tput")
207 })
208
209 Convey("can cancel it", func() {
210 sc, err := c.ForService("FakeService")
211 So(err, ShouldBeNil)
212
213 api := "POST /_ah/api/fakeservice/v1/add/foocounter"
214 fakeData := map[string][]interface{}{
215 api: {
216 nil, // force 404
217 func() interface{} {
218 cancel()
219 return nil
220 },
221 nil,
222 nil,
223 },
224 }
225 c.(*client).mkHttpDoer = fakeDoer(fakeData)
226
227 rsp := &CurResp{}
228 // 10 retries, but we'll never hit it
229 err = sc.DoWithRetries("Add", &AddReq{"foocounter", 19}, rsp, 10)
230 So(err.Error(), ShouldContainSubstring, "Not Found")
231 So(len(fakeData[api]), ShouldEqual, 2) // should have tw o 'nil's left
232 })
233
234 })
235 }
OLDNEW
« go/src/infra/libs/epclient/epclient.go ('K') | « go/src/infra/libs/epclient/epclient.infra_testing ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698