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