1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package storage
16
17 import (
18 "context"
19 "errors"
20 "fmt"
21 "log"
22 "net/url"
23 "os"
24 "strconv"
25 "strings"
26 "testing"
27 "time"
28
29 "cloud.google.com/go/iam/apiv1/iampb"
30 "github.com/google/go-cmp/cmp"
31 "github.com/googleapis/gax-go/v2"
32 "github.com/googleapis/gax-go/v2/apierror"
33 "github.com/googleapis/gax-go/v2/callctx"
34 "google.golang.org/api/iterator"
35 "google.golang.org/grpc/codes"
36 )
37
38 var emulatorClients map[string]storageClient
39 var veneerClient *Client
40
41 func TestCreateBucketEmulated(t *testing.T) {
42 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
43 want := &BucketAttrs{
44 Name: bucket,
45 Logging: &BucketLogging{
46 LogBucket: bucket,
47 },
48 }
49 got, err := client.CreateBucket(context.Background(), project, want.Name, want, nil)
50 if err != nil {
51 t.Fatal(err)
52 }
53 want.Location = "US"
54 if diff := cmp.Diff(got.Name, want.Name); diff != "" {
55 t.Errorf("Name got(-),want(+):\n%s", diff)
56 }
57 if diff := cmp.Diff(got.Location, want.Location); diff != "" {
58 t.Errorf("Location got(-),want(+):\n%s", diff)
59 }
60 if diff := cmp.Diff(got.Logging.LogBucket, want.Logging.LogBucket); diff != "" {
61 t.Errorf("LogBucket got(-),want(+):\n%s", diff)
62 }
63 })
64 }
65
66 func TestDeleteBucketEmulated(t *testing.T) {
67 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
68 b := &BucketAttrs{
69 Name: bucket,
70 }
71
72 _, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
73 if err != nil {
74 t.Fatalf("client.CreateBucket: %v", err)
75 }
76
77 err = client.DeleteBucket(context.Background(), b.Name, nil)
78 if err != nil {
79 t.Fatalf("client.DeleteBucket: %v", err)
80 }
81 })
82 }
83
84 func TestGetBucketEmulated(t *testing.T) {
85 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
86 want := &BucketAttrs{
87 Name: bucket,
88 }
89
90 _, err := client.CreateBucket(context.Background(), project, want.Name, want, nil)
91 if err != nil {
92 t.Fatalf("client.CreateBucket: %v", err)
93 }
94 got, err := client.GetBucket(context.Background(), want.Name, &BucketConditions{MetagenerationMatch: 1})
95 if err != nil {
96 t.Fatal(err)
97 }
98 if diff := cmp.Diff(got.Name, want.Name); diff != "" {
99 t.Errorf("got(-),want(+):\n%s", diff)
100 }
101 })
102 }
103
104 func TestUpdateBucketEmulated(t *testing.T) {
105 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
106 bkt := &BucketAttrs{
107 Name: bucket,
108 }
109
110 _, err := client.CreateBucket(context.Background(), project, bkt.Name, bkt, nil)
111 if err != nil {
112 t.Fatalf("client.CreateBucket: %v", err)
113 }
114
115 ua := &BucketAttrsToUpdate{
116 VersioningEnabled: false,
117 RequesterPays: false,
118 DefaultEventBasedHold: false,
119 Encryption: &BucketEncryption{DefaultKMSKeyName: "key2"},
120 Lifecycle: &Lifecycle{
121 Rules: []LifecycleRule{
122 {
123 Action: LifecycleAction{Type: "Delete"},
124 Condition: LifecycleCondition{AgeInDays: 30},
125 },
126 },
127 },
128 Logging: &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
129 Website: &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
130 StorageClass: "NEARLINE",
131 RPO: RPOAsyncTurbo,
132 }
133 want := &BucketAttrs{
134 Name: bucket,
135 VersioningEnabled: false,
136 RequesterPays: false,
137 DefaultEventBasedHold: false,
138 Encryption: &BucketEncryption{DefaultKMSKeyName: "key2"},
139 Lifecycle: Lifecycle{
140 Rules: []LifecycleRule{
141 {
142 Action: LifecycleAction{Type: "Delete"},
143 Condition: LifecycleCondition{AgeInDays: 30},
144 },
145 },
146 },
147 Logging: &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
148 Website: &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
149 StorageClass: "NEARLINE",
150 RPO: RPOAsyncTurbo,
151 }
152
153 got, err := client.UpdateBucket(context.Background(), bucket, ua, &BucketConditions{MetagenerationMatch: 1})
154 if err != nil {
155 t.Fatal(err)
156 }
157 if diff := cmp.Diff(got.Name, want.Name); diff != "" {
158 t.Errorf("Name: got(-),want(+):\n%s", diff)
159 }
160 if diff := cmp.Diff(got.VersioningEnabled, want.VersioningEnabled); diff != "" {
161 t.Errorf("VersioningEnabled: got(-),want(+):\n%s", diff)
162 }
163 if diff := cmp.Diff(got.RequesterPays, want.RequesterPays); diff != "" {
164 t.Errorf("RequesterPays: got(-),want(+):\n%s", diff)
165 }
166 if diff := cmp.Diff(got.DefaultEventBasedHold, want.DefaultEventBasedHold); diff != "" {
167 t.Errorf("DefaultEventBasedHold: got(-),want(+):\n%s", diff)
168 }
169 if diff := cmp.Diff(got.Encryption, want.Encryption); diff != "" {
170 t.Errorf("Encryption: got(-),want(+):\n%s", diff)
171 }
172 if diff := cmp.Diff(got.Lifecycle, want.Lifecycle); diff != "" {
173 t.Errorf("Lifecycle: got(-),want(+):\n%s", diff)
174 }
175 if diff := cmp.Diff(got.Logging, want.Logging); diff != "" {
176 t.Errorf("Logging: got(-),want(+):\n%s", diff)
177 }
178 if diff := cmp.Diff(got.Website, want.Website); diff != "" {
179 t.Errorf("Website: got(-),want(+):\n%s", diff)
180 }
181 if diff := cmp.Diff(got.RPO, want.RPO); diff != "" {
182 t.Errorf("RPO: got(-),want(+):\n%s", diff)
183 }
184 if diff := cmp.Diff(got.StorageClass, want.StorageClass); diff != "" {
185 t.Errorf("StorageClass: got(-),want(+):\n%s", diff)
186 }
187 })
188 }
189
190 func TestGetServiceAccountEmulated(t *testing.T) {
191 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
192 _, err := client.GetServiceAccount(context.Background(), project)
193 if err != nil {
194 t.Fatalf("client.GetServiceAccount: %v", err)
195 }
196 })
197 }
198
199 func TestGetSetTestIamPolicyEmulated(t *testing.T) {
200 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
201 battrs, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
202 Name: bucket,
203 }, nil)
204 if err != nil {
205 t.Fatalf("client.CreateBucket: %v", err)
206 }
207 got, err := client.GetIamPolicy(context.Background(), battrs.Name, 0)
208 if err != nil {
209 t.Fatalf("client.GetIamPolicy: %v", err)
210 }
211 err = client.SetIamPolicy(context.Background(), battrs.Name, &iampb.Policy{
212 Etag: got.GetEtag(),
213 Bindings: []*iampb.Binding{{Role: "roles/viewer", Members: []string{"allUsers"}}},
214 })
215 if err != nil {
216 t.Fatalf("client.SetIamPolicy: %v", err)
217 }
218 want := []string{"storage.foo", "storage.bar"}
219 perms, err := client.TestIamPermissions(context.Background(), battrs.Name, want)
220 if err != nil {
221 t.Fatalf("client.TestIamPermissions: %v", err)
222 }
223 if diff := cmp.Diff(perms, want); diff != "" {
224 t.Errorf("got(-),want(+):\n%s", diff)
225 }
226 })
227 }
228
229 func TestDeleteObjectEmulated(t *testing.T) {
230 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
231
232 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
233 Name: bucket,
234 }, nil)
235 if err != nil {
236 t.Fatalf("client.CreateBucket: %v", err)
237 }
238 want := ObjectAttrs{
239 Bucket: bucket,
240 Name: fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
241 }
242 w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
243 if _, err := w.Write(randomBytesToWrite); err != nil {
244 t.Fatalf("failed to populate test object: %v", err)
245 }
246 if err := w.Close(); err != nil {
247 t.Fatalf("closing object: %v", err)
248 }
249 err = client.DeleteObject(context.Background(), bucket, want.Name, defaultGen, nil)
250 if err != nil {
251 t.Fatalf("client.DeleteBucket: %v", err)
252 }
253 })
254 }
255
256 func TestGetObjectEmulated(t *testing.T) {
257 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
258
259 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
260 Name: bucket,
261 }, nil)
262 if err != nil {
263 t.Fatalf("client.CreateBucket: %v", err)
264 }
265 want := ObjectAttrs{
266 Bucket: bucket,
267 Name: fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
268 }
269 w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
270 if _, err := w.Write(randomBytesToWrite); err != nil {
271 t.Fatalf("failed to populate test object: %v", err)
272 }
273 if err := w.Close(); err != nil {
274 t.Fatalf("closing object: %v", err)
275 }
276 got, err := client.GetObject(context.Background(), &getObjectParams{bucket: bucket, object: want.Name, gen: defaultGen})
277 if err != nil {
278 t.Fatal(err)
279 }
280 if diff := cmp.Diff(got.Name, want.Name); diff != "" {
281 t.Errorf("got(-),want(+):\n%s", diff)
282 }
283 })
284 }
285
286 func TestRewriteObjectEmulated(t *testing.T) {
287 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
288
289 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
290 Name: bucket,
291 }, nil)
292 if err != nil {
293 t.Fatalf("client.CreateBucket: %v", err)
294 }
295 src := ObjectAttrs{
296 Bucket: bucket,
297 Name: fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
298 }
299 w := veneerClient.Bucket(bucket).Object(src.Name).NewWriter(context.Background())
300 if _, err := w.Write(randomBytesToWrite); err != nil {
301 t.Fatalf("failed to populate test object: %v", err)
302 }
303 if err := w.Close(); err != nil {
304 t.Fatalf("closing object: %v", err)
305 }
306 req := &rewriteObjectRequest{
307 dstObject: destinationObject{
308 bucket: bucket,
309 name: fmt.Sprintf("copy-of-%s", src.Name),
310 attrs: &ObjectAttrs{},
311 },
312 srcObject: sourceObject{
313 bucket: bucket,
314 name: src.Name,
315 gen: defaultGen,
316 },
317 }
318 got, err := client.RewriteObject(context.Background(), req)
319 if err != nil {
320 t.Fatal(err)
321 }
322 if !got.done {
323 t.Fatal("didn't finish writing!")
324 }
325 if want := int64(len(randomBytesToWrite)); got.written != want {
326 t.Errorf("Bytes written: got %d, want %d", got.written, want)
327 }
328 })
329 }
330
331 func TestUpdateObjectEmulated(t *testing.T) {
332 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
333
334 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
335 Name: bucket,
336 }, nil)
337 if err != nil {
338 t.Fatalf("client.CreateBucket: %v", err)
339 }
340 ct := time.Date(2022, 5, 25, 12, 12, 12, 0, time.UTC)
341 o := ObjectAttrs{
342 Bucket: bucket,
343 Name: fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
344 CustomTime: ct,
345 }
346 w := veneerClient.Bucket(bucket).Object(o.Name).NewWriter(context.Background())
347 if _, err := w.Write(randomBytesToWrite); err != nil {
348 t.Fatalf("failed to populate test object: %v", err)
349 }
350 if err := w.Close(); err != nil {
351 t.Fatalf("closing object: %v", err)
352 }
353 want := &ObjectAttrsToUpdate{
354 EventBasedHold: false,
355 TemporaryHold: false,
356 ContentType: "text/html",
357 ContentLanguage: "en",
358 ContentEncoding: "gzip",
359 ContentDisposition: "",
360 CacheControl: "",
361 CustomTime: ct.Add(10 * time.Hour),
362 }
363
364 params := &updateObjectParams{bucket: bucket, object: o.Name, uattrs: want, gen: defaultGen, conds: &Conditions{MetagenerationMatch: 1}}
365 got, err := client.UpdateObject(context.Background(), params)
366 if err != nil {
367 t.Fatalf("client.UpdateObject: %v", err)
368 }
369 if diff := cmp.Diff(got.Name, o.Name); diff != "" {
370 t.Errorf("Name: got(-),want(+):\n%s", diff)
371 }
372 if diff := cmp.Diff(got.EventBasedHold, want.EventBasedHold); diff != "" {
373 t.Errorf("EventBasedHold: got(-),want(+):\n%s", diff)
374 }
375 if diff := cmp.Diff(got.TemporaryHold, want.TemporaryHold); diff != "" {
376 t.Errorf("TemporaryHold: got(-),want(+):\n%s", diff)
377 }
378 if diff := cmp.Diff(got.ContentType, want.ContentType); diff != "" {
379 t.Errorf("ContentType: got(-),want(+):\n%s", diff)
380 }
381 if diff := cmp.Diff(got.ContentLanguage, want.ContentLanguage); diff != "" {
382 t.Errorf("ContentLanguage: got(-),want(+):\n%s", diff)
383 }
384 if diff := cmp.Diff(got.ContentEncoding, want.ContentEncoding); diff != "" {
385 t.Errorf("ContentEncoding: got(-),want(+):\n%s", diff)
386 }
387 if diff := cmp.Diff(got.ContentDisposition, want.ContentDisposition); diff != "" {
388 t.Errorf("ContentDisposition: got(-),want(+):\n%s", diff)
389 }
390 if diff := cmp.Diff(got.CacheControl, want.CacheControl); diff != "" {
391 t.Errorf("CacheControl: got(-),want(+):\n%s", diff)
392 }
393 if diff := cmp.Diff(got.CustomTime, want.CustomTime); diff != "" {
394 t.Errorf("CustomTime: got(-),want(+):\n%s", diff)
395 }
396 })
397 }
398
399 func TestListObjectsEmulated(t *testing.T) {
400 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
401
402 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
403 Name: bucket,
404 }, nil)
405 if err != nil {
406 t.Fatalf("client.CreateBucket: %v", err)
407 }
408 prefix := time.Now().Nanosecond()
409 want := []*ObjectAttrs{
410 {
411 Bucket: bucket,
412 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
413 },
414 {
415 Bucket: bucket,
416 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
417 },
418 {
419 Bucket: bucket,
420 Name: fmt.Sprintf("object-%d", time.Now().Nanosecond()),
421 },
422 }
423 for _, obj := range want {
424 w := veneerClient.Bucket(bucket).Object(obj.Name).NewWriter(context.Background())
425 if _, err := w.Write(randomBytesToWrite); err != nil {
426 t.Fatalf("failed to populate test data: %v", err)
427 }
428 if err := w.Close(); err != nil {
429 t.Fatalf("closing object: %v", err)
430 }
431 }
432
433
434 it := client.ListObjects(context.Background(), bucket, nil)
435 var o *ObjectAttrs
436 var got int
437 for i := 0; err == nil && i <= len(want); i++ {
438 o, err = it.Next()
439 if err != nil {
440 break
441 }
442 got++
443 if diff := cmp.Diff(o.Name, want[i].Name); diff != "" {
444 t.Errorf("got(-),want(+):\n%s", diff)
445 }
446 }
447 if err != iterator.Done {
448 t.Fatalf("expected %q but got %q", iterator.Done, err)
449 }
450 expected := len(want)
451 if got != expected {
452 t.Errorf("expected to get %d objects, but got %d", expected, got)
453 }
454 })
455 }
456
457 func TestListObjectsWithPrefixEmulated(t *testing.T) {
458 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
459
460 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
461 Name: bucket,
462 }, nil)
463 if err != nil {
464 t.Fatalf("client.CreateBucket: %v", err)
465 }
466 prefix := time.Now().Nanosecond()
467 want := []*ObjectAttrs{
468 {
469 Bucket: bucket,
470 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
471 },
472 {
473 Bucket: bucket,
474 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
475 },
476 {
477 Bucket: bucket,
478 Name: fmt.Sprintf("object-%d", time.Now().Nanosecond()),
479 },
480 }
481 for _, obj := range want {
482 w := veneerClient.Bucket(bucket).Object(obj.Name).NewWriter(context.Background())
483 if _, err := w.Write(randomBytesToWrite); err != nil {
484 t.Fatalf("failed to populate test data: %v", err)
485 }
486 if err := w.Close(); err != nil {
487 t.Fatalf("closing object: %v", err)
488 }
489 }
490
491
492 it := client.ListObjects(context.Background(), bucket, &Query{Prefix: strconv.Itoa(prefix)})
493 var o *ObjectAttrs
494 var got int
495 want = want[:2]
496 for i := 0; i <= len(want); i++ {
497 o, err = it.Next()
498 if err != nil {
499 break
500 }
501 got++
502 if diff := cmp.Diff(o.Name, want[i].Name); diff != "" {
503 t.Errorf("got(-),want(+):\n%s", diff)
504 }
505 }
506 if err != iterator.Done {
507 t.Fatalf("expected %q but got %q", iterator.Done, err)
508 }
509 expected := len(want)
510 if got != expected {
511 t.Errorf("expected to get %d objects, but got %d", expected, got)
512 }
513 })
514 }
515
516 func TestListBucketsEmulated(t *testing.T) {
517 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
518 prefix := time.Now().Nanosecond()
519 want := []*BucketAttrs{
520 {Name: fmt.Sprintf("%d-%s-%d", prefix, bucket, time.Now().Nanosecond())},
521 {Name: fmt.Sprintf("%d-%s-%d", prefix, bucket, time.Now().Nanosecond())},
522 {Name: fmt.Sprintf("%s-%d", bucket, time.Now().Nanosecond())},
523 }
524
525 for _, b := range want {
526 _, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
527 if err != nil {
528 t.Fatalf("client.CreateBucket: %v", err)
529 }
530 }
531
532 it := client.ListBuckets(context.Background(), project)
533 it.Prefix = strconv.Itoa(prefix)
534
535 want = want[:2]
536 var err error
537 var b *BucketAttrs
538 var got int
539 for i := 0; err == nil && i <= len(want); i++ {
540 b, err = it.Next()
541 if err != nil {
542 break
543 }
544 got++
545 if diff := cmp.Diff(b.Name, want[i].Name); diff != "" {
546 t.Errorf("got(-),want(+):\n%s", diff)
547 }
548 }
549 if err != iterator.Done {
550 t.Fatalf("expected %q but got %q", iterator.Done, err)
551 }
552 expected := len(want)
553 if got != expected {
554 t.Errorf("expected to get %d buckets, but got %d", expected, got)
555 }
556 })
557 }
558
559 func TestListBucketACLsEmulated(t *testing.T) {
560 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
561 ctx := context.Background()
562 attrs := &BucketAttrs{
563 Name: bucket,
564 PredefinedACL: "publicRead",
565 }
566
567 if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
568 t.Fatalf("client.CreateBucket: %v", err)
569 }
570
571 acls, err := client.ListBucketACLs(ctx, bucket)
572 if err != nil {
573 t.Fatalf("client.ListBucketACLs: %v", err)
574 }
575 if want, got := len(acls), 2; want != got {
576 t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
577 }
578 })
579 }
580
581 func TestUpdateBucketACLEmulated(t *testing.T) {
582 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
583 ctx := context.Background()
584 attrs := &BucketAttrs{
585 Name: bucket,
586 PredefinedACL: "authenticatedRead",
587 }
588
589 if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
590 t.Fatalf("client.CreateBucket: %v", err)
591 }
592 var listAcls []ACLRule
593 var err error
594
595 if listAcls, err = client.ListBucketACLs(ctx, bucket); err != nil {
596 t.Fatalf("client.ListBucketACLs: %v", err)
597 }
598 if got, want := len(listAcls), 2; got != want {
599 t.Errorf("ListBucketACLs: got %v, want %v items", listAcls, want)
600 }
601 entity := AllUsers
602 role := RoleReader
603 err = client.UpdateBucketACL(ctx, bucket, entity, role)
604 if err != nil {
605 t.Fatalf("client.UpdateBucketACL: %v", err)
606 }
607
608 if listAcls, err = client.ListBucketACLs(ctx, bucket); err != nil {
609 t.Fatalf("client.ListBucketACLs: %v", err)
610 }
611 if got, want := len(listAcls), 3; got != want {
612 t.Errorf("ListBucketACLs: got %v, want %v items", listAcls, want)
613 }
614 })
615 }
616
617 func TestDeleteBucketACLEmulated(t *testing.T) {
618 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
619 ctx := context.Background()
620 attrs := &BucketAttrs{
621 Name: bucket,
622 PredefinedACL: "publicRead",
623 }
624
625 if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
626 t.Fatalf("client.CreateBucket: %v", err)
627 }
628
629 acls, err := client.ListBucketACLs(ctx, bucket)
630 if err != nil {
631 t.Fatalf("client.ListBucketACLs: %v", err)
632 }
633 if got, want := len(acls), 2; got != want {
634 t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
635 }
636
637 if err := client.DeleteBucketACL(ctx, bucket, AllUsers); err != nil {
638 t.Fatalf("client.DeleteBucketACL: %v", err)
639 }
640
641 acls, err = client.ListBucketACLs(ctx, bucket)
642 if err != nil {
643 t.Fatalf("client.ListBucketACLs: %v", err)
644 }
645 if got, want := len(acls), 1; got != want {
646 t.Errorf("ListBucketACLs: got %v, want %v items", acls, want)
647 }
648 })
649 }
650
651 func TestDefaultObjectACLCRUDEmulated(t *testing.T) {
652 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
653 ctx := context.Background()
654 attrs := &BucketAttrs{
655 Name: bucket,
656 PredefinedDefaultObjectACL: "publicRead",
657 }
658
659 if _, err := client.CreateBucket(ctx, project, attrs.Name, attrs, nil); err != nil {
660 t.Fatalf("client.CreateBucket: %v", err)
661 }
662
663 acls, err := client.ListDefaultObjectACLs(ctx, bucket)
664 if err != nil {
665 t.Fatalf("client.ListDefaultObjectACLs: %v", err)
666 }
667 if got, want := len(acls), 2; got != want {
668 t.Errorf("ListDefaultObjectACLs: got %v, want %v items", acls, want)
669 }
670 entity := AllAuthenticatedUsers
671 role := RoleOwner
672 err = client.UpdateDefaultObjectACL(ctx, bucket, entity, role)
673 if err != nil {
674 t.Fatalf("UpdateDefaultObjectCL: %v", err)
675 }
676
677 acls, err = client.ListDefaultObjectACLs(ctx, bucket)
678 if err != nil {
679 t.Fatalf("client.ListDefaultObjectACLs: %v", err)
680 }
681 if got, want := len(acls), 3; got != want {
682 t.Errorf("ListDefaultObjectACLs: %v got %v, want %v items", len(acls), acls, want)
683 }
684
685 if err := client.DeleteDefaultObjectACL(ctx, bucket, AllUsers); err != nil {
686 t.Fatalf("client.DeleteDefaultObjectACL: %v", err)
687 }
688
689 acls, err = client.ListDefaultObjectACLs(ctx, bucket)
690 if err != nil {
691 t.Fatalf("client.ListDefaultObjectACLs: %v", err)
692 }
693 if got, want := len(acls), 2; got != want {
694 t.Errorf("ListDefaultObjectACLs: %v got %v, want %v items", len(acls), acls, want)
695 }
696 })
697 }
698
699 func TestObjectACLCRUDEmulated(t *testing.T) {
700 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
701
702 ctx := context.Background()
703 _, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
704 Name: bucket,
705 }, nil)
706 if err != nil {
707 t.Fatalf("CreateBucket: %v", err)
708 }
709 o := ObjectAttrs{
710 Bucket: bucket,
711 Name: fmt.Sprintf("testObject-%d", time.Now().Nanosecond()),
712 }
713 w := veneerClient.Bucket(bucket).Object(o.Name).NewWriter(context.Background())
714 if _, err := w.Write(randomBytesToWrite); err != nil {
715 t.Fatalf("failed to populate test object: %v", err)
716 }
717 if err := w.Close(); err != nil {
718 t.Fatalf("closing object: %v", err)
719 }
720 var listAcls []ACLRule
721
722 if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
723 t.Fatalf("ListObjectACLs: %v", err)
724 }
725 if got, want := len(listAcls), 4; got != want {
726 t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
727 }
728 entity := AllUsers
729 role := RoleReader
730 err = client.UpdateObjectACL(ctx, bucket, o.Name, entity, role)
731 if err != nil {
732 t.Fatalf("UpdateObjectCL: %v", err)
733 }
734
735 if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
736 t.Fatalf("ListObjectACLs: %v", err)
737 }
738 if got, want := len(listAcls), 5; got != want {
739 t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
740 }
741 if err = client.DeleteObjectACL(ctx, bucket, o.Name, AllUsers); err != nil {
742 t.Fatalf("client.DeleteObjectACL: %v", err)
743 }
744
745 if listAcls, err = client.ListObjectACLs(ctx, bucket, o.Name); err != nil {
746 t.Fatalf("ListObjectACLs: %v", err)
747 }
748 if got, want := len(listAcls), 4; got != want {
749 t.Errorf("ListObjectACLs: got %v, want %v items", listAcls, want)
750 }
751 })
752 }
753
754 func TestOpenReaderEmulated(t *testing.T) {
755 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
756
757 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
758 Name: bucket,
759 }, nil)
760 if err != nil {
761 t.Fatalf("client.CreateBucket: %v", err)
762 }
763 prefix := time.Now().Nanosecond()
764 want := &ObjectAttrs{
765 Bucket: bucket,
766 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
767 }
768 w := veneerClient.Bucket(bucket).Object(want.Name).NewWriter(context.Background())
769 if _, err := w.Write(randomBytesToWrite); err != nil {
770 t.Fatalf("failed to populate test data: %v", err)
771 }
772 if err := w.Close(); err != nil {
773 t.Fatalf("closing object: %v", err)
774 }
775
776 params := &newRangeReaderParams{
777 bucket: bucket,
778 object: want.Name,
779 gen: defaultGen,
780 offset: 0,
781 length: -1,
782 }
783 r, err := client.NewRangeReader(context.Background(), params)
784 if err != nil {
785 t.Fatalf("opening reading: %v", err)
786 }
787 wantLen := len(randomBytesToWrite)
788 got := make([]byte, wantLen)
789 n, err := r.Read(got)
790 if n != wantLen {
791 t.Fatalf("expected to read %d bytes, but got %d", wantLen, n)
792 }
793 if diff := cmp.Diff(got, randomBytesToWrite); diff != "" {
794 t.Fatalf("Read: got(-),want(+):\n%s", diff)
795 }
796 })
797 }
798
799 func TestOpenWriterEmulated(t *testing.T) {
800 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
801
802 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
803 Name: bucket,
804 }, nil)
805 if err != nil {
806 t.Fatalf("client.CreateBucket: %v", err)
807 }
808 prefix := time.Now().Nanosecond()
809 want := &ObjectAttrs{
810 Bucket: bucket,
811 Name: fmt.Sprintf("%d-object-%d", prefix, time.Now().Nanosecond()),
812 Generation: defaultGen,
813 }
814
815 var gotAttrs *ObjectAttrs
816 params := &openWriterParams{
817 attrs: want,
818 bucket: bucket,
819 ctx: context.Background(),
820 donec: make(chan struct{}),
821 setError: func(_ error) {},
822 progress: func(_ int64) {},
823 setObj: func(o *ObjectAttrs) { gotAttrs = o },
824 }
825 pw, err := client.OpenWriter(params)
826 if err != nil {
827 t.Fatalf("failed to open writer: %v", err)
828 }
829 if _, err := pw.Write(randomBytesToWrite); err != nil {
830 t.Fatalf("failed to populate test data: %v", err)
831 }
832 if err := pw.Close(); err != nil {
833 t.Fatalf("closing object: %v", err)
834 }
835 select {
836 case <-params.donec:
837 }
838 if gotAttrs == nil {
839 t.Fatalf("Writer finished, but resulting object wasn't set")
840 }
841 if diff := cmp.Diff(gotAttrs.Name, want.Name); diff != "" {
842 t.Fatalf("Resulting object name: got(-),want(+):\n%s", diff)
843 }
844
845 r, err := veneerClient.Bucket(bucket).Object(want.Name).NewReader(context.Background())
846 if err != nil {
847 t.Fatalf("opening reading: %v", err)
848 }
849 wantLen := len(randomBytesToWrite)
850 got := make([]byte, wantLen)
851 n, err := r.Read(got)
852 if n != wantLen {
853 t.Fatalf("expected to read %d bytes, but got %d", wantLen, n)
854 }
855 if diff := cmp.Diff(got, randomBytesToWrite); diff != "" {
856 t.Fatalf("checking written content: got(-),want(+):\n%s", diff)
857 }
858 })
859 }
860
861 func TestListNotificationsEmulated(t *testing.T) {
862 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
863
864 ctx := context.Background()
865 _, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
866 Name: bucket,
867 }, nil)
868 if err != nil {
869 t.Fatalf("client.CreateBucket: %v", err)
870 }
871 _, err = client.CreateNotification(ctx, bucket, &Notification{
872 TopicProjectID: project,
873 TopicID: "go-storage-notification-test",
874 PayloadFormat: "JSON_API_V1",
875 })
876 if err != nil {
877 t.Fatalf("client.CreateNotification: %v", err)
878 }
879 n, err := client.ListNotifications(ctx, bucket)
880 if err != nil {
881 t.Fatalf("client.ListNotifications: %v", err)
882 }
883 if want, got := 1, len(n); want != got {
884 t.Errorf("ListNotifications: got %v, want %v items", n, want)
885 }
886 })
887 }
888
889 func TestCreateNotificationEmulated(t *testing.T) {
890 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
891
892 ctx := context.Background()
893 _, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
894 Name: bucket,
895 }, nil)
896 if err != nil {
897 t.Fatalf("client.CreateBucket: %v", err)
898 }
899
900 want := &Notification{
901 TopicProjectID: project,
902 TopicID: "go-storage-notification-test",
903 PayloadFormat: "JSON_API_V1",
904 }
905 got, err := client.CreateNotification(ctx, bucket, want)
906 if err != nil {
907 t.Fatalf("client.CreateNotification: %v", err)
908 }
909 if diff := cmp.Diff(got.TopicID, want.TopicID); diff != "" {
910 t.Errorf("CreateNotification topic: got(-),want(+):\n%s", diff)
911 }
912 })
913 }
914
915 func TestDeleteNotificationEmulated(t *testing.T) {
916 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
917
918 ctx := context.Background()
919 _, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{
920 Name: bucket,
921 }, nil)
922 if err != nil {
923 t.Fatalf("client.CreateBucket: %v", err)
924 }
925 var n *Notification
926 n, err = client.CreateNotification(ctx, bucket, &Notification{
927 TopicProjectID: project,
928 TopicID: "go-storage-notification-test",
929 PayloadFormat: "JSON_API_V1",
930 })
931 if err != nil {
932 t.Fatalf("client.CreateNotification: %v", err)
933 }
934 err = client.DeleteNotification(ctx, bucket, n.ID)
935 if err != nil {
936 t.Fatalf("client.DeleteNotification: %v", err)
937 }
938 })
939 }
940
941 func initEmulatorClients() func() error {
942 noopCloser := func() error { return nil }
943 if !isEmulatorEnvironmentSet() {
944 return noopCloser
945 }
946 ctx := context.Background()
947
948 grpcClient, err := newGRPCStorageClient(ctx)
949 if err != nil {
950 log.Fatalf("Error setting up gRPC client for emulator tests: %v", err)
951 return noopCloser
952 }
953 httpClient, err := newHTTPStorageClient(ctx)
954 if err != nil {
955 log.Fatalf("Error setting up HTTP client for emulator tests: %v", err)
956 return noopCloser
957 }
958
959 emulatorClients = map[string]storageClient{
960 "http": httpClient,
961 "grpc": grpcClient,
962 }
963
964 veneerClient, err = NewClient(ctx)
965 if err != nil {
966 log.Fatalf("Error setting up Veneer client for emulator tests: %v", err)
967 return noopCloser
968 }
969
970 return func() error {
971 gerr := grpcClient.Close()
972 herr := httpClient.Close()
973 verr := veneerClient.Close()
974
975 if gerr != nil {
976 return gerr
977 } else if herr != nil {
978 return herr
979 }
980 return verr
981 }
982 }
983
984 func TestLockBucketRetentionPolicyEmulated(t *testing.T) {
985 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
986 b := &BucketAttrs{
987 Name: bucket,
988 RetentionPolicy: &RetentionPolicy{
989 RetentionPeriod: time.Minute,
990 },
991 }
992
993 _, err := client.CreateBucket(context.Background(), project, b.Name, b, nil)
994 if err != nil {
995 t.Fatalf("client.CreateBucket: %v", err)
996 }
997
998 err = client.LockBucketRetentionPolicy(context.Background(), b.Name, &BucketConditions{MetagenerationMatch: 1})
999 if err != nil {
1000 t.Fatalf("client.LockBucketRetentionPolicy: %v", err)
1001 }
1002 got, err := client.GetBucket(context.Background(), bucket, nil)
1003 if err != nil {
1004 t.Fatalf("client.GetBucket: %v", err)
1005 }
1006 if !got.RetentionPolicy.IsLocked {
1007 t.Error("Expected bucket retention policy to be locked, but was not.")
1008 }
1009 })
1010 }
1011
1012 func TestComposeEmulated(t *testing.T) {
1013 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1014 ctx := context.Background()
1015
1016
1017 _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{
1018 Name: bucket,
1019 }, nil)
1020 if err != nil {
1021 t.Fatalf("client.CreateBucket: %v", err)
1022 }
1023 prefix := time.Now().Nanosecond()
1024 srcNames := []string{
1025 fmt.Sprintf("%d-object1", prefix),
1026 fmt.Sprintf("%d-object2", prefix),
1027 }
1028
1029 for _, n := range srcNames {
1030 w := veneerClient.Bucket(bucket).Object(n).NewWriter(ctx)
1031 if _, err := w.Write(randomBytesToWrite); err != nil {
1032 t.Fatalf("failed to populate test data: %v", err)
1033 }
1034 if err := w.Close(); err != nil {
1035 t.Fatalf("closing object: %v", err)
1036 }
1037 }
1038
1039 dstName := fmt.Sprintf("%d-object3", prefix)
1040 req := composeObjectRequest{
1041 dstBucket: bucket,
1042 dstObject: destinationObject{
1043 name: dstName,
1044 attrs: &ObjectAttrs{StorageClass: "COLDLINE"},
1045 },
1046 srcs: []sourceObject{
1047 {name: srcNames[0]},
1048 {name: srcNames[1]},
1049 },
1050 }
1051 attrs, err := client.ComposeObject(ctx, &req)
1052 if err != nil {
1053 t.Fatalf("client.ComposeObject(): %v", err)
1054 }
1055 if got := attrs.Name; got != dstName {
1056 t.Errorf("attrs.Name: got %v, want %v", got, dstName)
1057 }
1058
1059
1060 if got, want := attrs.Size, 2*len(randomBytesToWrite); got != int64(want) {
1061 t.Errorf("attrs.Size: got %v, want %v", got, want)
1062 }
1063
1064 if got, want := attrs.StorageClass, "COLDLINE"; got != want {
1065 t.Errorf("attrs.StorageClass: got %v, want %v", got, want)
1066 }
1067 })
1068 }
1069
1070 func TestHMACKeyCRUDEmulated(t *testing.T) {
1071 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1072 ctx := context.Background()
1073 serviceAccountEmail := "test@test-project.iam.gserviceaccount.com"
1074 want, err := client.CreateHMACKey(ctx, project, serviceAccountEmail)
1075 if err != nil {
1076 t.Fatalf("CreateHMACKey: %v", err)
1077 }
1078 if want == nil {
1079 t.Fatal("CreateHMACKey: Unexpectedly got back a nil HMAC key")
1080 }
1081 if want.State != Active {
1082 t.Fatalf("CreateHMACKey: Unexpected state %q, expected %q", want.State, Active)
1083 }
1084 got, err := client.GetHMACKey(ctx, project, want.AccessID)
1085 if err != nil {
1086 t.Fatalf("GetHMACKey: %v", err)
1087 }
1088 if diff := cmp.Diff(got.ID, want.ID); diff != "" {
1089 t.Errorf("GetHMACKey ID:got(-),want(+):\n%s", diff)
1090 }
1091 if diff := cmp.Diff(got.UpdatedTime, want.UpdatedTime); diff != "" {
1092 t.Errorf("GetHMACKey UpdatedTime: got(-),want(+):\n%s", diff)
1093 }
1094 attr := &HMACKeyAttrsToUpdate{
1095 State: Inactive,
1096 }
1097 got, err = client.UpdateHMACKey(ctx, project, serviceAccountEmail, want.AccessID, attr)
1098 if err != nil {
1099 t.Fatalf("UpdateHMACKey: %v", err)
1100 }
1101 if got.State != attr.State {
1102 t.Errorf("UpdateHMACKey State: got %v, want %v", got.State, attr.State)
1103 }
1104 showDeletedKeys := false
1105 it := client.ListHMACKeys(ctx, project, serviceAccountEmail, showDeletedKeys)
1106 var count int
1107 var e error
1108 for ; ; count++ {
1109 _, e = it.Next()
1110 if e != nil {
1111 break
1112 }
1113 }
1114 if e != iterator.Done {
1115 t.Fatalf("ListHMACKeys: expected %q but got %q", iterator.Done, err)
1116 }
1117 if expected := 1; count != expected {
1118 t.Errorf("ListHMACKeys: expected to get %d hmacKeys, but got %d", expected, count)
1119 }
1120 err = client.DeleteHMACKey(ctx, project, want.AccessID)
1121 if err != nil {
1122 t.Fatalf("DeleteHMACKey: %v", err)
1123 }
1124 got, err = client.GetHMACKey(ctx, project, want.AccessID)
1125 if err == nil {
1126 t.Fatalf("GetHMACKey unexcepted error: wanted 404")
1127 }
1128 })
1129 }
1130
1131 func TestBucketConditionsEmulated(t *testing.T) {
1132 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1133 ctx := context.Background()
1134 cases := []struct {
1135 name string
1136 call func(bucket string, metaGen int64) error
1137 }{
1138 {
1139 name: "get",
1140 call: func(bucket string, metaGen int64) error {
1141 _, err := client.GetBucket(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
1142 return err
1143 },
1144 },
1145 {
1146 name: "update",
1147 call: func(bucket string, metaGen int64) error {
1148 _, err := client.UpdateBucket(ctx, bucket, &BucketAttrsToUpdate{StorageClass: "ARCHIVE"}, &BucketConditions{MetagenerationMatch: metaGen})
1149 return err
1150 },
1151 },
1152 {
1153 name: "delete",
1154 call: func(bucket string, metaGen int64) error {
1155 return client.DeleteBucket(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
1156 },
1157 },
1158 {
1159 name: "lockRetentionPolicy",
1160 call: func(bucket string, metaGen int64) error {
1161 return client.LockBucketRetentionPolicy(ctx, bucket, &BucketConditions{MetagenerationMatch: metaGen})
1162 },
1163 },
1164 }
1165 for _, c := range cases {
1166 t.Run(c.name, func(r *testing.T) {
1167 bucket, metaGen, err := createBucket(ctx, project)
1168 if err != nil {
1169 r.Fatalf("creating bucket: %v", err)
1170 }
1171 if err := c.call(bucket, metaGen); err != nil {
1172 r.Errorf("error: %v", err)
1173 }
1174 })
1175 }
1176 })
1177 }
1178
1179 func TestObjectConditionsEmulated(t *testing.T) {
1180 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1181 ctx := context.Background()
1182
1183
1184 if _, err := client.CreateBucket(context.Background(), project, bucket, &BucketAttrs{Name: bucket}, nil); err != nil {
1185 t.Fatalf("client.CreateBucket: %v", err)
1186 }
1187
1188 cases := []struct {
1189 name string
1190 call func() error
1191 }{
1192 {
1193 name: "update generation",
1194 call: func() error {
1195 objName, gen, _, err := createObject(ctx, bucket)
1196 if err != nil {
1197 return fmt.Errorf("creating object: %w", err)
1198 }
1199 uattrs := &ObjectAttrsToUpdate{CustomTime: time.Now()}
1200 _, err = client.UpdateObject(ctx, &updateObjectParams{bucket: bucket, object: objName, uattrs: uattrs, gen: gen})
1201 return err
1202 },
1203 },
1204 {
1205 name: "update ifMetagenerationMatch",
1206 call: func() error {
1207 objName, gen, metaGen, err := createObject(ctx, bucket)
1208 if err != nil {
1209 return fmt.Errorf("creating object: %w", err)
1210 }
1211 uattrs := &ObjectAttrsToUpdate{CustomTime: time.Now()}
1212 conds := &Conditions{
1213 GenerationMatch: gen,
1214 MetagenerationMatch: metaGen,
1215 }
1216 _, err = client.UpdateObject(ctx, &updateObjectParams{bucket: bucket, object: objName, uattrs: uattrs, gen: gen, conds: conds})
1217 return err
1218 },
1219 },
1220 {
1221 name: "write ifGenerationMatch",
1222 call: func() error {
1223 var err error
1224 _, err = client.OpenWriter(&openWriterParams{
1225 ctx: ctx,
1226 chunkSize: 256 * 1024,
1227 chunkRetryDeadline: 0,
1228 bucket: bucket,
1229 attrs: &ObjectAttrs{},
1230 conds: &Conditions{DoesNotExist: true},
1231 encryptionKey: nil,
1232 sendCRC32C: false,
1233 donec: nil,
1234 setError: func(e error) {
1235 if e != nil {
1236 err = e
1237 }
1238 },
1239 progress: nil,
1240 setObj: nil,
1241 })
1242 return err
1243 },
1244 },
1245 {
1246 name: "rewrite ifMetagenerationMatch",
1247 call: func() error {
1248 objName, gen, metaGen, err := createObject(ctx, bucket)
1249 if err != nil {
1250 return fmt.Errorf("creating object: %w", err)
1251 }
1252 _, err = client.RewriteObject(ctx, &rewriteObjectRequest{
1253 srcObject: sourceObject{
1254 name: objName,
1255 bucket: bucket,
1256 gen: gen,
1257 conds: &Conditions{
1258 GenerationMatch: gen,
1259 MetagenerationMatch: metaGen,
1260 },
1261 },
1262 dstObject: destinationObject{
1263 name: fmt.Sprintf("%d-object", time.Now().Nanosecond()),
1264 bucket: bucket,
1265 conds: &Conditions{
1266 DoesNotExist: true,
1267 },
1268 attrs: &ObjectAttrs{},
1269 },
1270 })
1271 return err
1272 },
1273 },
1274 {
1275 name: "compose ifGenerationMatch",
1276 call: func() error {
1277 obj1, obj1Gen, _, err := createObject(ctx, bucket)
1278 if err != nil {
1279 return fmt.Errorf("creating object: %w", err)
1280 }
1281 obj2, obj2Gen, _, err := createObject(ctx, bucket)
1282 if err != nil {
1283 return fmt.Errorf("creating object: %w", err)
1284 }
1285 _, err = client.ComposeObject(ctx, &composeObjectRequest{
1286 dstBucket: bucket,
1287 dstObject: destinationObject{
1288 name: fmt.Sprintf("%d-object", time.Now().Nanosecond()),
1289 bucket: bucket,
1290 conds: &Conditions{DoesNotExist: true},
1291 attrs: &ObjectAttrs{},
1292 },
1293 srcs: []sourceObject{
1294 {
1295 name: obj1,
1296 bucket: bucket,
1297 gen: obj1Gen,
1298 conds: &Conditions{
1299 GenerationMatch: obj1Gen,
1300 },
1301 },
1302 {
1303 name: obj2,
1304 bucket: bucket,
1305 conds: &Conditions{
1306 GenerationMatch: obj2Gen,
1307 },
1308 },
1309 },
1310 })
1311 return err
1312 },
1313 },
1314 {
1315 name: "delete ifGenerationMatch",
1316 call: func() error {
1317 objName, gen, _, err := createObject(ctx, bucket)
1318 if err != nil {
1319 return fmt.Errorf("creating object: %w", err)
1320 }
1321 err = client.DeleteObject(ctx, bucket, objName, gen, &Conditions{GenerationMatch: gen})
1322 return err
1323 },
1324 },
1325 {
1326 name: "get ifMetagenerationMatch",
1327 call: func() error {
1328 objName, gen, metaGen, err := createObject(ctx, bucket)
1329 if err != nil {
1330 return fmt.Errorf("creating object: %w", err)
1331 }
1332 _, err = client.GetObject(ctx, &getObjectParams{bucket: bucket, object: objName, gen: gen, conds: &Conditions{GenerationMatch: gen, MetagenerationMatch: metaGen}})
1333 return err
1334 },
1335 },
1336 }
1337 for _, c := range cases {
1338 t.Run(c.name, func(r *testing.T) {
1339 if err := c.call(); err != nil {
1340 r.Errorf("error: %v", err)
1341 }
1342 })
1343 }
1344 })
1345 }
1346
1347
1348 func TestRetryNeverEmulated(t *testing.T) {
1349 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1350 ctx := context.Background()
1351 instructions := map[string][]string{"storage.buckets.get": {"return-503"}}
1352 testID := createRetryTest(t, project, bucket, client, instructions)
1353 ctx = callctx.SetHeaders(ctx, "x-retry-test-id", testID)
1354 _, err := client.GetBucket(ctx, bucket, nil, withRetryConfig(&retryConfig{policy: RetryNever}))
1355
1356 var ae *apierror.APIError
1357 if errors.As(err, &ae) {
1358
1359
1360 if ae.GRPCStatus().Code() != codes.Unavailable && ae.HTTPCode() != 503 {
1361 t.Errorf("GetBucket: got unexpected error %v; want 503", err)
1362 }
1363 }
1364 })
1365 }
1366
1367
1368 func TestRetryTimeoutEmulated(t *testing.T) {
1369 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1370 ctx := context.Background()
1371 instructions := map[string][]string{"storage.buckets.get": {"return-503", "return-503", "return-503", "return-503", "return-503"}}
1372 testID := createRetryTest(t, project, bucket, client, instructions)
1373 ctx = callctx.SetHeaders(ctx, "x-retry-test-id", testID)
1374 ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
1375 defer cancel()
1376 _, err := client.GetBucket(ctx, bucket, nil, idempotent(true))
1377
1378 var ae *apierror.APIError
1379 if errors.As(err, &ae) {
1380
1381
1382 if ae.GRPCStatus().Code() != codes.Unavailable && ae.HTTPCode() != 503 {
1383 t.Errorf("GetBucket: got unexpected error: %v; want 503", err)
1384 }
1385 }
1386
1387 if !errors.Is(err, context.DeadlineExceeded) {
1388 t.Errorf("GetBucket: got unexpected error %v, want to match DeadlineExceeded.", err)
1389 }
1390 })
1391 }
1392
1393
1394 func TestRetryMaxAttemptsEmulated(t *testing.T) {
1395 transportClientTest(t, func(t *testing.T, project, bucket string, client storageClient) {
1396 ctx := context.Background()
1397 instructions := map[string][]string{"storage.buckets.get": {"return-503", "return-503", "return-503", "return-503", "return-503"}}
1398 testID := createRetryTest(t, project, bucket, client, instructions)
1399 ctx = callctx.SetHeaders(ctx, "x-retry-test-id", testID)
1400 config := &retryConfig{maxAttempts: expectedAttempts(3), backoff: &gax.Backoff{Initial: 10 * time.Millisecond}}
1401 _, err := client.GetBucket(ctx, bucket, nil, idempotent(true), withRetryConfig(config))
1402
1403 var ae *apierror.APIError
1404 if errors.As(err, &ae) {
1405
1406
1407 if ae.GRPCStatus().Code() != codes.Unavailable && ae.HTTPCode() != 503 {
1408 t.Errorf("GetBucket: got unexpected error %v; want 503", err)
1409 }
1410 }
1411
1412 if got, want := err.Error(), "retry failed after 3 attempts"; !strings.Contains(got, want) {
1413 t.Errorf("got error: %q, want to contain: %q", got, want)
1414 }
1415 })
1416 }
1417
1418
1419
1420
1421 func createRetryTest(t *testing.T, project, bucket string, client storageClient, instructions map[string][]string) string {
1422 t.Helper()
1423 ctx := context.Background()
1424
1425 _, err := client.CreateBucket(ctx, project, bucket, &BucketAttrs{}, nil)
1426 if err != nil {
1427 t.Fatalf("creating bucket: %v", err)
1428 }
1429
1430
1431
1432 host := os.Getenv("STORAGE_EMULATOR_HOST")
1433 endpoint, err := url.Parse(host)
1434 if err != nil {
1435 t.Fatalf("parsing endpoint: %v", err)
1436 }
1437 var transport string
1438 if _, ok := client.(*httpStorageClient); ok {
1439 transport = "http"
1440 } else {
1441 transport = "grpc"
1442 }
1443
1444 et := emulatorTest{T: t, name: t.Name(), resources: resources{}, host: endpoint}
1445 et.create(instructions, transport)
1446 t.Cleanup(func() {
1447 et.delete()
1448 })
1449 return et.id
1450 }
1451
1452
1453
1454 func createObject(ctx context.Context, bucket string) (string, int64, int64, error) {
1455 prefix := time.Now().Nanosecond()
1456 objName := fmt.Sprintf("%d-object", prefix)
1457
1458 w := veneerClient.Bucket(bucket).Object(objName).NewWriter(ctx)
1459 if _, err := w.Write(randomBytesToWrite); err != nil {
1460 return "", 0, 0, fmt.Errorf("failed to populate test data: %w", err)
1461 }
1462 if err := w.Close(); err != nil {
1463 return "", 0, 0, fmt.Errorf("closing object: %w", err)
1464 }
1465 attrs, err := veneerClient.Bucket(bucket).Object(objName).Attrs(ctx)
1466 if err != nil {
1467 return "", 0, 0, fmt.Errorf("get object: %w", err)
1468 }
1469 return objName, attrs.Generation, attrs.Metageneration, nil
1470 }
1471
1472
1473
1474 func createBucket(ctx context.Context, projectID string) (string, int64, error) {
1475 prefix := time.Now().Nanosecond()
1476 bucket := fmt.Sprintf("%d-bucket", prefix)
1477
1478 if err := veneerClient.Bucket(bucket).Create(ctx, projectID, nil); err != nil {
1479 return "", 0, fmt.Errorf("Bucket.Create: %w", err)
1480 }
1481 attrs, err := veneerClient.Bucket(bucket).Attrs(ctx)
1482 if err != nil {
1483 return "", 0, fmt.Errorf("Bucket.Attrs: %w", err)
1484 }
1485 return bucket, attrs.MetaGeneration, nil
1486 }
1487
1488
1489
1490
1491
1492 func transportClientTest(t *testing.T, test func(*testing.T, string, string, storageClient)) {
1493 checkEmulatorEnvironment(t)
1494
1495 for transport, client := range emulatorClients {
1496 t.Run(transport, func(t *testing.T) {
1497 project := fmt.Sprintf("%s-project", transport)
1498 bucket := fmt.Sprintf("%s-bucket-%d", transport, time.Now().Nanosecond())
1499 test(t, project, bucket, client)
1500 })
1501 }
1502 }
1503
1504
1505
1506 func checkEmulatorEnvironment(t *testing.T) {
1507 if !isEmulatorEnvironmentSet() {
1508 t.Skip("Emulator tests skipped without emulator environment variables set")
1509 }
1510 }
1511
1512
1513 func isEmulatorEnvironmentSet() bool {
1514 return os.Getenv("STORAGE_EMULATOR_HOST_GRPC") != "" && os.Getenv("STORAGE_EMULATOR_HOST") != ""
1515 }
1516
View as plain text