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  }