github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/objectstorage/v1/objects/testing/requests_test.go (about) 1 package testing 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/md5" 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 "testing" 12 "time" 13 14 v1 "github.com/vnpaycloud-console/gophercloud/v2/openstack/objectstorage/v1" 15 accountTesting "github.com/vnpaycloud-console/gophercloud/v2/openstack/objectstorage/v1/accounts/testing" 16 containerTesting "github.com/vnpaycloud-console/gophercloud/v2/openstack/objectstorage/v1/containers/testing" 17 "github.com/vnpaycloud-console/gophercloud/v2/openstack/objectstorage/v1/objects" 18 "github.com/vnpaycloud-console/gophercloud/v2/pagination" 19 th "github.com/vnpaycloud-console/gophercloud/v2/testhelper" 20 fake "github.com/vnpaycloud-console/gophercloud/v2/testhelper/client" 21 ) 22 23 func TestContainerNames(t *testing.T) { 24 for _, tc := range [...]struct { 25 name string 26 containerName string 27 expectedError error 28 }{ 29 { 30 "rejects_a_slash", 31 "one/two", 32 v1.ErrInvalidContainerName{}, 33 }, 34 { 35 "rejects_an_empty_string", 36 "", 37 v1.ErrEmptyContainerName{}, 38 }, 39 } { 40 t.Run(tc.name, func(t *testing.T) { 41 t.Run("list", func(t *testing.T) { 42 th.SetupHTTP() 43 defer th.TeardownHTTP() 44 HandleListObjectsInfoSuccessfully(t, WithPath("/")) 45 46 _, err := objects.List(fake.ServiceClient(), tc.containerName, nil).AllPages(context.TODO()) 47 th.CheckErr(t, err, &tc.expectedError) 48 }) 49 t.Run("download", func(t *testing.T) { 50 th.SetupHTTP() 51 defer th.TeardownHTTP() 52 HandleDownloadObjectSuccessfully(t, WithPath("/")) 53 54 _, err := objects.Download(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil).Extract() 55 th.CheckErr(t, err, &tc.expectedError) 56 }) 57 t.Run("create", func(t *testing.T) { 58 th.SetupHTTP() 59 defer th.TeardownHTTP() 60 content := "Ceci n'est pas une pipe" 61 HandleCreateTextObjectSuccessfully(t, content, WithPath("/")) 62 63 res := objects.Create(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", &objects.CreateOpts{ 64 ContentType: "text/plain", 65 Content: strings.NewReader(content), 66 }) 67 th.CheckErr(t, res.Err, &tc.expectedError) 68 }) 69 t.Run("delete", func(t *testing.T) { 70 th.SetupHTTP() 71 defer th.TeardownHTTP() 72 HandleDeleteObjectSuccessfully(t, WithPath("/")) 73 74 res := objects.Delete(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil) 75 th.CheckErr(t, res.Err, &tc.expectedError) 76 }) 77 t.Run("get", func(t *testing.T) { 78 th.SetupHTTP() 79 defer th.TeardownHTTP() 80 HandleGetObjectSuccessfully(t, WithPath("/")) 81 82 _, err := objects.Get(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", nil).ExtractMetadata() 83 th.CheckErr(t, err, &tc.expectedError) 84 }) 85 t.Run("update", func(t *testing.T) { 86 th.SetupHTTP() 87 defer th.TeardownHTTP() 88 HandleUpdateObjectSuccessfully(t) 89 90 res := objects.Update(context.TODO(), fake.ServiceClient(), tc.containerName, "testObject", &objects.UpdateOpts{ 91 Metadata: map[string]string{"Gophercloud-Test": "objects"}, 92 }) 93 th.CheckErr(t, res.Err, &tc.expectedError) 94 }) 95 t.Run("createTempURL", func(t *testing.T) { 96 port := 33200 97 th.SetupHTTP() 98 th.SetupPersistentPortHTTP(t, port) 99 defer th.TeardownHTTP() 100 101 // Handle fetching of secret key inside of CreateTempURL 102 containerTesting.HandleGetContainerSuccessfully(t) 103 accountTesting.HandleGetAccountSuccessfully(t) 104 client := fake.ServiceClient() 105 106 // Append v1/ to client endpoint URL to be compliant with tempURL generator 107 client.Endpoint = client.Endpoint + "v1/" 108 _, err := objects.CreateTempURL(context.TODO(), client, tc.containerName, "testObject/testFile.txt", objects.CreateTempURLOpts{ 109 Method: http.MethodGet, 110 TTL: 60, 111 Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), 112 }) 113 114 th.CheckErr(t, err, &tc.expectedError) 115 }) 116 t.Run("bulk-delete", func(t *testing.T) { 117 th.SetupHTTP() 118 defer th.TeardownHTTP() 119 HandleBulkDeleteSuccessfully(t) 120 121 res := objects.BulkDelete(context.TODO(), fake.ServiceClient(), tc.containerName, []string{"testObject"}) 122 th.CheckErr(t, res.Err, &tc.expectedError) 123 }) 124 }) 125 } 126 } 127 128 func TestDownloadReader(t *testing.T) { 129 th.SetupHTTP() 130 defer th.TeardownHTTP() 131 HandleDownloadObjectSuccessfully(t) 132 133 response := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) 134 defer response.Body.Close() 135 136 // Check reader 137 buf := bytes.NewBuffer(make([]byte, 0)) 138 _, err := io.CopyN(buf, response.Body, 10) 139 th.AssertNoErr(t, err) 140 th.CheckEquals(t, "Successful", buf.String()) 141 } 142 143 func TestDownloadExtraction(t *testing.T) { 144 th.SetupHTTP() 145 defer th.TeardownHTTP() 146 HandleDownloadObjectSuccessfully(t) 147 148 response := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) 149 150 // Check []byte extraction 151 bytes, err := response.ExtractContent() 152 th.AssertNoErr(t, err) 153 th.CheckEquals(t, "Successful download with Gophercloud", string(bytes)) 154 155 expected := &objects.DownloadHeader{ 156 ContentLength: 36, 157 ContentType: "text/plain; charset=utf-8", 158 Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 159 StaticLargeObject: true, 160 LastModified: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 161 } 162 actual, err := response.Extract() 163 th.AssertNoErr(t, err) 164 th.CheckDeepEquals(t, expected, actual) 165 } 166 167 func TestDownloadWithLastModified(t *testing.T) { 168 th.SetupHTTP() 169 defer th.TeardownHTTP() 170 HandleDownloadObjectSuccessfully(t) 171 172 options1 := &objects.DownloadOpts{ 173 IfUnmodifiedSince: time.Date(2009, time.November, 10, 22, 59, 59, 0, time.UTC), 174 } 175 response1 := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options1) 176 _, err1 := response1.Extract() 177 th.AssertErr(t, err1) 178 179 options2 := &objects.DownloadOpts{ 180 IfModifiedSince: time.Date(2009, time.November, 10, 23, 0, 1, 0, time.UTC), 181 } 182 response2 := objects.Download(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options2) 183 content, err2 := response2.ExtractContent() 184 th.AssertNoErr(t, err2) 185 th.AssertEquals(t, 0, len(content)) 186 } 187 188 func TestListObjectInfo(t *testing.T) { 189 th.SetupHTTP() 190 defer th.TeardownHTTP() 191 HandleListObjectsInfoSuccessfully(t) 192 193 count := 0 194 options := &objects.ListOpts{} 195 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { 196 count++ 197 actual, err := objects.ExtractInfo(page) 198 th.AssertNoErr(t, err) 199 200 th.CheckDeepEquals(t, ExpectedListInfo, actual) 201 202 return true, nil 203 }) 204 th.AssertNoErr(t, err) 205 th.CheckEquals(t, 1, count) 206 } 207 208 func TestListObjectSubdir(t *testing.T) { 209 th.SetupHTTP() 210 defer th.TeardownHTTP() 211 HandleListSubdirSuccessfully(t) 212 213 count := 0 214 options := &objects.ListOpts{Prefix: "", Delimiter: "/"} 215 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { 216 count++ 217 actual, err := objects.ExtractInfo(page) 218 th.AssertNoErr(t, err) 219 220 th.CheckDeepEquals(t, ExpectedListSubdir, actual) 221 222 return true, nil 223 }) 224 th.AssertNoErr(t, err) 225 th.CheckEquals(t, 1, count) 226 } 227 228 func TestListObjectNames(t *testing.T) { 229 th.SetupHTTP() 230 defer th.TeardownHTTP() 231 HandleListObjectsInfoSuccessfully(t) 232 233 // Check without delimiter. 234 count := 0 235 options := &objects.ListOpts{} 236 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { 237 count++ 238 actual, err := objects.ExtractNames(page) 239 if err != nil { 240 t.Errorf("Failed to extract container names: %v", err) 241 return false, err 242 } 243 244 th.CheckDeepEquals(t, ExpectedListNames, actual) 245 246 return true, nil 247 }) 248 th.AssertNoErr(t, err) 249 th.CheckEquals(t, 1, count) 250 251 // Check with delimiter. 252 count = 0 253 options = &objects.ListOpts{Delimiter: "/"} 254 err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { 255 count++ 256 actual, err := objects.ExtractNames(page) 257 if err != nil { 258 t.Errorf("Failed to extract container names: %v", err) 259 return false, err 260 } 261 262 th.CheckDeepEquals(t, ExpectedListNames, actual) 263 264 return true, nil 265 }) 266 th.AssertNoErr(t, err) 267 th.CheckEquals(t, 1, count) 268 } 269 270 func TestListZeroObjectNames204(t *testing.T) { 271 th.SetupHTTP() 272 defer th.TeardownHTTP() 273 HandleListZeroObjectNames204(t) 274 275 count := 0 276 options := &objects.ListOpts{} 277 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) { 278 count++ 279 actual, err := objects.ExtractNames(page) 280 if err != nil { 281 t.Errorf("Failed to extract container names: %v", err) 282 return false, err 283 } 284 285 th.CheckDeepEquals(t, []string{}, actual) 286 287 return true, nil 288 }) 289 th.AssertNoErr(t, err) 290 th.CheckEquals(t, 0, count) 291 } 292 293 func TestCreateObject(t *testing.T) { 294 th.SetupHTTP() 295 defer th.TeardownHTTP() 296 297 content := "Did gyre and gimble in the wabe" 298 299 HandleCreateTextObjectSuccessfully(t, content) 300 301 options := &objects.CreateOpts{ContentType: "text/plain", Content: strings.NewReader(content)} 302 res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 303 th.AssertNoErr(t, res.Err) 304 } 305 306 func TestCreateObjectWithCacheControl(t *testing.T) { 307 th.SetupHTTP() 308 defer th.TeardownHTTP() 309 310 content := "All mimsy were the borogoves" 311 312 HandleCreateTextWithCacheControlSuccessfully(t, content) 313 314 options := &objects.CreateOpts{ 315 CacheControl: `max-age="3600", public`, 316 Content: strings.NewReader(content), 317 } 318 res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 319 th.AssertNoErr(t, res.Err) 320 } 321 322 func TestCreateObjectWithoutContentType(t *testing.T) { 323 th.SetupHTTP() 324 defer th.TeardownHTTP() 325 326 content := "The sky was the color of television, tuned to a dead channel." 327 328 HandleCreateTypelessObjectSuccessfully(t, content) 329 330 res := objects.Create(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)}) 331 th.AssertNoErr(t, res.Err) 332 } 333 334 func TestCopyObject(t *testing.T) { 335 t.Run("simple", func(t *testing.T) { 336 th.SetupHTTP() 337 defer th.TeardownHTTP() 338 HandleCopyObjectSuccessfully(t, "/newTestContainer/newTestObject") 339 340 options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} 341 res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 342 th.AssertNoErr(t, res.Err) 343 }) 344 t.Run("slash", func(t *testing.T) { 345 th.SetupHTTP() 346 defer th.TeardownHTTP() 347 HandleCopyObjectSuccessfully(t, "/newTestContainer/path%2Fto%2FnewTestObject") 348 349 options := &objects.CopyOpts{Destination: "/newTestContainer/path/to/newTestObject"} 350 res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 351 th.AssertNoErr(t, res.Err) 352 }) 353 t.Run("emojis", func(t *testing.T) { 354 th.SetupHTTP() 355 defer th.TeardownHTTP() 356 HandleCopyObjectSuccessfully(t, "/newTestContainer/new%F0%9F%98%8ATest%2C%3B%22O%28bject%21_%E7%AF%84") 357 358 options := &objects.CopyOpts{Destination: "/newTestContainer/new😊Test,;\"O(bject!_範"} 359 res := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 360 th.AssertNoErr(t, res.Err) 361 }) 362 } 363 364 func TestCopyObjectVersion(t *testing.T) { 365 th.SetupHTTP() 366 defer th.TeardownHTTP() 367 HandleCopyObjectVersionSuccessfully(t) 368 369 options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject", ObjectVersionID: "123456788"} 370 res, err := objects.Copy(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options).Extract() 371 th.AssertNoErr(t, err) 372 th.AssertEquals(t, "123456789", res.ObjectVersionID) 373 } 374 375 func TestDeleteObject(t *testing.T) { 376 th.SetupHTTP() 377 defer th.TeardownHTTP() 378 HandleDeleteObjectSuccessfully(t) 379 380 res := objects.Delete(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil) 381 th.AssertNoErr(t, res.Err) 382 } 383 384 func TestBulkDelete(t *testing.T) { 385 th.SetupHTTP() 386 defer th.TeardownHTTP() 387 HandleBulkDeleteSuccessfully(t) 388 389 expected := objects.BulkDeleteResponse{ 390 ResponseStatus: "foo", 391 ResponseBody: "bar", 392 NumberDeleted: 2, 393 Errors: [][]string{}, 394 } 395 396 resp, err := objects.BulkDelete(context.TODO(), fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() 397 th.AssertNoErr(t, err) 398 th.AssertDeepEquals(t, expected, *resp) 399 } 400 401 func TestUpateObjectMetadata(t *testing.T) { 402 th.SetupHTTP() 403 defer th.TeardownHTTP() 404 HandleUpdateObjectSuccessfully(t) 405 406 s := new(string) 407 i := new(int64) 408 options := &objects.UpdateOpts{ 409 Metadata: map[string]string{"Gophercloud-Test": "objects"}, 410 RemoveMetadata: []string{"Gophercloud-Test-Remove"}, 411 ContentDisposition: s, 412 ContentEncoding: s, 413 ContentType: s, 414 DeleteAt: i, 415 DetectContentType: new(bool), 416 } 417 res := objects.Update(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", options) 418 th.AssertNoErr(t, res.Err) 419 } 420 421 func TestGetObject(t *testing.T) { 422 th.SetupHTTP() 423 defer th.TeardownHTTP() 424 HandleGetObjectSuccessfully(t) 425 426 expected := map[string]string{"Gophercloud-Test": "objects"} 427 actual, err := objects.Get(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() 428 th.AssertNoErr(t, err) 429 th.CheckDeepEquals(t, expected, actual) 430 431 getOpts := objects.GetOpts{ 432 Newest: true, 433 } 434 actualHeaders, err := objects.Get(context.TODO(), fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() 435 th.AssertNoErr(t, err) 436 th.AssertEquals(t, true, actualHeaders.StaticLargeObject) 437 } 438 439 func TestETag(t *testing.T) { 440 content := "some example object" 441 createOpts := objects.CreateOpts{ 442 Content: strings.NewReader(content), 443 NoETag: true, 444 } 445 446 _, headers, _, err := createOpts.ToObjectCreateParams() 447 th.AssertNoErr(t, err) 448 _, ok := headers["ETag"] 449 th.AssertEquals(t, false, ok) 450 451 hash := md5.New() 452 _, err = io.WriteString(hash, content) 453 th.AssertNoErr(t, err) 454 localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) 455 456 createOpts = objects.CreateOpts{ 457 Content: strings.NewReader(content), 458 ETag: localChecksum, 459 } 460 461 _, headers, _, err = createOpts.ToObjectCreateParams() 462 th.AssertNoErr(t, err) 463 th.AssertEquals(t, localChecksum, headers["ETag"]) 464 } 465 466 func TestObjectCreateParamsWithoutSeek(t *testing.T) { 467 content := "I do not implement Seek()" 468 buf := strings.NewReader(content) 469 470 createOpts := objects.CreateOpts{Content: buf} 471 reader, headers, _, err := createOpts.ToObjectCreateParams() 472 473 th.AssertNoErr(t, err) 474 475 _, ok := reader.(io.ReadSeeker) 476 th.AssertEquals(t, true, ok) 477 478 c, err := io.ReadAll(reader) 479 th.AssertNoErr(t, err) 480 481 th.AssertEquals(t, content, string(c)) 482 483 _, ok = headers["ETag"] 484 th.AssertEquals(t, true, ok) 485 } 486 487 func TestObjectCreateParamsWithSeek(t *testing.T) { 488 content := "I implement Seek()" 489 createOpts := objects.CreateOpts{Content: strings.NewReader(content)} 490 reader, headers, _, err := createOpts.ToObjectCreateParams() 491 492 th.AssertNoErr(t, err) 493 494 _, ok := reader.(io.ReadSeeker) 495 th.AssertEquals(t, ok, true) 496 497 c, err := io.ReadAll(reader) 498 th.AssertNoErr(t, err) 499 500 th.AssertEquals(t, content, string(c)) 501 502 _, ok = headers["ETag"] 503 th.AssertEquals(t, true, ok) 504 } 505 506 func TestCreateTempURL(t *testing.T) { 507 port := 33200 508 th.SetupHTTP() 509 th.SetupPersistentPortHTTP(t, port) 510 defer th.TeardownHTTP() 511 512 // Handle fetching of secret key inside of CreateTempURL 513 containerTesting.HandleGetContainerSuccessfully(t) 514 accountTesting.HandleGetAccountSuccessfully(t) 515 client := fake.ServiceClient() 516 517 // Append v1/ to client endpoint URL to be compliant with tempURL generator 518 client.Endpoint = client.Endpoint + "v1/" 519 tempURL, err := objects.CreateTempURL(context.TODO(), client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ 520 Method: http.MethodGet, 521 TTL: 60, 522 Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), 523 }) 524 525 sig := "89be454a9c7e2e9f3f50a8441815e0b5801cba5b" 526 expiry := "1593565980" 527 expectedURL := fmt.Sprintf("http://127.0.0.1:%v/v1/testContainer/testObject%%2FtestFile.txt?temp_url_sig=%v&temp_url_expires=%v", port, sig, expiry) 528 529 th.AssertNoErr(t, err) 530 th.AssertEquals(t, expectedURL, tempURL) 531 532 // Test TTL=0, but different timestamp 533 tempURL, err = objects.CreateTempURL(context.TODO(), client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ 534 Method: http.MethodGet, 535 Timestamp: time.Date(2020, 07, 01, 01, 13, 00, 00, time.UTC), 536 }) 537 th.AssertNoErr(t, err) 538 th.AssertEquals(t, expectedURL, tempURL) 539 }