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