1// Copyright 2017 Google LLC. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package main 6 7import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "io/ioutil" 12 "math" 13 "net/http" 14 "net/http/httptest" 15 "net/url" 16 "reflect" 17 "strings" 18 "testing" 19 "testing/iotest" 20 21 // If you add a client, add a matching go:generate line below. 22 mon "google.golang.org/api/monitoring/v3" 23 storage "google.golang.org/api/storage/v1" 24) 25 26//go:generate -command api go run gen.go docurls.go replacements.go -install -api 27 28//go:generate api monitoring:v3 29//go:generate api storage:v1 30 31type myHandler struct { 32 location string 33 r *http.Request 34 body []byte 35 reqURIs []string 36 err error 37} 38 39func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 40 h.r = r 41 v, err := url.ParseRequestURI(r.URL.RequestURI()) 42 if err != nil { 43 h.err = err 44 return 45 } 46 h.reqURIs = append(h.reqURIs, v.String()) 47 if h.location != "" { 48 w.Header().Set("Location", h.location) 49 } 50 h.body, h.err = ioutil.ReadAll(r.Body) 51 fmt.Fprintf(w, "{}") 52} 53 54func TestMedia(t *testing.T) { 55 handler := &myHandler{} 56 server := httptest.NewServer(handler) 57 defer server.Close() 58 59 client := &http.Client{} 60 s, err := storage.New(client) 61 if err != nil { 62 t.Fatalf("unable to create service: %v", err) 63 } 64 s.BasePath = fmt.Sprintf("%s%s", server.URL, "/storage/v1/") 65 66 const body = "fake media data" 67 f := strings.NewReader(body) 68 o := &storage.Object{ 69 Bucket: "mybucket", 70 Name: "filename", 71 ContentType: "plain/text", 72 ContentEncoding: "utf-8", 73 ContentLanguage: "en", 74 } 75 _, err = s.Objects.Insert("mybucket", o).Media(f).Do() 76 if err != nil { 77 t.Fatalf("unable to insert object: %v", err) 78 } 79 g := handler.r 80 if w := "POST"; g.Method != w { 81 t.Errorf("Method = %q; want %q", g.Method, w) 82 } 83 if w := "HTTP/1.1"; g.Proto != w { 84 t.Errorf("Proto = %q; want %q", g.Proto, w) 85 } 86 if w := 1; g.ProtoMajor != w { 87 t.Errorf("ProtoMajor = %v; want %v", g.ProtoMajor, w) 88 } 89 if w := 1; g.ProtoMinor != w { 90 t.Errorf("ProtoMinor = %v; want %v", g.ProtoMinor, w) 91 } 92 if w, k := "google-api-go-client/0.5", "User-Agent"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 93 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 94 } 95 if w, k := "multipart/related; boundary=", "Content-Type"; len(g.Header[k]) != 1 || !strings.HasPrefix(g.Header[k][0], w) { 96 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 97 } 98 if w, k := "gzip", "Accept-Encoding"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 99 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 100 } 101 if w := int64(-1); g.ContentLength != w { 102 t.Errorf("ContentLength = %v; want %v", g.ContentLength, w) 103 } 104 if w := "chunked"; len(g.TransferEncoding) != 1 || g.TransferEncoding[0] != w { 105 t.Errorf("TransferEncoding = %#v; want %q", g.TransferEncoding, w) 106 } 107 if w := server.Listener.Addr().String(); g.Host != w { 108 t.Errorf("Host = %q; want %q", g.Host, w) 109 } 110 if g.Form != nil { 111 t.Errorf("Form = %#v; want nil", g.Form) 112 } 113 if g.PostForm != nil { 114 t.Errorf("PostForm = %#v; want nil", g.PostForm) 115 } 116 if g.MultipartForm != nil { 117 t.Errorf("MultipartForm = %#v; want nil", g.MultipartForm) 118 } 119 if w := "/upload/storage/v1/b/mybucket/o?alt=json&prettyPrint=false&uploadType=multipart"; g.RequestURI != w { 120 t.Errorf("RequestURI = %q; want %q", g.RequestURI, w) 121 } 122 if w := "\r\n\r\n" + body + "\r\n"; !strings.Contains(string(handler.body), w) { 123 t.Errorf("Body = %q, want substring %q", handler.body, w) 124 } 125 if handler.err != nil { 126 t.Errorf("handler err = %v, want nil", handler.err) 127 } 128} 129 130func TestResumableMedia(t *testing.T) { 131 handler := &myHandler{} 132 server := httptest.NewServer(handler) 133 defer server.Close() 134 135 handler.location = server.URL 136 client := &http.Client{} 137 s, err := storage.New(client) 138 if err != nil { 139 t.Fatalf("unable to create service: %v", err) 140 } 141 s.BasePath = server.URL 142 143 const data = "fake resumable media data" 144 mediaSize := len(data) 145 f := strings.NewReader(data) 146 o := &storage.Object{ 147 Bucket: "mybucket", 148 Name: "filename", 149 ContentType: "plain/text", 150 ContentEncoding: "utf-8", 151 ContentLanguage: "en", 152 } 153 _, err = s.Objects.Insert("mybucket", o).Name("filename").ResumableMedia(context.Background(), f, int64(len(data)), "text/plain").Do() 154 if err != nil { 155 t.Fatalf("unable to insert object: %v", err) 156 } 157 g := handler.r 158 if w := "POST"; g.Method != w { 159 t.Errorf("Method = %q; want %q", g.Method, w) 160 } 161 if w := "HTTP/1.1"; g.Proto != w { 162 t.Errorf("Proto = %q; want %q", g.Proto, w) 163 } 164 if w := 1; g.ProtoMajor != w { 165 t.Errorf("ProtoMajor = %v; want %v", g.ProtoMajor, w) 166 } 167 if w := 1; g.ProtoMinor != w { 168 t.Errorf("ProtoMinor = %v; want %v", g.ProtoMinor, w) 169 } 170 if w, k := "google-api-go-client/0.5", "User-Agent"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 171 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 172 } 173 if want, got := []string{"text/plain"}, g.Header["Content-Type"]; !reflect.DeepEqual(got, want) { 174 t.Errorf("header Content-Type got: %#v; want: %#v", got, want) 175 } 176 if w, k := "gzip", "Accept-Encoding"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 177 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 178 } 179 if w := int64(mediaSize); g.ContentLength != w { 180 t.Errorf("ContentLength = %v; want %v", g.ContentLength, w) 181 } 182 if len(g.TransferEncoding) != 0 { 183 t.Errorf("TransferEncoding = %#v; want nil", g.TransferEncoding) 184 } 185 if g.Form != nil { 186 t.Errorf("Form = %#v; want nil", g.Form) 187 } 188 if g.PostForm != nil { 189 t.Errorf("PostForm = %#v; want nil", g.PostForm) 190 } 191 if g.MultipartForm != nil { 192 t.Errorf("MultipartForm = %#v; want nil", g.MultipartForm) 193 } 194 if handler.err != nil { 195 t.Errorf("handler err = %v, want nil", handler.err) 196 } 197} 198 199func TestNoMedia(t *testing.T) { 200 handler := &myHandler{} 201 server := httptest.NewServer(handler) 202 defer server.Close() 203 204 client := &http.Client{} 205 s, err := storage.New(client) 206 if err != nil { 207 t.Fatalf("unable to create service: %v", err) 208 } 209 s.BasePath = fmt.Sprintf("%s%s", server.URL, "/storage/v1/") 210 211 o := &storage.Object{ 212 Bucket: "mybucket", 213 Name: "filename", 214 ContentType: "plain/text", 215 ContentEncoding: "utf-8", 216 ContentLanguage: "en", 217 } 218 _, err = s.Objects.Insert("mybucket", o).Do() 219 if err != nil { 220 t.Fatalf("unable to insert object: %v", err) 221 } 222 g := handler.r 223 if w := "POST"; g.Method != w { 224 t.Errorf("Method = %q; want %q", g.Method, w) 225 } 226 if w := "HTTP/1.1"; g.Proto != w { 227 t.Errorf("Proto = %q; want %q", g.Proto, w) 228 } 229 if w := 1; g.ProtoMajor != w { 230 t.Errorf("ProtoMajor = %v; want %v", g.ProtoMajor, w) 231 } 232 if w := 1; g.ProtoMinor != w { 233 t.Errorf("ProtoMinor = %v; want %v", g.ProtoMinor, w) 234 } 235 if w, k := "google-api-go-client/0.5", "User-Agent"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 236 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 237 } 238 if w, k := "application/json", "Content-Type"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 239 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 240 } 241 if w, k := "gzip", "Accept-Encoding"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 242 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 243 } 244 if w := int64(116); g.ContentLength != w { 245 t.Errorf("ContentLength = %v; want %v", g.ContentLength, w) 246 } 247 if len(g.TransferEncoding) != 0 { 248 t.Errorf("TransferEncoding = %#v; want []string{}", g.TransferEncoding) 249 } 250 if w := server.Listener.Addr().String(); g.Host != w { 251 t.Errorf("Host = %q; want %q", g.Host, w) 252 } 253 if g.Form != nil { 254 t.Errorf("Form = %#v; want nil", g.Form) 255 } 256 if g.PostForm != nil { 257 t.Errorf("PostForm = %#v; want nil", g.PostForm) 258 } 259 if g.MultipartForm != nil { 260 t.Errorf("MultipartForm = %#v; want nil", g.MultipartForm) 261 } 262 if w := "/storage/v1/b/mybucket/o?alt=json&prettyPrint=false"; g.RequestURI != w { 263 t.Errorf("RequestURI = %q; want %q", g.RequestURI, w) 264 } 265 if w := `{"bucket":"mybucket","contentEncoding":"utf-8","contentLanguage":"en","contentType":"plain/text","name":"filename"}` + "\n"; string(handler.body) != w { 266 t.Errorf("Body = %q, want %q", handler.body, w) 267 } 268 if handler.err != nil { 269 t.Errorf("handler err = %v, want nil", handler.err) 270 } 271} 272 273func TestMediaErrHandling(t *testing.T) { 274 handler := &myHandler{} 275 server := httptest.NewServer(handler) 276 defer server.Close() 277 278 client := &http.Client{} 279 s, err := storage.New(client) 280 if err != nil { 281 t.Fatalf("unable to create service: %v", err) 282 } 283 s.BasePath = fmt.Sprintf("%s%s", server.URL, "/storage/v1/") 284 285 const body = "fake media data" 286 f := strings.NewReader(body) 287 // The combination of TimeoutReader and OneByteReader causes the first byte to 288 // be successfully delivered, but then a timeout error is reported. 289 r := iotest.TimeoutReader(iotest.OneByteReader(f)) 290 o := &storage.Object{ 291 Bucket: "mybucket", 292 Name: "filename", 293 ContentType: "plain/text", 294 ContentEncoding: "utf-8", 295 ContentLanguage: "en", 296 } 297 _, err = s.Objects.Insert("mybucket", o).Media(r).Do() 298 if err == nil || !strings.Contains(err.Error(), "timeout") { 299 t.Errorf("expected timeout error, got %v", err) 300 } 301 if handler.err != nil { 302 t.Errorf("handler err = %v, want nil", handler.err) 303 } 304} 305 306func TestUserAgent(t *testing.T) { 307 handler := &myHandler{} 308 server := httptest.NewServer(handler) 309 defer server.Close() 310 311 client := &http.Client{} 312 s, err := storage.New(client) 313 if err != nil { 314 t.Fatalf("unable to create service: %v", err) 315 } 316 s.BasePath = server.URL 317 s.UserAgent = "myagent/1.0" 318 319 f := strings.NewReader("fake media data") 320 o := &storage.Object{ 321 Bucket: "mybucket", 322 Name: "filename", 323 ContentType: "plain/text", 324 ContentEncoding: "utf-8", 325 ContentLanguage: "en", 326 } 327 _, err = s.Objects.Insert("mybucket", o).Media(f).Do() 328 if err != nil { 329 t.Fatalf("unable to insert object: %v", err) 330 } 331 g := handler.r 332 if w, k := "google-api-go-client/0.5 myagent/1.0", "User-Agent"; len(g.Header[k]) != 1 || g.Header[k][0] != w { 333 t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) 334 } 335} 336 337func myProgressUpdater(current, total int64) {} 338 339func TestParams(t *testing.T) { 340 handler := &myHandler{} 341 server := httptest.NewServer(handler) 342 defer server.Close() 343 344 handler.location = server.URL + "/uploadURL" 345 client := &http.Client{} 346 s, err := storage.New(client) 347 if err != nil { 348 t.Fatalf("unable to create service: %v", err) 349 } 350 s.BasePath = server.URL 351 s.UserAgent = "myagent/1.0" 352 353 const data = "fake media data" 354 f := strings.NewReader(data) 355 o := &storage.Object{ 356 Bucket: "mybucket", 357 Name: "filename", 358 ContentType: "plain/text", 359 ContentEncoding: "utf-8", 360 ContentLanguage: "en", 361 } 362 _, err = s.Objects.Insert("mybucket", o).Name(o.Name).IfGenerationMatch(42).ResumableMedia(context.Background(), f, int64(len(data)), "plain/text").ProgressUpdater(myProgressUpdater).Projection("full").Do() 363 if err != nil { 364 t.Fatalf("unable to insert object: %v", err) 365 } 366 if g, w := len(handler.reqURIs), 2; g != w { 367 t.Fatalf("len(reqURIs) = %v, want %v", g, w) 368 } 369 want := []string{ 370 "/upload/storage/v1/b/mybucket/o?alt=json&ifGenerationMatch=42&name=filename&prettyPrint=false&projection=full&uploadType=resumable", 371 "/uploadURL", 372 } 373 if !reflect.DeepEqual(handler.reqURIs, want) { 374 t.Errorf("reqURIs = %#v, want = %#v", handler.reqURIs, want) 375 } 376} 377 378// This test verifies that the unmarshal code generated for float64s 379// (in this case, the one inside mon.TypedValue) compiles and 380// behaves correctly. 381func TestUnmarshalSpecialFloats(t *testing.T) { 382 for _, test := range []struct { 383 in string 384 want float64 385 }{ 386 {`{"doubleValue": 3}`, 3}, 387 {`{"doubleValue": "Infinity"}`, math.Inf(1)}, 388 {`{"doubleValue": "-Infinity"}`, math.Inf(-1)}, 389 {`{"doubleValue": "NaN"}`, math.NaN()}, 390 } { 391 var got mon.TypedValue 392 if err := json.Unmarshal([]byte(test.in), &got); err != nil { 393 t.Fatal(err) 394 } 395 if !fleq(*got.DoubleValue, test.want) { 396 t.Errorf("got\n%+v\nwant\n%+v", *got.DoubleValue, test.want) 397 } 398 } 399} 400 401func fleq(f1, f2 float64) bool { 402 return f1 == f2 || (math.IsNaN(f1) && math.IsNaN(f2)) 403} 404