storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/etag/etag_test.go (about)

     1  // MinIO Cloud Storage, (C) 2021 MinIO, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package etag
    16  
    17  import (
    18  	"io"
    19  	"io/ioutil"
    20  	"net/http"
    21  	"strings"
    22  	"testing"
    23  )
    24  
    25  var parseTests = []struct {
    26  	String     string
    27  	ETag       ETag
    28  	ShouldFail bool
    29  }{
    30  	{String: "3b83ef96387f1465", ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101}},                                                                    // 0
    31  	{String: "3b83ef96387f14655fc854ddc3c6bd57", ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}},               // 1
    32  	{String: `"3b83ef96387f14655fc854ddc3c6bd57"`, ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}},             // 2
    33  	{String: "ceb8853ddc5086cc4ab9e149f8f09c88-1", ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 49}},   // 3
    34  	{String: `"ceb8853ddc5086cc4ab9e149f8f09c88-2"`, ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 50}}, // 4
    35  	{ // 5
    36  		String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941",
    37  		ETag:   ETag{144, 64, 44, 120, 210, 220, 205, 222, 225, 233, 232, 98, 34, 206, 44, 99, 97, 103, 95, 53, 41, 210, 96, 0, 174, 46, 144, 15, 242, 22, 179, 203, 89, 225, 48, 224, 146, 216, 162, 152, 30, 119, 111, 77, 11, 214, 9, 65},
    38  	},
    39  
    40  	{String: `"3b83ef96387f14655fc854ddc3c6bd57`, ShouldFail: true},                                                                  // 6
    41  	{String: "ceb8853ddc5086cc4ab9e149f8f09c88-", ShouldFail: true},                                                                  // 7
    42  	{String: "ceb8853ddc5086cc4ab9e149f8f09c88-2a", ShouldFail: true},                                                                // 8
    43  	{String: "ceb8853ddc5086cc4ab9e149f8f09c88-2-1", ShouldFail: true},                                                               // 9
    44  	{String: "90402c78d2dccddee1e9e86222ce2c-1", ShouldFail: true},                                                                   // 10
    45  	{String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941-1", ShouldFail: true}, // 11
    46  }
    47  
    48  func TestParse(t *testing.T) {
    49  	for i, test := range parseTests {
    50  		etag, err := Parse(test.String)
    51  		if err == nil && test.ShouldFail {
    52  			t.Fatalf("Test %d: parse should have failed but succeeded", i)
    53  		}
    54  		if err != nil && !test.ShouldFail {
    55  			t.Fatalf("Test %d: failed to parse ETag %q: %v", i, test.String, err)
    56  		}
    57  		if !Equal(etag, test.ETag) {
    58  			t.Log([]byte(etag))
    59  			t.Fatalf("Test %d: ETags don't match", i)
    60  		}
    61  	}
    62  }
    63  
    64  var stringTests = []struct {
    65  	ETag   ETag
    66  	String string
    67  }{
    68  	{ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101}, String: "3b83ef96387f1465"},                                                                  // 0
    69  	{ETag: ETag{59, 131, 239, 150, 56, 127, 20, 101, 95, 200, 84, 221, 195, 198, 189, 87}, String: "3b83ef96387f14655fc854ddc3c6bd57"},             // 1
    70  	{ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 49}, String: "ceb8853ddc5086cc4ab9e149f8f09c88-1"}, // 2
    71  	{ETag: ETag{206, 184, 133, 61, 220, 80, 134, 204, 74, 185, 225, 73, 248, 240, 156, 136, 45, 50}, String: "ceb8853ddc5086cc4ab9e149f8f09c88-2"}, // 3
    72  	{ // 4
    73  		ETag:   ETag{144, 64, 44, 120, 210, 220, 205, 222, 225, 233, 232, 98, 34, 206, 44, 99, 97, 103, 95, 53, 41, 210, 96, 0, 174, 46, 144, 15, 242, 22, 179, 203, 89, 225, 48, 224, 146, 216, 162, 152, 30, 119, 111, 77, 11, 214, 9, 65},
    74  		String: "90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941",
    75  	},
    76  }
    77  
    78  func TestString(t *testing.T) {
    79  	for i, test := range stringTests {
    80  		s := test.ETag.String()
    81  		if s != test.String {
    82  			t.Fatalf("Test %d: got %s - want %s", i, s, test.String)
    83  		}
    84  	}
    85  }
    86  
    87  var equalTests = []struct {
    88  	A     string
    89  	B     string
    90  	Equal bool
    91  }{
    92  	{A: "3b83ef96387f14655fc854ddc3c6bd57", B: "3b83ef96387f14655fc854ddc3c6bd57", Equal: true},   // 0
    93  	{A: "3b83ef96387f14655fc854ddc3c6bd57", B: `"3b83ef96387f14655fc854ddc3c6bd57"`, Equal: true}, // 1
    94  
    95  	{A: "3b83ef96387f14655fc854ddc3c6bd57", B: "3b83ef96387f14655fc854ddc3c6bd57-2", Equal: false}, // 2
    96  	{A: "3b83ef96387f14655fc854ddc3c6bd57", B: "ceb8853ddc5086cc4ab9e149f8f09c88", Equal: false},   // 3
    97  }
    98  
    99  func TestEqual(t *testing.T) {
   100  	for i, test := range equalTests {
   101  		A, err := Parse(test.A)
   102  		if err != nil {
   103  			t.Fatalf("Test %d: %v", i, err)
   104  		}
   105  		B, err := Parse(test.B)
   106  		if err != nil {
   107  			t.Fatalf("Test %d: %v", i, err)
   108  		}
   109  		if equal := Equal(A, B); equal != test.Equal {
   110  			t.Fatalf("Test %d: got %v - want %v", i, equal, test.Equal)
   111  		}
   112  	}
   113  }
   114  
   115  var readerTests = []struct { // Reference values computed by: echo <content> | md5sum
   116  	Content string
   117  	ETag    ETag
   118  }{
   119  	{
   120  		Content: "", ETag: ETag{212, 29, 140, 217, 143, 0, 178, 4, 233, 128, 9, 152, 236, 248, 66, 126},
   121  	},
   122  	{
   123  		Content: " ", ETag: ETag{114, 21, 238, 156, 125, 157, 194, 41, 210, 146, 26, 64, 232, 153, 236, 95},
   124  	},
   125  	{
   126  		Content: "Hello World", ETag: ETag{177, 10, 141, 177, 100, 224, 117, 65, 5, 183, 169, 155, 231, 46, 63, 229},
   127  	},
   128  }
   129  
   130  func TestReader(t *testing.T) {
   131  	for i, test := range readerTests {
   132  		reader := NewReader(strings.NewReader(test.Content), test.ETag)
   133  		if _, err := io.Copy(ioutil.Discard, reader); err != nil {
   134  			t.Fatalf("Test %d: read failed: %v", i, err)
   135  		}
   136  		if ETag := reader.ETag(); !Equal(ETag, test.ETag) {
   137  			t.Fatalf("Test %d: ETag mismatch: got %q - want %q", i, ETag, test.ETag)
   138  		}
   139  	}
   140  }
   141  
   142  var multipartTests = []struct { // Test cases have been generated using AWS S3
   143  	ETags     []ETag
   144  	Multipart ETag
   145  }{
   146  	{
   147  		ETags:     []ETag{},
   148  		Multipart: ETag{},
   149  	},
   150  	{
   151  		ETags:     []ETag{must("b10a8db164e0754105b7a99be72e3fe5")},
   152  		Multipart: must("7b976cc68452e003eec7cb0eb631a19a-1"),
   153  	},
   154  	{
   155  		ETags:     []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6")},
   156  		Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"),
   157  	},
   158  	{
   159  		ETags:     []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("a096eb5968d607c2975fb2c4af9ab225"), must("b10a8db164e0754105b7a99be72e3fe5")},
   160  		Multipart: must("9a0d1febd9265f59f368ceb652770bc2-3"),
   161  	},
   162  	{ // Check that multipart ETags are ignored
   163  		ETags:     []ETag{must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("ceb8853ddc5086cc4ab9e149f8f09c88-1")},
   164  		Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"),
   165  	},
   166  	{ // Check that encrypted ETags are ignored
   167  		ETags: []ETag{
   168  			must("90402c78d2dccddee1e9e86222ce2c6361675f3529d26000ae2e900ff216b3cb59e130e092d8a2981e776f4d0bd60941"),
   169  			must("5f363e0e58a95f06cbe9bbc662c5dfb6"), must("5f363e0e58a95f06cbe9bbc662c5dfb6"),
   170  		},
   171  		Multipart: must("a7d414b9133d6483d9a1c4e04e856e3b-2"),
   172  	},
   173  }
   174  
   175  func TestMultipart(t *testing.T) {
   176  	for i, test := range multipartTests {
   177  		if multipart := Multipart(test.ETags...); !Equal(multipart, test.Multipart) {
   178  			t.Fatalf("Test %d: got %q - want %q", i, multipart, test.Multipart)
   179  		}
   180  	}
   181  }
   182  
   183  var fromContentMD5Tests = []struct {
   184  	Header     http.Header
   185  	ETag       ETag
   186  	ShouldFail bool
   187  }{
   188  	{Header: http.Header{}, ETag: nil}, // 0
   189  	{Header: http.Header{"Content-Md5": []string{"1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: must("d41d8cd98f00b204e9800998ecf8427e")},                             // 1
   190  	{Header: http.Header{"Content-Md5": []string{"sQqNsWTgdUEFt6mb5y4/5Q=="}}, ETag: must("b10a8db164e0754105b7a99be72e3fe5")},                             // 2
   191  	{Header: http.Header{"Content-MD5": []string{"1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: nil},                                                                  // 3 (Content-MD5 vs Content-Md5)
   192  	{Header: http.Header{"Content-Md5": []string{"sQqNsWTgdUEFt6mb5y4/5Q==", "1B2M2Y8AsgTpgAmY7PhCfg=="}}, ETag: must("b10a8db164e0754105b7a99be72e3fe5")}, // 4
   193  
   194  	{Header: http.Header{"Content-Md5": []string{""}}, ShouldFail: true},                                 // 5 (empty value)
   195  	{Header: http.Header{"Content-Md5": []string{"", "sQqNsWTgdUEFt6mb5y4/5Q=="}}, ShouldFail: true},     // 6 (empty value)
   196  	{Header: http.Header{"Content-Md5": []string{"d41d8cd98f00b204e9800998ecf8427e"}}, ShouldFail: true}, // 7 (content-md5 is invalid b64 / of invalid length)
   197  }
   198  
   199  func TestFromContentMD5(t *testing.T) {
   200  	for i, test := range fromContentMD5Tests {
   201  		ETag, err := FromContentMD5(test.Header)
   202  		if err != nil && !test.ShouldFail {
   203  			t.Fatalf("Test %d: failed to convert Content-MD5 to ETag: %v", i, err)
   204  		}
   205  		if err == nil && test.ShouldFail {
   206  			t.Fatalf("Test %d: should have failed but succeeded", i)
   207  		}
   208  		if err == nil {
   209  			if !Equal(ETag, test.ETag) {
   210  				t.Fatalf("Test %d: got %q - want %q", i, ETag, test.ETag)
   211  			}
   212  		}
   213  	}
   214  }
   215  
   216  func must(s string) ETag {
   217  	t, err := Parse(s)
   218  	if err != nil {
   219  		panic(err)
   220  	}
   221  	return t
   222  }