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  }