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 }