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 }