storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/utils_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"net/url"
    27  	"os"
    28  	"reflect"
    29  	"strings"
    30  	"testing"
    31  )
    32  
    33  // Tests maximum object size.
    34  func TestMaxObjectSize(t *testing.T) {
    35  	sizes := []struct {
    36  		isMax bool
    37  		size  int64
    38  	}{
    39  		// Test - 1 - maximum object size.
    40  		{
    41  			true,
    42  			globalMaxObjectSize + 1,
    43  		},
    44  		// Test - 2 - not maximum object size.
    45  		{
    46  			false,
    47  			globalMaxObjectSize - 1,
    48  		},
    49  	}
    50  	for i, s := range sizes {
    51  		isMax := isMaxObjectSize(s.size)
    52  		if isMax != s.isMax {
    53  			t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMax, isMax)
    54  		}
    55  	}
    56  }
    57  
    58  // Tests minimum allowed part size.
    59  func TestMinAllowedPartSize(t *testing.T) {
    60  	sizes := []struct {
    61  		isMin bool
    62  		size  int64
    63  	}{
    64  		// Test - 1 - within minimum part size.
    65  		{
    66  			true,
    67  			globalMinPartSize + 1,
    68  		},
    69  		// Test - 2 - smaller than minimum part size.
    70  		{
    71  			false,
    72  			globalMinPartSize - 1,
    73  		},
    74  	}
    75  
    76  	for i, s := range sizes {
    77  		isMin := isMinAllowedPartSize(s.size)
    78  		if isMin != s.isMin {
    79  			t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMin, isMin)
    80  		}
    81  	}
    82  }
    83  
    84  // Tests maximum allowed part number.
    85  func TestMaxPartID(t *testing.T) {
    86  	sizes := []struct {
    87  		isMax bool
    88  		partN int
    89  	}{
    90  		// Test - 1 part number within max part number.
    91  		{
    92  			false,
    93  			globalMaxPartID - 1,
    94  		},
    95  		// Test - 2 part number bigger than max part number.
    96  		{
    97  			true,
    98  			globalMaxPartID + 1,
    99  		},
   100  	}
   101  
   102  	for i, s := range sizes {
   103  		isMax := isMaxPartID(s.partN)
   104  		if isMax != s.isMax {
   105  			t.Errorf("Test %d: Expected %t, got %t", i+1, s.isMax, isMax)
   106  		}
   107  	}
   108  }
   109  
   110  // Tests extracting bucket and objectname from various types of paths.
   111  func TestPath2BucketObjectName(t *testing.T) {
   112  	testCases := []struct {
   113  		path           string
   114  		bucket, object string
   115  	}{
   116  		// Test case 1 normal case.
   117  		{
   118  			path:   "/bucket/object",
   119  			bucket: "bucket",
   120  			object: "object",
   121  		},
   122  		// Test case 2 where url only has separator.
   123  		{
   124  			path:   SlashSeparator,
   125  			bucket: "",
   126  			object: "",
   127  		},
   128  		// Test case 3 only bucket is present.
   129  		{
   130  			path:   "/bucket",
   131  			bucket: "bucket",
   132  			object: "",
   133  		},
   134  		// Test case 4 many separators and object is a directory.
   135  		{
   136  			path:   "/bucket/object/1/",
   137  			bucket: "bucket",
   138  			object: "object/1/",
   139  		},
   140  		// Test case 5 object has many trailing separators.
   141  		{
   142  			path:   "/bucket/object/1///",
   143  			bucket: "bucket",
   144  			object: "object/1///",
   145  		},
   146  		// Test case 6 object has only trailing separators.
   147  		{
   148  			path:   "/bucket/object///////",
   149  			bucket: "bucket",
   150  			object: "object///////",
   151  		},
   152  		// Test case 7 object has preceding separators.
   153  		{
   154  			path:   "/bucket////object////",
   155  			bucket: "bucket",
   156  			object: "///object////",
   157  		},
   158  		// Test case 8 url path is empty.
   159  		{
   160  			path:   "",
   161  			bucket: "",
   162  			object: "",
   163  		},
   164  	}
   165  
   166  	// Validate all test cases.
   167  	for _, testCase := range testCases {
   168  		testCase := testCase
   169  		t.Run("", func(t *testing.T) {
   170  			bucketName, objectName := path2BucketObject(testCase.path)
   171  			if bucketName != testCase.bucket {
   172  				t.Errorf("failed expected bucket name \"%s\", got \"%s\"", testCase.bucket, bucketName)
   173  			}
   174  			if objectName != testCase.object {
   175  				t.Errorf("failed expected bucket name \"%s\", got \"%s\"", testCase.object, objectName)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  // Add tests for starting and stopping different profilers.
   182  func TestStartProfiler(t *testing.T) {
   183  	_, err := startProfiler("")
   184  	if err == nil {
   185  		t.Fatal("Expected a non nil error, but nil error returned for invalid profiler.")
   186  	}
   187  }
   188  
   189  // checkURL - checks if passed address correspond
   190  func checkURL(urlStr string) (*url.URL, error) {
   191  	if urlStr == "" {
   192  		return nil, errors.New("Address cannot be empty")
   193  	}
   194  	u, err := url.Parse(urlStr)
   195  	if err != nil {
   196  		return nil, fmt.Errorf("`%s` invalid: %s", urlStr, err.Error())
   197  	}
   198  	return u, nil
   199  }
   200  
   201  // TestCheckURL tests valid url.
   202  func TestCheckURL(t *testing.T) {
   203  	testCases := []struct {
   204  		urlStr     string
   205  		shouldPass bool
   206  	}{
   207  		{"", false},
   208  		{":", false},
   209  		{"http://localhost/", true},
   210  		{"http://127.0.0.1/", true},
   211  		{"proto://myhostname/path", true},
   212  	}
   213  
   214  	// Validates fetching local address.
   215  	for i, testCase := range testCases {
   216  		_, err := checkURL(testCase.urlStr)
   217  		if testCase.shouldPass && err != nil {
   218  			t.Errorf("Test %d: expected to pass but got an error: %v\n", i+1, err)
   219  		}
   220  		if !testCase.shouldPass && err == nil {
   221  			t.Errorf("Test %d: expected to fail but passed.", i+1)
   222  		}
   223  	}
   224  }
   225  
   226  // Testing dumping request function.
   227  func TestDumpRequest(t *testing.T) {
   228  	req, err := http.NewRequest(http.MethodGet, "http://localhost:9000?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=USWUXHGYZQYFYFFIT3RE%2F20170529%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20170529T190139Z&X-Amz-Expires=600&X-Amz-Signature=19b58080999df54b446fc97304eb8dda60d3df1812ae97f3e8783351bfd9781d&X-Amz-SignedHeaders=host&prefix=Hello%2AWorld%2A", nil)
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  	req.RequestURI = "/?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=USWUXHGYZQYFYFFIT3RE%2F20170529%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20170529T190139Z&X-Amz-Expires=600&X-Amz-Signature=19b58080999df54b446fc97304eb8dda60d3df1812ae97f3e8783351bfd9781d&X-Amz-SignedHeaders=host&prefix=Hello%2AWorld%2A"
   233  	req.Header.Set("content-md5", "====test")
   234  	jsonReq := dumpRequest(req)
   235  	type jsonResult struct {
   236  		Method     string      `json:"method"`
   237  		RequestURI string      `json:"reqURI"`
   238  		Header     http.Header `json:"header"`
   239  	}
   240  	jsonReq = strings.Replace(jsonReq, "%%", "%", -1)
   241  	res := jsonResult{}
   242  	if err = json.Unmarshal([]byte(jsonReq), &res); err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	// Look for expected method.
   247  	if res.Method != http.MethodGet {
   248  		t.Fatalf("Unexpected method %s, expected 'GET'", res.Method)
   249  	}
   250  
   251  	// Look for expected query values
   252  	expectedQuery := url.Values{}
   253  	expectedQuery.Set("prefix", "Hello*World*")
   254  	expectedQuery.Set("X-Amz-Algorithm", "AWS4-HMAC-SHA256")
   255  	expectedQuery.Set("X-Amz-Credential", "USWUXHGYZQYFYFFIT3RE/20170529/us-east-1/s3/aws4_request")
   256  	expectedQuery.Set("X-Amz-Date", "20170529T190139Z")
   257  	expectedQuery.Set("X-Amz-Expires", "600")
   258  	expectedQuery.Set("X-Amz-SignedHeaders", "host")
   259  	expectedQuery.Set("X-Amz-Signature", "19b58080999df54b446fc97304eb8dda60d3df1812ae97f3e8783351bfd9781d")
   260  	expectedRequestURI := "/?" + expectedQuery.Encode()
   261  	if !reflect.DeepEqual(res.RequestURI, expectedRequestURI) {
   262  		t.Fatalf("Expected %#v, got %#v", expectedRequestURI, res.RequestURI)
   263  	}
   264  
   265  	// Look for expected header.
   266  	expectedHeader := http.Header{}
   267  	expectedHeader.Set("content-md5", "====test")
   268  	expectedHeader.Set("host", "localhost:9000")
   269  	if !reflect.DeepEqual(res.Header, expectedHeader) {
   270  		t.Fatalf("Expected %#v, got %#v", expectedHeader, res.Header)
   271  	}
   272  }
   273  
   274  // Test ToS3ETag()
   275  func TestToS3ETag(t *testing.T) {
   276  	testCases := []struct {
   277  		etag         string
   278  		expectedETag string
   279  	}{
   280  		{`"8019e762"`, `8019e762-1`},
   281  		{"5d57546eeb86b3eba68967292fba0644", "5d57546eeb86b3eba68967292fba0644-1"},
   282  		{`"8019e762-1"`, `8019e762-1`},
   283  		{"5d57546eeb86b3eba68967292fba0644-1", "5d57546eeb86b3eba68967292fba0644-1"},
   284  	}
   285  	for i, testCase := range testCases {
   286  		etag := ToS3ETag(testCase.etag)
   287  		if etag != testCase.expectedETag {
   288  			t.Fatalf("test %v: expected: %v, got: %v", i+1, testCase.expectedETag, etag)
   289  		}
   290  	}
   291  }
   292  
   293  // Test contains
   294  func TestContains(t *testing.T) {
   295  
   296  	testErr := errors.New("test err")
   297  
   298  	testCases := []struct {
   299  		slice interface{}
   300  		elem  interface{}
   301  		found bool
   302  	}{
   303  		{nil, nil, false},
   304  		{"1", "1", false},
   305  		{nil, "1", false},
   306  		{[]string{"1"}, nil, false},
   307  		{[]string{}, "1", false},
   308  		{[]string{"1"}, "1", true},
   309  		{[]string{"2"}, "1", false},
   310  		{[]string{"1", "2"}, "1", true},
   311  		{[]string{"2", "1"}, "1", true},
   312  		{[]string{"2", "1", "3"}, "1", true},
   313  		{[]int{1, 2, 3}, "1", false},
   314  		{[]int{1, 2, 3}, 2, true},
   315  		{[]int{1, 2, 3, 4, 5, 6}, 7, false},
   316  		{[]error{errors.New("new err")}, testErr, false},
   317  		{[]error{errors.New("new err"), testErr}, testErr, true},
   318  	}
   319  
   320  	for i, testCase := range testCases {
   321  		found := contains(testCase.slice, testCase.elem)
   322  		if found != testCase.found {
   323  			t.Fatalf("Test %v: expected: %v, got: %v", i+1, testCase.found, found)
   324  		}
   325  	}
   326  }
   327  
   328  // Test jsonLoad.
   329  func TestJSONLoad(t *testing.T) {
   330  	format := newFormatFSV1()
   331  	b, err := json.Marshal(format)
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	var gotFormat formatFSV1
   336  	if err = jsonLoad(bytes.NewReader(b), &gotFormat); err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	if *format != gotFormat {
   340  		t.Fatal("jsonLoad() failed to decode json")
   341  	}
   342  }
   343  
   344  // Test jsonSave.
   345  func TestJSONSave(t *testing.T) {
   346  	f, err := ioutil.TempFile("", "")
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	defer os.Remove(f.Name())
   351  
   352  	// Test to make sure formatFSSave overwrites and does not append.
   353  	format := newFormatFSV1()
   354  	if err = jsonSave(f, format); err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	fi1, err := f.Stat()
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	if err = jsonSave(f, format); err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	fi2, err := f.Stat()
   365  	if err != nil {
   366  		t.Fatal(err)
   367  	}
   368  	if fi1.Size() != fi2.Size() {
   369  		t.Fatal("Size should not differs after jsonSave()", fi1.Size(), fi2.Size(), f.Name())
   370  	}
   371  }
   372  
   373  // Test ceilFrac
   374  func TestCeilFrac(t *testing.T) {
   375  	cases := []struct {
   376  		numerator, denominator, ceiling int64
   377  	}{
   378  		{0, 1, 0},
   379  		{-1, 2, 0},
   380  		{1, 2, 1},
   381  		{1, 1, 1},
   382  		{3, 2, 2},
   383  		{54, 11, 5},
   384  		{45, 11, 5},
   385  		{-4, 3, -1},
   386  		{4, -3, -1},
   387  		{-4, -3, 2},
   388  		{3, 0, 0},
   389  	}
   390  	for i, testCase := range cases {
   391  		ceiling := ceilFrac(testCase.numerator, testCase.denominator)
   392  		if ceiling != testCase.ceiling {
   393  			t.Errorf("Case %d: Unexpected result: %d", i, ceiling)
   394  		}
   395  	}
   396  }
   397  
   398  // Test if isErrIgnored works correctly.
   399  func TestIsErrIgnored(t *testing.T) {
   400  	var errIgnored = fmt.Errorf("ignored error")
   401  	ignoredErrs := append(baseIgnoredErrs, errIgnored)
   402  	var testCases = []struct {
   403  		err     error
   404  		ignored bool
   405  	}{
   406  		{
   407  			err:     nil,
   408  			ignored: false,
   409  		},
   410  		{
   411  			err:     errIgnored,
   412  			ignored: true,
   413  		},
   414  		{
   415  			err:     errFaultyDisk,
   416  			ignored: true,
   417  		},
   418  	}
   419  	for i, testCase := range testCases {
   420  		if ok := IsErrIgnored(testCase.err, ignoredErrs...); ok != testCase.ignored {
   421  			t.Errorf("Test: %d, Expected %t, got %t", i+1, testCase.ignored, ok)
   422  		}
   423  	}
   424  }
   425  
   426  // Test queries()
   427  func TestQueries(t *testing.T) {
   428  	var testCases = []struct {
   429  		keys      []string
   430  		keyvalues []string
   431  	}{
   432  		{
   433  			[]string{"aaaa", "bbbb"},
   434  			[]string{"aaaa", "{aaaa:.*}", "bbbb", "{bbbb:.*}"},
   435  		},
   436  	}
   437  
   438  	for i, test := range testCases {
   439  		keyvalues := restQueries(test.keys...)
   440  		for j := range keyvalues {
   441  			if keyvalues[j] != test.keyvalues[j] {
   442  				t.Fatalf("test %d: keyvalues[%d] does not match", i+1, j)
   443  			}
   444  		}
   445  	}
   446  }
   447  
   448  func TestLCP(t *testing.T) {
   449  	var testCases = []struct {
   450  		prefixes     []string
   451  		commonPrefix string
   452  	}{
   453  		{[]string{"", ""}, ""},
   454  		{[]string{"a", "b"}, ""},
   455  		{[]string{"a", "a"}, "a"},
   456  		{[]string{"a/", "a/"}, "a/"},
   457  		{[]string{"abcd/", ""}, ""},
   458  		{[]string{"abcd/foo/", "abcd/bar/"}, "abcd/"},
   459  		{[]string{"abcd/foo/bar/", "abcd/foo/bar/zoo"}, "abcd/foo/bar/"},
   460  	}
   461  
   462  	for i, test := range testCases {
   463  		foundPrefix := lcp(test.prefixes, true)
   464  		if foundPrefix != test.commonPrefix {
   465  			t.Fatalf("Test %d: Common prefix found: `%v`, expected: `%v`", i+1, foundPrefix, test.commonPrefix)
   466  		}
   467  	}
   468  }
   469  
   470  func TestGetMinioMode(t *testing.T) {
   471  	testMinioMode := func(expected string) {
   472  		if mode := getMinioMode(); mode != expected {
   473  			t.Fatalf("Expected %s got %s", expected, mode)
   474  		}
   475  	}
   476  	globalIsDistErasure = true
   477  	testMinioMode(globalMinioModeDistErasure)
   478  
   479  	globalIsDistErasure = false
   480  	globalIsErasure = true
   481  	testMinioMode(globalMinioModeErasure)
   482  
   483  	globalIsDistErasure, globalIsErasure = false, false
   484  	testMinioMode(globalMinioModeFS)
   485  
   486  	GlobalIsGateway, globalGatewayName = true, "azure"
   487  	testMinioMode(globalMinioModeGatewayPrefix + globalGatewayName)
   488  
   489  }