You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

356 lines
9.7 KiB

  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. package storage
  15. import (
  16. "net/http"
  17. "reflect"
  18. "testing"
  19. "time"
  20. "cloud.google.com/go/internal/testutil"
  21. "github.com/google/go-cmp/cmp"
  22. "github.com/google/go-cmp/cmp/cmpopts"
  23. "google.golang.org/api/googleapi"
  24. raw "google.golang.org/api/storage/v1"
  25. )
  26. func TestBucketAttrsToRawBucket(t *testing.T) {
  27. t.Parallel()
  28. attrs := &BucketAttrs{
  29. Name: "name",
  30. ACL: []ACLRule{{Entity: "bob@example.com", Role: RoleOwner}},
  31. DefaultObjectACL: []ACLRule{{Entity: AllUsers, Role: RoleReader}},
  32. Location: "loc",
  33. StorageClass: "class",
  34. RetentionPolicy: &RetentionPolicy{
  35. RetentionPeriod: 3 * time.Second,
  36. },
  37. VersioningEnabled: false,
  38. // should be ignored:
  39. MetaGeneration: 39,
  40. Created: time.Now(),
  41. Labels: map[string]string{"label": "value"},
  42. CORS: []CORS{
  43. {
  44. MaxAge: time.Hour,
  45. Methods: []string{"GET", "POST"},
  46. Origins: []string{"*"},
  47. ResponseHeaders: []string{"FOO"},
  48. },
  49. },
  50. Encryption: &BucketEncryption{DefaultKMSKeyName: "key"},
  51. }
  52. got := attrs.toRawBucket()
  53. want := &raw.Bucket{
  54. Name: "name",
  55. Acl: []*raw.BucketAccessControl{
  56. {Entity: "bob@example.com", Role: "OWNER"},
  57. },
  58. DefaultObjectAcl: []*raw.ObjectAccessControl{
  59. {Entity: "allUsers", Role: "READER"},
  60. },
  61. Location: "loc",
  62. StorageClass: "class",
  63. RetentionPolicy: &raw.BucketRetentionPolicy{
  64. RetentionPeriod: 3,
  65. },
  66. Versioning: nil, // ignore VersioningEnabled if false
  67. Labels: map[string]string{"label": "value"},
  68. Cors: []*raw.BucketCors{
  69. {
  70. MaxAgeSeconds: 3600,
  71. Method: []string{"GET", "POST"},
  72. Origin: []string{"*"},
  73. ResponseHeader: []string{"FOO"},
  74. },
  75. },
  76. Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key"},
  77. }
  78. if msg := testutil.Diff(got, want); msg != "" {
  79. t.Error(msg)
  80. }
  81. attrs.VersioningEnabled = true
  82. attrs.RequesterPays = true
  83. got = attrs.toRawBucket()
  84. want.Versioning = &raw.BucketVersioning{Enabled: true}
  85. want.Billing = &raw.BucketBilling{RequesterPays: true}
  86. if msg := testutil.Diff(got, want); msg != "" {
  87. t.Error(msg)
  88. }
  89. }
  90. func TestBucketAttrsToUpdateToRawBucket(t *testing.T) {
  91. t.Parallel()
  92. au := &BucketAttrsToUpdate{
  93. VersioningEnabled: false,
  94. RequesterPays: false,
  95. RetentionPolicy: &RetentionPolicy{RetentionPeriod: time.Hour},
  96. Encryption: &BucketEncryption{DefaultKMSKeyName: "key2"},
  97. Lifecycle: &Lifecycle{
  98. Rules: []LifecycleRule{
  99. {
  100. Action: LifecycleAction{Type: "Delete"},
  101. Condition: LifecycleCondition{AgeInDays: 30},
  102. },
  103. },
  104. },
  105. }
  106. au.SetLabel("a", "foo")
  107. au.DeleteLabel("b")
  108. au.SetLabel("c", "")
  109. got := au.toRawBucket()
  110. want := &raw.Bucket{
  111. Versioning: &raw.BucketVersioning{
  112. Enabled: false,
  113. ForceSendFields: []string{"Enabled"},
  114. },
  115. Labels: map[string]string{
  116. "a": "foo",
  117. "c": "",
  118. },
  119. Billing: &raw.BucketBilling{
  120. RequesterPays: false,
  121. ForceSendFields: []string{"RequesterPays"},
  122. },
  123. RetentionPolicy: &raw.BucketRetentionPolicy{RetentionPeriod: 3600},
  124. Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key2"},
  125. NullFields: []string{"Labels.b"},
  126. Lifecycle: &raw.BucketLifecycle{
  127. Rule: []*raw.BucketLifecycleRule{
  128. {
  129. Action: &raw.BucketLifecycleRuleAction{Type: "Delete"},
  130. Condition: &raw.BucketLifecycleRuleCondition{Age: 30},
  131. },
  132. },
  133. },
  134. }
  135. if msg := testutil.Diff(got, want); msg != "" {
  136. t.Error(msg)
  137. }
  138. var au2 BucketAttrsToUpdate
  139. au2.DeleteLabel("b")
  140. got = au2.toRawBucket()
  141. want = &raw.Bucket{
  142. Labels: map[string]string{},
  143. ForceSendFields: []string{"Labels"},
  144. NullFields: []string{"Labels.b"},
  145. }
  146. if msg := testutil.Diff(got, want); msg != "" {
  147. t.Error(msg)
  148. }
  149. // Test nulls.
  150. au3 := &BucketAttrsToUpdate{
  151. RetentionPolicy: &RetentionPolicy{},
  152. Encryption: &BucketEncryption{},
  153. }
  154. got = au3.toRawBucket()
  155. want = &raw.Bucket{
  156. NullFields: []string{"RetentionPolicy", "Encryption"},
  157. }
  158. if msg := testutil.Diff(got, want); msg != "" {
  159. t.Error(msg)
  160. }
  161. }
  162. func TestCallBuilders(t *testing.T) {
  163. rc, err := raw.New(&http.Client{})
  164. if err != nil {
  165. t.Fatal(err)
  166. }
  167. c := &Client{raw: rc}
  168. const metagen = 17
  169. b := c.Bucket("name")
  170. bm := b.If(BucketConditions{MetagenerationMatch: metagen}).UserProject("p")
  171. equal := func(x, y interface{}) bool {
  172. return testutil.Equal(x, y,
  173. cmp.AllowUnexported(
  174. raw.BucketsGetCall{},
  175. raw.BucketsDeleteCall{},
  176. raw.BucketsPatchCall{},
  177. ),
  178. cmp.FilterPath(func(p cmp.Path) bool {
  179. return p[len(p)-1].Type() == reflect.TypeOf(&raw.Service{})
  180. }, cmp.Ignore()),
  181. )
  182. }
  183. for i, test := range []struct {
  184. callFunc func(*BucketHandle) (interface{}, error)
  185. want interface {
  186. Header() http.Header
  187. }
  188. metagenFunc func(interface{})
  189. }{
  190. {
  191. func(b *BucketHandle) (interface{}, error) { return b.newGetCall() },
  192. rc.Buckets.Get("name").Projection("full"),
  193. func(req interface{}) { req.(*raw.BucketsGetCall).IfMetagenerationMatch(metagen).UserProject("p") },
  194. },
  195. {
  196. func(b *BucketHandle) (interface{}, error) { return b.newDeleteCall() },
  197. rc.Buckets.Delete("name"),
  198. func(req interface{}) { req.(*raw.BucketsDeleteCall).IfMetagenerationMatch(metagen).UserProject("p") },
  199. },
  200. {
  201. func(b *BucketHandle) (interface{}, error) {
  202. return b.newPatchCall(&BucketAttrsToUpdate{
  203. VersioningEnabled: false,
  204. RequesterPays: false,
  205. })
  206. },
  207. rc.Buckets.Patch("name", &raw.Bucket{
  208. Versioning: &raw.BucketVersioning{
  209. Enabled: false,
  210. ForceSendFields: []string{"Enabled"},
  211. },
  212. Billing: &raw.BucketBilling{
  213. RequesterPays: false,
  214. ForceSendFields: []string{"RequesterPays"},
  215. },
  216. }).Projection("full"),
  217. func(req interface{}) { req.(*raw.BucketsPatchCall).IfMetagenerationMatch(metagen).UserProject("p") },
  218. },
  219. } {
  220. got, err := test.callFunc(b)
  221. if err != nil {
  222. t.Fatal(err)
  223. }
  224. setClientHeader(test.want.Header())
  225. if !equal(got, test.want) {
  226. t.Errorf("#%d: got %#v, want %#v", i, got, test.want)
  227. }
  228. got, err = test.callFunc(bm)
  229. if err != nil {
  230. t.Fatal(err)
  231. }
  232. test.metagenFunc(test.want)
  233. if !equal(got, test.want) {
  234. t.Errorf("#%d:\ngot %#v\nwant %#v", i, got, test.want)
  235. }
  236. }
  237. // Error.
  238. bm = b.If(BucketConditions{MetagenerationMatch: 1, MetagenerationNotMatch: 2})
  239. if _, err := bm.newGetCall(); err == nil {
  240. t.Errorf("got nil, want error")
  241. }
  242. if _, err := bm.newDeleteCall(); err == nil {
  243. t.Errorf("got nil, want error")
  244. }
  245. if _, err := bm.newPatchCall(&BucketAttrsToUpdate{}); err == nil {
  246. t.Errorf("got nil, want error")
  247. }
  248. }
  249. func TestNewBucket(t *testing.T) {
  250. labels := map[string]string{"a": "b"}
  251. matchClasses := []string{"MULTI_REGIONAL", "REGIONAL", "STANDARD"}
  252. rb := &raw.Bucket{
  253. Name: "name",
  254. Location: "loc",
  255. Metageneration: 3,
  256. StorageClass: "sc",
  257. TimeCreated: "2017-10-23T04:05:06Z",
  258. Versioning: &raw.BucketVersioning{Enabled: true},
  259. Labels: labels,
  260. Billing: &raw.BucketBilling{RequesterPays: true},
  261. Lifecycle: &raw.BucketLifecycle{
  262. Rule: []*raw.BucketLifecycleRule{{
  263. Action: &raw.BucketLifecycleRuleAction{
  264. Type: "SetStorageClass",
  265. StorageClass: "NEARLINE",
  266. },
  267. Condition: &raw.BucketLifecycleRuleCondition{
  268. Age: 10,
  269. IsLive: googleapi.Bool(true),
  270. CreatedBefore: "2017-01-02",
  271. MatchesStorageClass: matchClasses,
  272. NumNewerVersions: 3,
  273. },
  274. }},
  275. },
  276. RetentionPolicy: &raw.BucketRetentionPolicy{
  277. RetentionPeriod: 3,
  278. EffectiveTime: time.Now().Format(time.RFC3339),
  279. },
  280. Cors: []*raw.BucketCors{
  281. {
  282. MaxAgeSeconds: 3600,
  283. Method: []string{"GET", "POST"},
  284. Origin: []string{"*"},
  285. ResponseHeader: []string{"FOO"},
  286. },
  287. },
  288. Acl: []*raw.BucketAccessControl{
  289. {Bucket: "name", Role: "READER", Email: "joe@example.com", Entity: "allUsers"},
  290. },
  291. Encryption: &raw.BucketEncryption{DefaultKmsKeyName: "key"},
  292. }
  293. want := &BucketAttrs{
  294. Name: "name",
  295. Location: "loc",
  296. MetaGeneration: 3,
  297. StorageClass: "sc",
  298. Created: time.Date(2017, 10, 23, 4, 5, 6, 0, time.UTC),
  299. VersioningEnabled: true,
  300. Labels: labels,
  301. RequesterPays: true,
  302. Lifecycle: Lifecycle{
  303. Rules: []LifecycleRule{
  304. {
  305. Action: LifecycleAction{
  306. Type: SetStorageClassAction,
  307. StorageClass: "NEARLINE",
  308. },
  309. Condition: LifecycleCondition{
  310. AgeInDays: 10,
  311. Liveness: Live,
  312. CreatedBefore: time.Date(2017, 1, 2, 0, 0, 0, 0, time.UTC),
  313. MatchesStorageClasses: matchClasses,
  314. NumNewerVersions: 3,
  315. },
  316. },
  317. },
  318. },
  319. RetentionPolicy: &RetentionPolicy{
  320. RetentionPeriod: 3 * time.Second,
  321. },
  322. CORS: []CORS{
  323. {
  324. MaxAge: time.Hour,
  325. Methods: []string{"GET", "POST"},
  326. Origins: []string{"*"},
  327. ResponseHeaders: []string{"FOO"},
  328. },
  329. },
  330. Encryption: &BucketEncryption{DefaultKMSKeyName: "key"},
  331. ACL: []ACLRule{{Entity: "allUsers", Role: RoleReader}},
  332. DefaultObjectACL: []ACLRule{},
  333. }
  334. got, err := newBucket(rb)
  335. if err != nil {
  336. t.Fatal(err)
  337. }
  338. if diff := testutil.Diff(got, want, cmpopts.IgnoreTypes(time.Time{})); diff != "" {
  339. t.Errorf("got=-, want=+:\n%s", diff)
  340. }
  341. }