1
2
3
4
5 package googleapi
6
7 import (
8 "encoding/json"
9 "io"
10 "net/http"
11 "net/url"
12 "reflect"
13 "strings"
14 "testing"
15 )
16
17 type ExpandTest struct {
18 in string
19 expansions map[string]string
20 want string
21 }
22
23 var expandTests = []ExpandTest{
24
25 {
26 "http://www.golang.org/",
27 map[string]string{},
28 "http://www.golang.org/",
29 },
30
31 {
32 "http://www.golang.org/{bucket}/delete",
33 map[string]string{
34 "bucket": "red",
35 },
36 "http://www.golang.org/red/delete",
37 },
38
39 {
40 "http://www.golang.org/{bucket}/delete",
41 map[string]string{
42 "bucket": "red/blue",
43 },
44 "http://www.golang.org/red%2Fblue/delete",
45 },
46
47 {
48 "http://www.golang.org/{bucket}/delete",
49 map[string]string{
50 "bucket": "red or blue",
51 },
52 "http://www.golang.org/red%20or%20blue/delete",
53 },
54
55 {
56 "http://www.golang.org/{object}/delete",
57 map[string]string{
58 "bucket": "red or blue",
59 },
60 "http://www.golang.org//delete",
61 },
62
63 {
64 "http://www.golang.org/{one}/{two}/{three}/get",
65 map[string]string{
66 "one": "ONE",
67 "two": "TWO",
68 "three": "THREE",
69 },
70 "http://www.golang.org/ONE/TWO/THREE/get",
71 },
72
73 {
74 "http://www.golang.org/{bucket}/get",
75 map[string]string{
76 "bucket": "£100",
77 },
78 "http://www.golang.org/%C2%A3100/get",
79 },
80
81 {
82 "http://www.golang.org/{bucket}/get",
83 map[string]string{
84 "bucket": `/\@:,.`,
85 },
86 "http://www.golang.org/%2F%5C%40%3A%2C./get",
87 },
88
89 {
90 "http://www.golang.org/{bucket/get",
91 map[string]string{
92 "bucket": "red",
93 },
94 "http://www.golang.org/%7Bbucket/get",
95 },
96
97
98 {
99 "http://www.golang.org/{+topic}",
100 map[string]string{
101 "topic": "/topics/myproject/mytopic",
102 },
103
104 "http://www.golang.org//topics/myproject/mytopic",
105 },
106 }
107
108 func TestExpand(t *testing.T) {
109 for i, test := range expandTests {
110 u := url.URL{
111 Path: test.in,
112 }
113 Expand(&u, test.expansions)
114 got := u.EscapedPath()
115 if got != test.want {
116 t.Errorf("got %q expected %q in test %d", got, test.want, i+1)
117 }
118 }
119 }
120
121 func TestResolveRelative(t *testing.T) {
122 resolveRelativeTests := []struct {
123 basestr string
124 relstr string
125 want string
126 wantPanic bool
127 }{
128 {
129 basestr: "http://www.golang.org/",
130 relstr: "topics/myproject/mytopic",
131 want: "http://www.golang.org/topics/myproject/mytopic",
132 },
133 {
134 basestr: "http://www.golang.org/",
135 relstr: "topics/{+myproject}/{release}:build:test:deploy",
136 want: "http://www.golang.org/topics/{+myproject}/{release}:build:test:deploy",
137 },
138 {
139 basestr: "https://www.googleapis.com/admin/reports/v1/",
140 relstr: "/admin/reports_v1/channels/stop",
141 want: "https://www.googleapis.com/admin/reports_v1/channels/stop",
142 },
143 {
144 basestr: "https://www.googleapis.com/admin/directory/v1/",
145 relstr: "customer/{customerId}/orgunits{/orgUnitPath*}",
146 want: "https://www.googleapis.com/admin/directory/v1/customer/{customerId}/orgunits{/orgUnitPath*}",
147 },
148 {
149 basestr: "https://www.googleapis.com/tagmanager/v2/",
150 relstr: "accounts",
151 want: "https://www.googleapis.com/tagmanager/v2/accounts",
152 },
153 {
154 basestr: "https://www.googleapis.com/tagmanager/v2/",
155 relstr: "{+parent}/workspaces",
156 want: "https://www.googleapis.com/tagmanager/v2/{+parent}/workspaces",
157 },
158 {
159 basestr: "https://www.googleapis.com/tagmanager/v2/",
160 relstr: "{+path}:create_version",
161 want: "https://www.googleapis.com/tagmanager/v2/{+path}:create_version",
162 },
163 {
164 basestr: "http://localhost",
165 relstr: ":8080foo",
166 wantPanic: true,
167 },
168 {
169 basestr: "https://www.googleapis.com/exampleapi/v2/somemethod",
170 relstr: "/upload/exampleapi/v2/somemethod",
171 want: "https://www.googleapis.com/upload/exampleapi/v2/somemethod",
172 },
173 {
174 basestr: "https://otherhost.googleapis.com/exampleapi/v2/somemethod",
175 relstr: "/upload/exampleapi/v2/alternatemethod",
176 want: "https://otherhost.googleapis.com/upload/exampleapi/v2/alternatemethod",
177 },
178 }
179
180 for _, test := range resolveRelativeTests {
181 t.Run(test.basestr+test.relstr, func(t *testing.T) {
182 if test.wantPanic {
183 defer func() {
184 if r := recover(); r == nil {
185 t.Errorf("expected panic, but did not see one")
186 }
187 }()
188 }
189
190 got := ResolveRelative(test.basestr, test.relstr)
191 if got != test.want {
192 t.Errorf("got %q expected %q", got, test.want)
193 }
194 })
195 }
196 }
197
198 type CheckResponseTest struct {
199 in *http.Response
200 bodyText string
201 want error
202 errText string
203 }
204
205 var checkResponseTests = []CheckResponseTest{
206 {
207 &http.Response{
208 StatusCode: http.StatusOK,
209 },
210 "",
211 nil,
212 "",
213 },
214 {
215 &http.Response{
216 StatusCode: http.StatusInternalServerError,
217 },
218 `{"error":{}}`,
219 &Error{
220 Code: http.StatusInternalServerError,
221 Body: `{"error":{}}`,
222 },
223 `googleapi: got HTTP response code 500 with body: {"error":{}}`,
224 },
225 {
226 &http.Response{
227 StatusCode: http.StatusNotFound,
228 },
229 `{"error":{"message":"Error message for StatusNotFound."}}`,
230 &Error{
231 Code: http.StatusNotFound,
232 Message: "Error message for StatusNotFound.",
233 Body: `{"error":{"message":"Error message for StatusNotFound."}}`,
234 },
235 "googleapi: Error 404: Error message for StatusNotFound.",
236 },
237 {
238 &http.Response{
239 StatusCode: http.StatusBadRequest,
240 },
241 `{"error":"invalid_token","error_description":"Invalid Value"}`,
242 &Error{
243 Code: http.StatusBadRequest,
244 Body: `{"error":"invalid_token","error_description":"Invalid Value"}`,
245 },
246 `googleapi: got HTTP response code 400 with body: {"error":"invalid_token","error_description":"Invalid Value"}`,
247 },
248 {
249 &http.Response{
250 StatusCode: http.StatusBadRequest,
251 },
252 `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
253 &Error{
254 Code: http.StatusBadRequest,
255 Errors: []ErrorItem{
256 {
257 Reason: "keyInvalid",
258 Message: "Bad Request",
259 },
260 },
261 Body: `{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}`,
262 Message: "Bad Request",
263 },
264 "googleapi: Error 400: Bad Request, keyInvalid",
265 },
266 {
267 &http.Response{
268 StatusCode: http.StatusBadRequest,
269 },
270 `{"error": {"code": 400,"message": "The request has errors","status": "INVALID_ARGUMENT","details": [{"@type": "type.googleapis.com/google.rpc.BadRequest","fieldViolations": [{"field": "metadata.name","description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -"}]}]}}`,
271 &Error{
272 Code: http.StatusBadRequest,
273 Message: "The request has errors",
274 Details: []interface{}{
275 map[string]interface{}{
276 "@type": "type.googleapis.com/google.rpc.BadRequest",
277 "fieldViolations": []interface{}{
278 map[string]interface{}{
279 "field": "metadata.name",
280 "description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -",
281 },
282 },
283 },
284 },
285 Body: `{"error": {"code": 400,"message": "The request has errors","status": "INVALID_ARGUMENT","details": [{"@type": "type.googleapis.com/google.rpc.BadRequest","fieldViolations": [{"field": "metadata.name","description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -"}]}]}}`,
286 },
287 `googleapi: Error 400: The request has errors
288 Details:
289 [
290 {
291 "@type": "type.googleapis.com/google.rpc.BadRequest",
292 "fieldViolations": [
293 {
294 "description": "The revision name must be prefixed by the name of the enclosing Service or Configuration with a trailing -",
295 "field": "metadata.name"
296 }
297 ]
298 }
299 ]`,
300 },
301 {
302
303 &http.Response{
304 StatusCode: http.StatusInternalServerError,
305 Header: map[string][]string{"key1": {"value1"}, "key2": {"value2", "value3"}},
306 },
307 `{"error":{}}`,
308 &Error{
309 Code: http.StatusInternalServerError,
310 Body: `{"error":{}}`,
311 Header: map[string][]string{"key1": {"value1"}, "key2": {"value2", "value3"}},
312 },
313 `googleapi: got HTTP response code 500 with body: {"error":{}}`,
314 },
315 }
316
317 func TestCheckResponse(t *testing.T) {
318 for _, test := range checkResponseTests {
319 res := test.in
320 if test.bodyText != "" {
321 res.Body = io.NopCloser(strings.NewReader(test.bodyText))
322 }
323 g := CheckResponse(res)
324 if !reflect.DeepEqual(g, test.want) {
325 t.Errorf("CheckResponse: got %v, want %v", g, test.want)
326 gotJSON, err := json.Marshal(g)
327 if err != nil {
328 t.Error(err)
329 }
330 wantJSON, err := json.Marshal(test.want)
331 if err != nil {
332 t.Error(err)
333 }
334 t.Errorf("json(got): %q\njson(want): %q", string(gotJSON), string(wantJSON))
335 }
336 if g != nil && g.Error() != test.errText {
337 t.Errorf("CheckResponse: unexpected error message.\nGot: %q\nwant: %q", g, test.errText)
338 }
339 }
340 }
341
342 type VariantPoint struct {
343 Type string
344 Coordinates []float64
345 }
346
347 type VariantTest struct {
348 in map[string]interface{}
349 result bool
350 want VariantPoint
351 }
352
353 var coords = []interface{}{1.0, 2.0}
354
355 var variantTests = []VariantTest{
356 {
357 in: map[string]interface{}{
358 "type": "Point",
359 "coordinates": coords,
360 },
361 result: true,
362 want: VariantPoint{
363 Type: "Point",
364 Coordinates: []float64{1.0, 2.0},
365 },
366 },
367 {
368 in: map[string]interface{}{
369 "type": "Point",
370 "bogus": coords,
371 },
372 result: true,
373 want: VariantPoint{
374 Type: "Point",
375 },
376 },
377 }
378
379 func TestVariantType(t *testing.T) {
380 for _, test := range variantTests {
381 if g := VariantType(test.in); g != test.want.Type {
382 t.Errorf("VariantType(%v): got %v, want %v", test.in, g, test.want.Type)
383 }
384 }
385 }
386
387 func TestConvertVariant(t *testing.T) {
388 for _, test := range variantTests {
389 g := VariantPoint{}
390 r := ConvertVariant(test.in, &g)
391 if r != test.result {
392 t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, r, test.result)
393 }
394 if !reflect.DeepEqual(g, test.want) {
395 t.Errorf("ConvertVariant(%v): got %v, want %v", test.in, g, test.want)
396 }
397 }
398 }
399
400 func TestRoundChunkSize(t *testing.T) {
401 type testCase struct {
402 in int
403 want int
404 }
405 for _, tc := range []testCase{
406 {0, 0},
407 {256*1024 - 1, 256 * 1024},
408 {256 * 1024, 256 * 1024},
409 {256*1024 + 1, 2 * 256 * 1024},
410 } {
411 mo := &MediaOptions{}
412 ChunkSize(tc.in).setOptions(mo)
413 if got := mo.ChunkSize; got != tc.want {
414 t.Errorf("rounding chunk size: got: %v; want %v", got, tc.want)
415 }
416 }
417 }
418
View as plain text