1package testing 2 3import ( 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 containerTesting "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/testing" 16 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" 17 "github.com/gophercloud/gophercloud/pagination" 18 th "github.com/gophercloud/gophercloud/testhelper" 19 fake "github.com/gophercloud/gophercloud/testhelper/client" 20) 21 22func TestDownloadReader(t *testing.T) { 23 th.SetupHTTP() 24 defer th.TeardownHTTP() 25 HandleDownloadObjectSuccessfully(t) 26 27 response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) 28 defer response.Body.Close() 29 30 // Check reader 31 buf := bytes.NewBuffer(make([]byte, 0)) 32 io.CopyN(buf, response.Body, 10) 33 th.CheckEquals(t, "Successful", string(buf.Bytes())) 34} 35 36func TestDownloadExtraction(t *testing.T) { 37 th.SetupHTTP() 38 defer th.TeardownHTTP() 39 HandleDownloadObjectSuccessfully(t) 40 41 response := objects.Download(fake.ServiceClient(), "testContainer", "testObject", nil) 42 43 // Check []byte extraction 44 bytes, err := response.ExtractContent() 45 th.AssertNoErr(t, err) 46 th.CheckEquals(t, "Successful download with Gophercloud", string(bytes)) 47 48 expected := &objects.DownloadHeader{ 49 ContentLength: 36, 50 ContentType: "text/plain; charset=utf-8", 51 Date: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 52 StaticLargeObject: true, 53 LastModified: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 54 } 55 actual, err := response.Extract() 56 th.AssertNoErr(t, err) 57 th.CheckDeepEquals(t, expected, actual) 58} 59 60func TestDownloadWithLastModified(t *testing.T) { 61 th.SetupHTTP() 62 defer th.TeardownHTTP() 63 HandleDownloadObjectSuccessfully(t) 64 65 options1 := &objects.DownloadOpts{ 66 IfUnmodifiedSince: time.Date(2009, time.November, 10, 22, 59, 59, 0, time.UTC), 67 } 68 response1 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options1) 69 _, err1 := response1.Extract() 70 th.AssertErr(t, err1) 71 72 options2 := &objects.DownloadOpts{ 73 IfModifiedSince: time.Date(2009, time.November, 10, 23, 0, 1, 0, time.UTC), 74 } 75 response2 := objects.Download(fake.ServiceClient(), "testContainer", "testObject", options2) 76 content, err2 := response2.ExtractContent() 77 th.AssertNoErr(t, err2) 78 th.AssertEquals(t, len(content), 0) 79} 80 81func TestListObjectInfo(t *testing.T) { 82 th.SetupHTTP() 83 defer th.TeardownHTTP() 84 HandleListObjectsInfoSuccessfully(t) 85 86 count := 0 87 options := &objects.ListOpts{Full: true} 88 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { 89 count++ 90 actual, err := objects.ExtractInfo(page) 91 th.AssertNoErr(t, err) 92 93 th.CheckDeepEquals(t, ExpectedListInfo, actual) 94 95 return true, nil 96 }) 97 th.AssertNoErr(t, err) 98 th.CheckEquals(t, count, 1) 99} 100 101func TestListObjectSubdir(t *testing.T) { 102 th.SetupHTTP() 103 defer th.TeardownHTTP() 104 HandleListSubdirSuccessfully(t) 105 106 count := 0 107 options := &objects.ListOpts{Full: true, Prefix: "", Delimiter: "/"} 108 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { 109 count++ 110 actual, err := objects.ExtractInfo(page) 111 th.AssertNoErr(t, err) 112 113 th.CheckDeepEquals(t, ExpectedListSubdir, actual) 114 115 return true, nil 116 }) 117 th.AssertNoErr(t, err) 118 th.CheckEquals(t, count, 1) 119} 120 121func TestListObjectNames(t *testing.T) { 122 th.SetupHTTP() 123 defer th.TeardownHTTP() 124 HandleListObjectNamesSuccessfully(t) 125 126 // Check without delimiter. 127 count := 0 128 options := &objects.ListOpts{Full: false} 129 err := objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { 130 count++ 131 actual, err := objects.ExtractNames(page) 132 if err != nil { 133 t.Errorf("Failed to extract container names: %v", err) 134 return false, err 135 } 136 137 th.CheckDeepEquals(t, ExpectedListNames, actual) 138 139 return true, nil 140 }) 141 th.AssertNoErr(t, err) 142 th.CheckEquals(t, count, 1) 143 144 // Check with delimiter. 145 count = 0 146 options = &objects.ListOpts{Full: false, Delimiter: "/"} 147 err = objects.List(fake.ServiceClient(), "testContainer", options).EachPage(func(page pagination.Page) (bool, error) { 148 count++ 149 actual, err := objects.ExtractNames(page) 150 if err != nil { 151 t.Errorf("Failed to extract container names: %v", err) 152 return false, err 153 } 154 155 th.CheckDeepEquals(t, ExpectedListNames, actual) 156 157 return true, nil 158 }) 159 th.AssertNoErr(t, err) 160 th.CheckEquals(t, count, 1) 161} 162 163func TestCreateObject(t *testing.T) { 164 th.SetupHTTP() 165 defer th.TeardownHTTP() 166 167 content := "Did gyre and gimble in the wabe" 168 169 HandleCreateTextObjectSuccessfully(t, content) 170 171 options := &objects.CreateOpts{ContentType: "text/plain", Content: strings.NewReader(content)} 172 res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) 173 th.AssertNoErr(t, res.Err) 174} 175 176func TestCreateObjectWithCacheControl(t *testing.T) { 177 th.SetupHTTP() 178 defer th.TeardownHTTP() 179 180 content := "All mimsy were the borogoves" 181 182 HandleCreateTextWithCacheControlSuccessfully(t, content) 183 184 options := &objects.CreateOpts{ 185 CacheControl: `max-age="3600", public`, 186 Content: strings.NewReader(content), 187 } 188 res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", options) 189 th.AssertNoErr(t, res.Err) 190} 191 192func TestCreateObjectWithoutContentType(t *testing.T) { 193 th.SetupHTTP() 194 defer th.TeardownHTTP() 195 196 content := "The sky was the color of television, tuned to a dead channel." 197 198 HandleCreateTypelessObjectSuccessfully(t, content) 199 200 res := objects.Create(fake.ServiceClient(), "testContainer", "testObject", &objects.CreateOpts{Content: strings.NewReader(content)}) 201 th.AssertNoErr(t, res.Err) 202} 203 204/* 205func TestErrorIsRaisedForChecksumMismatch(t *testing.T) { 206 th.SetupHTTP() 207 defer th.TeardownHTTP() 208 209 th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) { 210 w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8") 211 w.WriteHeader(http.StatusCreated) 212 }) 213 214 content := strings.NewReader("The sky was the color of television, tuned to a dead channel.") 215 res := Create(fake.ServiceClient(), "testContainer", "testObject", &CreateOpts{Content: content}) 216 217 err := fmt.Errorf("Local checksum does not match API ETag header") 218 th.AssertDeepEquals(t, err, res.Err) 219} 220*/ 221 222func TestCopyObject(t *testing.T) { 223 th.SetupHTTP() 224 defer th.TeardownHTTP() 225 HandleCopyObjectSuccessfully(t) 226 227 options := &objects.CopyOpts{Destination: "/newTestContainer/newTestObject"} 228 res := objects.Copy(fake.ServiceClient(), "testContainer", "testObject", options) 229 th.AssertNoErr(t, res.Err) 230} 231 232func TestDeleteObject(t *testing.T) { 233 th.SetupHTTP() 234 defer th.TeardownHTTP() 235 HandleDeleteObjectSuccessfully(t) 236 237 res := objects.Delete(fake.ServiceClient(), "testContainer", "testObject", nil) 238 th.AssertNoErr(t, res.Err) 239} 240 241func TestBulkDelete(t *testing.T) { 242 th.SetupHTTP() 243 defer th.TeardownHTTP() 244 HandleBulkDeleteSuccessfully(t) 245 246 expected := objects.BulkDeleteResponse{ 247 ResponseStatus: "foo", 248 ResponseBody: "bar", 249 NumberDeleted: 2, 250 Errors: [][]string{}, 251 } 252 253 resp, err := objects.BulkDelete(fake.ServiceClient(), "testContainer", []string{"testObject1", "testObject2"}).Extract() 254 th.AssertNoErr(t, err) 255 th.AssertDeepEquals(t, expected, *resp) 256} 257 258func TestUpateObjectMetadata(t *testing.T) { 259 th.SetupHTTP() 260 defer th.TeardownHTTP() 261 HandleUpdateObjectSuccessfully(t) 262 263 options := &objects.UpdateOpts{ 264 Metadata: map[string]string{"Gophercloud-Test": "objects"}, 265 RemoveMetadata: []string{"Gophercloud-Test-Remove"}, 266 } 267 res := objects.Update(fake.ServiceClient(), "testContainer", "testObject", options) 268 th.AssertNoErr(t, res.Err) 269} 270 271func TestGetObject(t *testing.T) { 272 th.SetupHTTP() 273 defer th.TeardownHTTP() 274 HandleGetObjectSuccessfully(t) 275 276 expected := map[string]string{"Gophercloud-Test": "objects"} 277 actual, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", nil).ExtractMetadata() 278 th.AssertNoErr(t, err) 279 th.CheckDeepEquals(t, expected, actual) 280 281 getOpts := objects.GetOpts{ 282 Newest: true, 283 } 284 actualHeaders, err := objects.Get(fake.ServiceClient(), "testContainer", "testObject", getOpts).Extract() 285 th.AssertNoErr(t, err) 286 th.AssertEquals(t, actualHeaders.StaticLargeObject, true) 287} 288 289func TestETag(t *testing.T) { 290 content := "some example object" 291 createOpts := objects.CreateOpts{ 292 Content: strings.NewReader(content), 293 NoETag: true, 294 } 295 296 _, headers, _, err := createOpts.ToObjectCreateParams() 297 th.AssertNoErr(t, err) 298 _, ok := headers["ETag"] 299 th.AssertEquals(t, ok, false) 300 301 hash := md5.New() 302 io.WriteString(hash, content) 303 localChecksum := fmt.Sprintf("%x", hash.Sum(nil)) 304 305 createOpts = objects.CreateOpts{ 306 Content: strings.NewReader(content), 307 ETag: localChecksum, 308 } 309 310 _, headers, _, err = createOpts.ToObjectCreateParams() 311 th.AssertNoErr(t, err) 312 th.AssertEquals(t, headers["ETag"], localChecksum) 313} 314 315func TestObjectCreateParamsWithoutSeek(t *testing.T) { 316 content := "I do not implement Seek()" 317 buf := bytes.NewBuffer([]byte(content)) 318 319 createOpts := objects.CreateOpts{Content: buf} 320 reader, headers, _, err := createOpts.ToObjectCreateParams() 321 322 th.AssertNoErr(t, err) 323 324 _, ok := reader.(io.ReadSeeker) 325 th.AssertEquals(t, ok, true) 326 327 c, err := ioutil.ReadAll(reader) 328 th.AssertNoErr(t, err) 329 330 th.AssertEquals(t, content, string(c)) 331 332 _, ok = headers["ETag"] 333 th.AssertEquals(t, true, ok) 334} 335 336func TestObjectCreateParamsWithSeek(t *testing.T) { 337 content := "I implement Seek()" 338 createOpts := objects.CreateOpts{Content: strings.NewReader(content)} 339 reader, headers, _, err := createOpts.ToObjectCreateParams() 340 341 th.AssertNoErr(t, err) 342 343 _, ok := reader.(io.ReadSeeker) 344 th.AssertEquals(t, ok, true) 345 346 c, err := ioutil.ReadAll(reader) 347 th.AssertNoErr(t, err) 348 349 th.AssertEquals(t, content, string(c)) 350 351 _, ok = headers["ETag"] 352 th.AssertEquals(t, true, ok) 353} 354 355func TestCreateTempURL(t *testing.T) { 356 port := 33200 357 th.SetupHTTP() 358 th.SetupPersistentPortHTTP(t, port) 359 defer th.TeardownHTTP() 360 361 // Handle fetching of secret key inside of CreateTempURL 362 containerTesting.HandleGetContainerSuccessfully(t) 363 accountTesting.HandleGetAccountSuccessfully(t) 364 client := fake.ServiceClient() 365 366 // Append v1/ to client endpoint URL to be compliant with tempURL generator 367 client.Endpoint = client.Endpoint + "v1/" 368 tempURL, err := objects.CreateTempURL(client, "testContainer", "testObject/testFile.txt", objects.CreateTempURLOpts{ 369 Method: http.MethodGet, 370 TTL: 60, 371 Timestamp: time.Date(2020, 07, 01, 01, 12, 00, 00, time.UTC), 372 }) 373 374 sig := "89be454a9c7e2e9f3f50a8441815e0b5801cba5b" 375 expiry := "1593565980" 376 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) 377 378 th.AssertNoErr(t, err) 379 th.AssertEquals(t, expectedURL, tempURL) 380} 381