1// Copyright 2017 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package storage
16
17import (
18	"context"
19	"net/http"
20	"reflect"
21	"testing"
22	"time"
23
24	"cloud.google.com/go/internal/testutil"
25	"github.com/google/go-cmp/cmp"
26	"google.golang.org/api/googleapi"
27	raw "google.golang.org/api/storage/v1"
28)
29
30func TestBucketAttrsToRawBucket(t *testing.T) {
31	t.Parallel()
32	attrs := &BucketAttrs{
33		Name: "name",
34		ACL:  []ACLRule{{Entity: "bob@example.com", Role: RoleOwner, Domain: "d", Email: "e"}},
35		DefaultObjectACL: []ACLRule{{Entity: AllUsers, Role: RoleReader, EntityID: "eid",
36			ProjectTeam: &ProjectTeam{ProjectNumber: "17", Team: "t"}}},
37		Etag:         "Zkyw9ACJZUvcYmlFaKGChzhmtnE/dt1zHSfweiWpwzdGsqXwuJZqiD0",
38		Location:     "loc",
39		StorageClass: "class",
40		RetentionPolicy: &RetentionPolicy{
41			RetentionPeriod: 3 * time.Second,
42		},
43		BucketPolicyOnly:         BucketPolicyOnly{Enabled: true},
44		UniformBucketLevelAccess: UniformBucketLevelAccess{Enabled: true},
45		VersioningEnabled:        false,
46		// should be ignored:
47		MetaGeneration: 39,
48		Created:        time.Now(),
49		Labels:         map[string]string{"label": "value"},
50		CORS: []CORS{
51			{
52				MaxAge:          time.Hour,
53				Methods:         []string{"GET", "POST"},
54				Origins:         []string{"*"},
55				ResponseHeaders: []string{"FOO"},
56			},
57		},
58		Encryption: &BucketEncryption{DefaultKMSKeyName: "key"},
59		Logging:    &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
60		Website:    &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
61		Lifecycle: Lifecycle{
62			Rules: []LifecycleRule{{
63				Action: LifecycleAction{
64					Type:         SetStorageClassAction,
65					StorageClass: "NEARLINE",
66				},
67				Condition: LifecycleCondition{
68					AgeInDays:             10,
69					Liveness:              Live,
70					CreatedBefore:         time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC),
71					MatchesStorageClasses: []string{"STANDARD"},
72					NumNewerVersions:      3,
73				},
74			}, {
75				Action: LifecycleAction{
76					Type: DeleteAction,
77				},
78				Condition: LifecycleCondition{
79					AgeInDays:             30,
80					Liveness:              Live,
81					CreatedBefore:         time.Date(2017, 1, 2, 3, 4, 5, 6, time.UTC),
82					MatchesStorageClasses: []string{"NEARLINE"},
83					NumNewerVersions:      10,
84				},
85			}, {
86				Action: LifecycleAction{
87					Type: DeleteAction,
88				},
89				Condition: LifecycleCondition{
90					Liveness: Archived,
91				},
92			}},
93		},
94	}
95	got := attrs.toRawBucket()
96	want := &raw.Bucket{
97		Name: "name",
98		Acl: []*raw.BucketAccessControl{
99			{Entity: "bob@example.com", Role: "OWNER"}, // other fields ignored on create/update
100		},
101		DefaultObjectAcl: []*raw.ObjectAccessControl{
102			{Entity: "allUsers", Role: "READER"}, // other fields ignored on create/update
103		},
104		Location:     "loc",
105		StorageClass: "class",
106		RetentionPolicy: &raw.BucketRetentionPolicy{
107			RetentionPeriod: 3,
108		},
109		IamConfiguration: &raw.BucketIamConfiguration{
110			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
111				Enabled: true,
112			},
113		},
114		Versioning: nil, // ignore VersioningEnabled if false
115		Labels:     map[string]string{"label": "value"},
116		Cors: []*raw.BucketCors{
117			{
118				MaxAgeSeconds:  3600,
119				Method:         []string{"GET", "POST"},
120				Origin:         []string{"*"},
121				ResponseHeader: []string{"FOO"},
122			},
123		},
124		Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key"},
125		Logging:    &raw.BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
126		Website:    &raw.BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
127		Lifecycle: &raw.BucketLifecycle{
128			Rule: []*raw.BucketLifecycleRule{{
129				Action: &raw.BucketLifecycleRuleAction{
130					Type:         SetStorageClassAction,
131					StorageClass: "NEARLINE",
132				},
133				Condition: &raw.BucketLifecycleRuleCondition{
134					Age:                 10,
135					IsLive:              googleapi.Bool(true),
136					CreatedBefore:       "2017-01-02",
137					MatchesStorageClass: []string{"STANDARD"},
138					NumNewerVersions:    3,
139				},
140			}, {
141				Action: &raw.BucketLifecycleRuleAction{
142					Type: DeleteAction,
143				},
144				Condition: &raw.BucketLifecycleRuleCondition{
145					Age:                 30,
146					IsLive:              googleapi.Bool(true),
147					CreatedBefore:       "2017-01-02",
148					MatchesStorageClass: []string{"NEARLINE"},
149					NumNewerVersions:    10,
150				},
151			}, {
152				Action: &raw.BucketLifecycleRuleAction{
153					Type: DeleteAction,
154				},
155				Condition: &raw.BucketLifecycleRuleCondition{
156					IsLive: googleapi.Bool(false),
157				},
158			}},
159		},
160	}
161	if msg := testutil.Diff(got, want); msg != "" {
162		t.Error(msg)
163	}
164
165	attrs.VersioningEnabled = true
166	attrs.RequesterPays = true
167	got = attrs.toRawBucket()
168	want.Versioning = &raw.BucketVersioning{Enabled: true}
169	want.Billing = &raw.BucketBilling{RequesterPays: true}
170	if msg := testutil.Diff(got, want); msg != "" {
171		t.Error(msg)
172	}
173
174	// Test that setting either of BucketPolicyOnly or UniformBucketLevelAccess
175	// will enable UniformBucketLevelAccess.
176	// Set UBLA.Enabled = true --> UBLA should be set to enabled in the proto.
177	attrs.BucketPolicyOnly = BucketPolicyOnly{}
178	attrs.UniformBucketLevelAccess = UniformBucketLevelAccess{Enabled: true}
179	got = attrs.toRawBucket()
180	want.IamConfiguration = &raw.BucketIamConfiguration{
181		UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
182			Enabled: true,
183		},
184	}
185	if msg := testutil.Diff(got, want); msg != "" {
186		t.Errorf(msg)
187	}
188
189	// Set BucketPolicyOnly.Enabled = true --> UBLA should be set to enabled in
190	// the proto.
191	attrs.BucketPolicyOnly = BucketPolicyOnly{Enabled: true}
192	attrs.UniformBucketLevelAccess = UniformBucketLevelAccess{}
193	got = attrs.toRawBucket()
194	want.IamConfiguration = &raw.BucketIamConfiguration{
195		UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
196			Enabled: true,
197		},
198	}
199	if msg := testutil.Diff(got, want); msg != "" {
200		t.Errorf(msg)
201	}
202
203	// Set both BucketPolicyOnly.Enabled = true and
204	// UniformBucketLevelAccess.Enabled=true --> UBLA should be set to enabled
205	// in the proto.
206	attrs.BucketPolicyOnly = BucketPolicyOnly{Enabled: true}
207	attrs.UniformBucketLevelAccess = UniformBucketLevelAccess{Enabled: true}
208	got = attrs.toRawBucket()
209	want.IamConfiguration = &raw.BucketIamConfiguration{
210		UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
211			Enabled: true,
212		},
213	}
214	if msg := testutil.Diff(got, want); msg != "" {
215		t.Errorf(msg)
216	}
217
218	// Set UBLA.Enabled=false and BucketPolicyOnly.Enabled=false --> UBLA
219	// should be disabled in the proto.
220	attrs.BucketPolicyOnly = BucketPolicyOnly{}
221	attrs.UniformBucketLevelAccess = UniformBucketLevelAccess{}
222	got = attrs.toRawBucket()
223	want.IamConfiguration = nil
224	if msg := testutil.Diff(got, want); msg != "" {
225		t.Errorf(msg)
226	}
227}
228
229func TestBucketAttrsToUpdateToRawBucket(t *testing.T) {
230	t.Parallel()
231	au := &BucketAttrsToUpdate{
232		VersioningEnabled:        false,
233		RequesterPays:            false,
234		BucketPolicyOnly:         &BucketPolicyOnly{Enabled: false},
235		UniformBucketLevelAccess: &UniformBucketLevelAccess{Enabled: false},
236		DefaultEventBasedHold:    false,
237		RetentionPolicy:          &RetentionPolicy{RetentionPeriod: time.Hour},
238		Encryption:               &BucketEncryption{DefaultKMSKeyName: "key2"},
239		Lifecycle: &Lifecycle{
240			Rules: []LifecycleRule{
241				{
242					Action:    LifecycleAction{Type: "Delete"},
243					Condition: LifecycleCondition{AgeInDays: 30},
244				},
245			},
246		},
247		Logging: &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
248		Website: &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
249	}
250	au.SetLabel("a", "foo")
251	au.DeleteLabel("b")
252	au.SetLabel("c", "")
253	got := au.toRawBucket()
254	want := &raw.Bucket{
255		Versioning: &raw.BucketVersioning{
256			Enabled:         false,
257			ForceSendFields: []string{"Enabled"},
258		},
259		Labels: map[string]string{
260			"a": "foo",
261			"c": "",
262		},
263		Billing: &raw.BucketBilling{
264			RequesterPays:   false,
265			ForceSendFields: []string{"RequesterPays"},
266		},
267		DefaultEventBasedHold: false,
268		RetentionPolicy:       &raw.BucketRetentionPolicy{RetentionPeriod: 3600},
269		IamConfiguration: &raw.BucketIamConfiguration{
270			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
271				Enabled:         false,
272				ForceSendFields: []string{"Enabled"},
273			},
274		},
275		Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key2"},
276		NullFields: []string{"Labels.b"},
277		Lifecycle: &raw.BucketLifecycle{
278			Rule: []*raw.BucketLifecycleRule{
279				{
280					Action:    &raw.BucketLifecycleRuleAction{Type: "Delete"},
281					Condition: &raw.BucketLifecycleRuleCondition{Age: 30},
282				},
283			},
284		},
285		Logging:         &raw.BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
286		Website:         &raw.BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
287		ForceSendFields: []string{"DefaultEventBasedHold"},
288	}
289	if msg := testutil.Diff(got, want); msg != "" {
290		t.Error(msg)
291	}
292
293	var au2 BucketAttrsToUpdate
294	au2.DeleteLabel("b")
295	got = au2.toRawBucket()
296	want = &raw.Bucket{
297		Labels:          map[string]string{},
298		ForceSendFields: []string{"Labels"},
299		NullFields:      []string{"Labels.b"},
300	}
301	if msg := testutil.Diff(got, want); msg != "" {
302		t.Error(msg)
303	}
304
305	// Test nulls.
306	au3 := &BucketAttrsToUpdate{
307		RetentionPolicy: &RetentionPolicy{},
308		Encryption:      &BucketEncryption{},
309		Logging:         &BucketLogging{},
310		Website:         &BucketWebsite{},
311	}
312	got = au3.toRawBucket()
313	want = &raw.Bucket{
314		NullFields: []string{"RetentionPolicy", "Encryption", "Logging", "Website"},
315	}
316	if msg := testutil.Diff(got, want); msg != "" {
317		t.Error(msg)
318	}
319
320	// Test that setting either of BucketPolicyOnly or UniformBucketLevelAccess
321	// will enable UniformBucketLevelAccess.
322	// Set UBLA.Enabled = true --> UBLA should be set to enabled in the proto.
323	au4 := &BucketAttrsToUpdate{
324		UniformBucketLevelAccess: &UniformBucketLevelAccess{Enabled: true},
325	}
326	got = au4.toRawBucket()
327	want = &raw.Bucket{
328		IamConfiguration: &raw.BucketIamConfiguration{
329			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
330				Enabled:         true,
331				ForceSendFields: []string{"Enabled"},
332			},
333		},
334	}
335	if msg := testutil.Diff(got, want); msg != "" {
336		t.Errorf(msg)
337	}
338
339	// Set BucketPolicyOnly.Enabled = true --> UBLA should be set to enabled in
340	// the proto.
341	au5 := &BucketAttrsToUpdate{
342		BucketPolicyOnly: &BucketPolicyOnly{Enabled: true},
343	}
344	got = au5.toRawBucket()
345	want = &raw.Bucket{
346		IamConfiguration: &raw.BucketIamConfiguration{
347			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
348				Enabled:         true,
349				ForceSendFields: []string{"Enabled"},
350			},
351		},
352	}
353	if msg := testutil.Diff(got, want); msg != "" {
354		t.Errorf(msg)
355	}
356
357	// Set both BucketPolicyOnly.Enabled = true and
358	// UniformBucketLevelAccess.Enabled=true --> UBLA should be set to enabled
359	// in the proto.
360	au6 := &BucketAttrsToUpdate{
361		BucketPolicyOnly:         &BucketPolicyOnly{Enabled: true},
362		UniformBucketLevelAccess: &UniformBucketLevelAccess{Enabled: true},
363	}
364	got = au6.toRawBucket()
365	want = &raw.Bucket{
366		IamConfiguration: &raw.BucketIamConfiguration{
367			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
368				Enabled:         true,
369				ForceSendFields: []string{"Enabled"},
370			},
371		},
372	}
373	if msg := testutil.Diff(got, want); msg != "" {
374		t.Errorf(msg)
375	}
376
377	// Set UBLA.Enabled=false and BucketPolicyOnly.Enabled=false --> UBLA
378	// should be disabled in the proto.
379	au7 := &BucketAttrsToUpdate{
380		BucketPolicyOnly:         &BucketPolicyOnly{Enabled: false},
381		UniformBucketLevelAccess: &UniformBucketLevelAccess{Enabled: false},
382	}
383	got = au7.toRawBucket()
384	want = &raw.Bucket{
385		IamConfiguration: &raw.BucketIamConfiguration{
386			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
387				Enabled:         false,
388				ForceSendFields: []string{"Enabled"},
389			},
390		},
391	}
392	if msg := testutil.Diff(got, want); msg != "" {
393		t.Errorf(msg)
394	}
395
396	// UBLA.Enabled will have precedence above BucketPolicyOnly.Enabled if both
397	// are set with different values.
398	au8 := &BucketAttrsToUpdate{
399		BucketPolicyOnly:         &BucketPolicyOnly{Enabled: true},
400		UniformBucketLevelAccess: &UniformBucketLevelAccess{Enabled: false},
401	}
402	got = au8.toRawBucket()
403	want = &raw.Bucket{
404		IamConfiguration: &raw.BucketIamConfiguration{
405			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
406				Enabled:         false,
407				ForceSendFields: []string{"Enabled"},
408			},
409		},
410	}
411	if msg := testutil.Diff(got, want); msg != "" {
412		t.Errorf(msg)
413	}
414}
415
416func TestCallBuilders(t *testing.T) {
417	rc, err := raw.NewService(context.Background())
418	if err != nil {
419		t.Fatal(err)
420	}
421	c := &Client{raw: rc}
422	const metagen = 17
423
424	b := c.Bucket("name")
425	bm := b.If(BucketConditions{MetagenerationMatch: metagen}).UserProject("p")
426
427	equal := func(x, y interface{}) bool {
428		return testutil.Equal(x, y,
429			cmp.AllowUnexported(
430				raw.BucketsGetCall{},
431				raw.BucketsDeleteCall{},
432				raw.BucketsPatchCall{},
433			),
434			cmp.FilterPath(func(p cmp.Path) bool {
435				return p[len(p)-1].Type() == reflect.TypeOf(&raw.Service{})
436			}, cmp.Ignore()),
437		)
438	}
439
440	for i, test := range []struct {
441		callFunc func(*BucketHandle) (interface{}, error)
442		want     interface {
443			Header() http.Header
444		}
445		metagenFunc func(interface{})
446	}{
447		{
448			func(b *BucketHandle) (interface{}, error) { return b.newGetCall() },
449			rc.Buckets.Get("name").Projection("full"),
450			func(req interface{}) { req.(*raw.BucketsGetCall).IfMetagenerationMatch(metagen).UserProject("p") },
451		},
452		{
453			func(b *BucketHandle) (interface{}, error) { return b.newDeleteCall() },
454			rc.Buckets.Delete("name"),
455			func(req interface{}) { req.(*raw.BucketsDeleteCall).IfMetagenerationMatch(metagen).UserProject("p") },
456		},
457		{
458			func(b *BucketHandle) (interface{}, error) {
459				return b.newPatchCall(&BucketAttrsToUpdate{
460					VersioningEnabled: false,
461					RequesterPays:     false,
462				})
463			},
464			rc.Buckets.Patch("name", &raw.Bucket{
465				Versioning: &raw.BucketVersioning{
466					Enabled:         false,
467					ForceSendFields: []string{"Enabled"},
468				},
469				Billing: &raw.BucketBilling{
470					RequesterPays:   false,
471					ForceSendFields: []string{"RequesterPays"},
472				},
473			}).Projection("full"),
474			func(req interface{}) { req.(*raw.BucketsPatchCall).IfMetagenerationMatch(metagen).UserProject("p") },
475		},
476	} {
477		got, err := test.callFunc(b)
478		if err != nil {
479			t.Fatal(err)
480		}
481		setClientHeader(test.want.Header())
482		if !equal(got, test.want) {
483			t.Errorf("#%d: got %#v, want %#v", i, got, test.want)
484		}
485		got, err = test.callFunc(bm)
486		if err != nil {
487			t.Fatal(err)
488		}
489		test.metagenFunc(test.want)
490		if !equal(got, test.want) {
491			t.Errorf("#%d:\ngot  %#v\nwant %#v", i, got, test.want)
492		}
493	}
494
495	// Error.
496	bm = b.If(BucketConditions{MetagenerationMatch: 1, MetagenerationNotMatch: 2})
497	if _, err := bm.newGetCall(); err == nil {
498		t.Errorf("got nil, want error")
499	}
500	if _, err := bm.newDeleteCall(); err == nil {
501		t.Errorf("got nil, want error")
502	}
503	if _, err := bm.newPatchCall(&BucketAttrsToUpdate{}); err == nil {
504		t.Errorf("got nil, want error")
505	}
506}
507
508func TestNewBucket(t *testing.T) {
509	labels := map[string]string{"a": "b"}
510	matchClasses := []string{"STANDARD"}
511	aTime := time.Date(2017, 1, 2, 0, 0, 0, 0, time.UTC)
512	rb := &raw.Bucket{
513		Name:                  "name",
514		Location:              "loc",
515		DefaultEventBasedHold: true,
516		Metageneration:        3,
517		StorageClass:          "sc",
518		TimeCreated:           "2017-10-23T04:05:06Z",
519		Versioning:            &raw.BucketVersioning{Enabled: true},
520		Labels:                labels,
521		Billing:               &raw.BucketBilling{RequesterPays: true},
522		Etag:                  "Zkyw9ACJZUvcYmlFaKGChzhmtnE/dt1zHSfweiWpwzdGsqXwuJZqiD0",
523		Lifecycle: &raw.BucketLifecycle{
524			Rule: []*raw.BucketLifecycleRule{{
525				Action: &raw.BucketLifecycleRuleAction{
526					Type:         "SetStorageClass",
527					StorageClass: "NEARLINE",
528				},
529				Condition: &raw.BucketLifecycleRuleCondition{
530					Age:                 10,
531					IsLive:              googleapi.Bool(true),
532					CreatedBefore:       "2017-01-02",
533					MatchesStorageClass: matchClasses,
534					NumNewerVersions:    3,
535				},
536			}},
537		},
538		RetentionPolicy: &raw.BucketRetentionPolicy{
539			RetentionPeriod: 3,
540			EffectiveTime:   aTime.Format(time.RFC3339),
541		},
542		IamConfiguration: &raw.BucketIamConfiguration{
543			BucketPolicyOnly: &raw.BucketIamConfigurationBucketPolicyOnly{
544				Enabled:    true,
545				LockedTime: aTime.Format(time.RFC3339),
546			},
547			UniformBucketLevelAccess: &raw.BucketIamConfigurationUniformBucketLevelAccess{
548				Enabled:    true,
549				LockedTime: aTime.Format(time.RFC3339),
550			},
551		},
552		Cors: []*raw.BucketCors{
553			{
554				MaxAgeSeconds:  3600,
555				Method:         []string{"GET", "POST"},
556				Origin:         []string{"*"},
557				ResponseHeader: []string{"FOO"},
558			},
559		},
560		Acl: []*raw.BucketAccessControl{
561			{Bucket: "name", Role: "READER", Email: "joe@example.com", Entity: "allUsers"},
562		},
563		LocationType: "dual-region",
564		Encryption:   &raw.BucketEncryption{DefaultKmsKeyName: "key"},
565		Logging:      &raw.BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
566		Website:      &raw.BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
567	}
568	want := &BucketAttrs{
569		Name:                  "name",
570		Location:              "loc",
571		DefaultEventBasedHold: true,
572		MetaGeneration:        3,
573		StorageClass:          "sc",
574		Created:               time.Date(2017, 10, 23, 4, 5, 6, 0, time.UTC),
575		VersioningEnabled:     true,
576		Labels:                labels,
577		Etag:                  "Zkyw9ACJZUvcYmlFaKGChzhmtnE/dt1zHSfweiWpwzdGsqXwuJZqiD0",
578		RequesterPays:         true,
579		Lifecycle: Lifecycle{
580			Rules: []LifecycleRule{
581				{
582					Action: LifecycleAction{
583						Type:         SetStorageClassAction,
584						StorageClass: "NEARLINE",
585					},
586					Condition: LifecycleCondition{
587						AgeInDays:             10,
588						Liveness:              Live,
589						CreatedBefore:         time.Date(2017, 1, 2, 0, 0, 0, 0, time.UTC),
590						MatchesStorageClasses: matchClasses,
591						NumNewerVersions:      3,
592					},
593				},
594			},
595		},
596		RetentionPolicy: &RetentionPolicy{
597			EffectiveTime:   aTime,
598			RetentionPeriod: 3 * time.Second,
599		},
600		BucketPolicyOnly:         BucketPolicyOnly{Enabled: true, LockedTime: aTime},
601		UniformBucketLevelAccess: UniformBucketLevelAccess{Enabled: true, LockedTime: aTime},
602		CORS: []CORS{
603			{
604				MaxAge:          time.Hour,
605				Methods:         []string{"GET", "POST"},
606				Origins:         []string{"*"},
607				ResponseHeaders: []string{"FOO"},
608			},
609		},
610		Encryption:       &BucketEncryption{DefaultKMSKeyName: "key"},
611		Logging:          &BucketLogging{LogBucket: "lb", LogObjectPrefix: "p"},
612		Website:          &BucketWebsite{MainPageSuffix: "mps", NotFoundPage: "404"},
613		ACL:              []ACLRule{{Entity: "allUsers", Role: RoleReader, Email: "joe@example.com"}},
614		DefaultObjectACL: nil,
615		LocationType:     "dual-region",
616	}
617	got, err := newBucket(rb)
618	if err != nil {
619		t.Fatal(err)
620	}
621	if diff := testutil.Diff(got, want); diff != "" {
622		t.Errorf("got=-, want=+:\n%s", diff)
623	}
624}
625