storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/crypto/metadata_test.go (about) 1 // MinIO Cloud Storage, (C) 2015, 2016, 2017, 2018 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 crypto 16 17 import ( 18 "bytes" 19 "encoding/base64" 20 "encoding/hex" 21 "testing" 22 23 "storj.io/minio/cmd/logger" 24 ) 25 26 var isMultipartTests = []struct { 27 Metadata map[string]string 28 Multipart bool 29 }{ 30 {Multipart: true, Metadata: map[string]string{MetaMultipart: ""}}, // 0 31 {Multipart: true, Metadata: map[string]string{"X-Minio-Internal-Encrypted-Multipart": ""}}, // 1 32 {Multipart: true, Metadata: map[string]string{MetaMultipart: "some-value"}}, // 2 33 {Multipart: false, Metadata: map[string]string{"": ""}}, // 3 34 {Multipart: false, Metadata: map[string]string{"X-Minio-Internal-EncryptedMultipart": ""}}, // 4 35 } 36 37 func TestIsMultipart(t *testing.T) { 38 for i, test := range isMultipartTests { 39 if isMultipart := IsMultiPart(test.Metadata); isMultipart != test.Multipart { 40 t.Errorf("Test %d: got '%v' - want '%v'", i, isMultipart, test.Multipart) 41 } 42 } 43 } 44 45 var isEncryptedTests = []struct { 46 Metadata map[string]string 47 Encrypted bool 48 }{ 49 {Encrypted: true, Metadata: map[string]string{MetaMultipart: ""}}, // 0 50 {Encrypted: true, Metadata: map[string]string{MetaIV: ""}}, // 1 51 {Encrypted: true, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2 52 {Encrypted: true, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3 53 {Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4 54 {Encrypted: true, Metadata: map[string]string{MetaKeyID: ""}}, // 5 55 {Encrypted: true, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6 56 {Encrypted: false, Metadata: map[string]string{"": ""}}, // 7 57 {Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8 58 } 59 60 func TestIsEncrypted(t *testing.T) { 61 for i, test := range isEncryptedTests { 62 if _, isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { 63 t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) 64 } 65 } 66 } 67 68 var s3IsEncryptedTests = []struct { 69 Metadata map[string]string 70 Encrypted bool 71 }{ 72 {Encrypted: false, Metadata: map[string]string{MetaMultipart: ""}}, // 0 73 {Encrypted: false, Metadata: map[string]string{MetaIV: ""}}, // 1 74 {Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2 75 {Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3 76 {Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4 77 {Encrypted: false, Metadata: map[string]string{MetaKeyID: ""}}, // 5 78 {Encrypted: false, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6 79 {Encrypted: false, Metadata: map[string]string{"": ""}}, // 7 80 {Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8 81 } 82 83 func TestS3IsEncrypted(t *testing.T) { 84 for i, test := range s3IsEncryptedTests { 85 if isEncrypted := S3.IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { 86 t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) 87 } 88 } 89 } 90 91 var ssecIsEncryptedTests = []struct { 92 Metadata map[string]string 93 Encrypted bool 94 }{ 95 {Encrypted: false, Metadata: map[string]string{MetaMultipart: ""}}, // 0 96 {Encrypted: false, Metadata: map[string]string{MetaIV: ""}}, // 1 97 {Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2 98 {Encrypted: true, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3 99 {Encrypted: false, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4 100 {Encrypted: false, Metadata: map[string]string{MetaKeyID: ""}}, // 5 101 {Encrypted: false, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6 102 {Encrypted: false, Metadata: map[string]string{"": ""}}, // 7 103 {Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8 104 } 105 106 func TestSSECIsEncrypted(t *testing.T) { 107 for i, test := range ssecIsEncryptedTests { 108 if isEncrypted := SSEC.IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { 109 t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) 110 } 111 } 112 } 113 114 var s3ParseMetadataTests = []struct { 115 Metadata map[string]string 116 ExpectedErr error 117 118 DataKey []byte 119 KeyID string 120 SealedKey SealedKey 121 }{ 122 {ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}}, // 0 123 { 124 ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{MetaIV: ""}, 125 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 126 }, // 1 127 { 128 ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-S3"), 129 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 130 }, // 2 131 { 132 ExpectedErr: Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3"), 133 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaDataEncryptionKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 134 }, // 3 135 { 136 ExpectedErr: Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3"), 137 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: ""}, 138 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 139 }, // 4 140 { 141 ExpectedErr: errInvalidInternalIV, 142 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: "", MetaDataEncryptionKey: ""}, 143 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 144 }, // 5 145 { 146 ExpectedErr: errInvalidInternalSealAlgorithm, 147 Metadata: map[string]string{ 148 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: "", MetaSealedKeyS3: "", MetaKeyID: "", MetaDataEncryptionKey: "", 149 }, 150 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 151 }, // 6 152 { 153 ExpectedErr: Errorf("The internal sealed key for SSE-S3 is invalid"), 154 Metadata: map[string]string{ 155 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, MetaSealedKeyS3: "", 156 MetaKeyID: "", MetaDataEncryptionKey: "", 157 }, 158 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, 159 }, // 7 160 { 161 ExpectedErr: Errorf("The internal sealed KMS data key for SSE-S3 is invalid"), 162 Metadata: map[string]string{ 163 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, 164 MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)), MetaKeyID: "key-1", 165 MetaDataEncryptionKey: ".MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=", // invalid base64 166 }, 167 DataKey: []byte{}, KeyID: "key-1", SealedKey: SealedKey{}, 168 }, // 8 169 { 170 ExpectedErr: nil, 171 Metadata: map[string]string{ 172 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, 173 MetaSealedKeyS3: base64.StdEncoding.EncodeToString(make([]byte, 64)), MetaKeyID: "", MetaDataEncryptionKey: "", 174 }, 175 DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{Algorithm: SealAlgorithm}, 176 }, // 9 177 { 178 ExpectedErr: nil, 179 Metadata: map[string]string{ 180 MetaIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), MetaAlgorithm: SealAlgorithm, 181 MetaSealedKeyS3: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), MetaKeyID: "key-1", 182 MetaDataEncryptionKey: base64.StdEncoding.EncodeToString(make([]byte, 48)), 183 }, 184 DataKey: make([]byte, 48), KeyID: "key-1", SealedKey: SealedKey{Algorithm: SealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}}, 185 }, // 10 186 } 187 188 func TestS3ParseMetadata(t *testing.T) { 189 for i, test := range s3ParseMetadataTests { 190 keyID, dataKey, sealedKey, err := S3.ParseMetadata(test.Metadata) 191 if err != nil && test.ExpectedErr == nil { 192 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 193 } 194 if err == nil && test.ExpectedErr != nil { 195 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 196 } 197 if err != nil && test.ExpectedErr != nil { 198 if err.Error() != test.ExpectedErr.Error() { 199 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 200 } 201 } 202 if !bytes.Equal(dataKey, test.DataKey) { 203 t.Errorf("Test %d: got data key '%v' - want data key '%v'", i, dataKey, test.DataKey) 204 } 205 if keyID != test.KeyID { 206 t.Errorf("Test %d: got key-ID '%v' - want key-ID '%v'", i, keyID, test.KeyID) 207 } 208 if sealedKey.Algorithm != test.SealedKey.Algorithm { 209 t.Errorf("Test %d: got sealed key algorithm '%v' - want sealed key algorithm '%v'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) 210 } 211 if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { 212 t.Errorf("Test %d: got sealed key '%v' - want sealed key '%v'", i, sealedKey.Key, test.SealedKey.Key) 213 } 214 if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { 215 t.Errorf("Test %d: got sealed key IV '%v' - want sealed key IV '%v'", i, sealedKey.IV, test.SealedKey.IV) 216 } 217 } 218 } 219 220 var ssecParseMetadataTests = []struct { 221 Metadata map[string]string 222 ExpectedErr error 223 224 SealedKey SealedKey 225 }{ 226 {ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0 227 {ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{MetaIV: ""}, SealedKey: SealedKey{}}, // 1 228 { 229 ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-C"), 230 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: ""}, SealedKey: SealedKey{}, 231 }, // 2 232 { 233 ExpectedErr: errInvalidInternalIV, 234 Metadata: map[string]string{MetaIV: "", MetaAlgorithm: "", MetaSealedKeySSEC: ""}, SealedKey: SealedKey{}, 235 }, // 3 236 { 237 ExpectedErr: errInvalidInternalSealAlgorithm, 238 Metadata: map[string]string{ 239 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: "", MetaSealedKeySSEC: "", 240 }, 241 SealedKey: SealedKey{}, 242 }, // 4 243 { 244 ExpectedErr: Errorf("The internal sealed key for SSE-C is invalid"), 245 Metadata: map[string]string{ 246 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, MetaSealedKeySSEC: "", 247 }, 248 SealedKey: SealedKey{}, 249 }, // 5 250 { 251 ExpectedErr: nil, 252 Metadata: map[string]string{ 253 MetaIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), MetaAlgorithm: SealAlgorithm, 254 MetaSealedKeySSEC: base64.StdEncoding.EncodeToString(make([]byte, 64)), 255 }, 256 SealedKey: SealedKey{Algorithm: SealAlgorithm}, 257 }, // 6 258 { 259 ExpectedErr: nil, 260 Metadata: map[string]string{ 261 MetaIV: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 31)...)), MetaAlgorithm: InsecureSealAlgorithm, 262 MetaSealedKeySSEC: base64.StdEncoding.EncodeToString(append([]byte{1}, make([]byte, 63)...)), 263 }, 264 SealedKey: SealedKey{Algorithm: InsecureSealAlgorithm, Key: [64]byte{1}, IV: [32]byte{1}}, 265 }, // 7 266 } 267 268 func TestCreateMultipartMetadata(t *testing.T) { 269 metadata := CreateMultipartMetadata(nil) 270 if v, ok := metadata[MetaMultipart]; !ok || v != "" { 271 t.Errorf("Metadata is missing the correct value for '%s': got '%s' - want '%s'", MetaMultipart, v, "") 272 } 273 } 274 275 func TestSSECParseMetadata(t *testing.T) { 276 for i, test := range ssecParseMetadataTests { 277 sealedKey, err := SSEC.ParseMetadata(test.Metadata) 278 if err != nil && test.ExpectedErr == nil { 279 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 280 } 281 if err == nil && test.ExpectedErr != nil { 282 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 283 } 284 if err != nil && test.ExpectedErr != nil { 285 if err.Error() != test.ExpectedErr.Error() { 286 t.Errorf("Test %d: got error '%v' - want error '%v'", i, err, test.ExpectedErr) 287 } 288 } 289 if sealedKey.Algorithm != test.SealedKey.Algorithm { 290 t.Errorf("Test %d: got sealed key algorithm '%v' - want sealed key algorithm '%v'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) 291 } 292 if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { 293 t.Errorf("Test %d: got sealed key '%v' - want sealed key '%v'", i, sealedKey.Key, test.SealedKey.Key) 294 } 295 if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { 296 t.Errorf("Test %d: got sealed key IV '%v' - want sealed key IV '%v'", i, sealedKey.IV, test.SealedKey.IV) 297 } 298 } 299 } 300 301 var s3CreateMetadataTests = []struct { 302 KeyID string 303 SealedDataKey []byte 304 SealedKey SealedKey 305 }{ 306 307 {KeyID: "", SealedDataKey: nil, SealedKey: SealedKey{Algorithm: SealAlgorithm}}, 308 {KeyID: "my-minio-key", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, 309 {KeyID: "cafebabe", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, 310 {KeyID: "deadbeef", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{IV: [32]byte{0xf7}, Key: [64]byte{0xea}, Algorithm: SealAlgorithm}}, 311 } 312 313 func TestS3CreateMetadata(t *testing.T) { 314 defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable) 315 logger.Disable = true 316 for i, test := range s3CreateMetadataTests { 317 metadata := S3.CreateMetadata(nil, test.KeyID, test.SealedDataKey, test.SealedKey) 318 keyID, kmsKey, sealedKey, err := S3.ParseMetadata(metadata) 319 if err != nil { 320 t.Errorf("Test %d: failed to parse metadata: %v", i, err) 321 continue 322 } 323 if keyID != test.KeyID { 324 t.Errorf("Test %d: Key-ID mismatch: got '%s' - want '%s'", i, keyID, test.KeyID) 325 } 326 if !bytes.Equal(kmsKey, test.SealedDataKey) { 327 t.Errorf("Test %d: sealed KMS data mismatch: got '%v' - want '%v'", i, kmsKey, test.SealedDataKey) 328 } 329 if sealedKey.Algorithm != test.SealedKey.Algorithm { 330 t.Errorf("Test %d: seal algorithm mismatch: got '%s' - want '%s'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) 331 } 332 if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { 333 t.Errorf("Test %d: IV mismatch: got '%v' - want '%v'", i, sealedKey.IV, test.SealedKey.IV) 334 } 335 if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { 336 t.Errorf("Test %d: sealed key mismatch: got '%v' - want '%v'", i, sealedKey.Key, test.SealedKey.Key) 337 } 338 } 339 340 defer func() { 341 if err := recover(); err == nil || err != logger.ErrCritical { 342 t.Errorf("Expected '%s' panic for invalid seal algorithm but got '%s'", logger.ErrCritical, err) 343 } 344 }() 345 _ = S3.CreateMetadata(nil, "", []byte{}, SealedKey{Algorithm: InsecureSealAlgorithm}) 346 } 347 348 var ssecCreateMetadataTests = []struct { 349 KeyID string 350 SealedDataKey []byte 351 SealedKey SealedKey 352 }{ 353 {KeyID: "", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, 354 {KeyID: "cafebabe", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, 355 {KeyID: "deadbeef", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{IV: [32]byte{0xf7}, Key: [64]byte{0xea}, Algorithm: SealAlgorithm}}, 356 } 357 358 func TestSSECCreateMetadata(t *testing.T) { 359 defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable) 360 logger.Disable = true 361 for i, test := range ssecCreateMetadataTests { 362 metadata := SSEC.CreateMetadata(nil, test.SealedKey) 363 sealedKey, err := SSEC.ParseMetadata(metadata) 364 if err != nil { 365 t.Errorf("Test %d: failed to parse metadata: %v", i, err) 366 continue 367 } 368 if sealedKey.Algorithm != test.SealedKey.Algorithm { 369 t.Errorf("Test %d: seal algorithm mismatch: got '%s' - want '%s'", i, sealedKey.Algorithm, test.SealedKey.Algorithm) 370 } 371 if !bytes.Equal(sealedKey.IV[:], test.SealedKey.IV[:]) { 372 t.Errorf("Test %d: IV mismatch: got '%v' - want '%v'", i, sealedKey.IV, test.SealedKey.IV) 373 } 374 if !bytes.Equal(sealedKey.Key[:], test.SealedKey.Key[:]) { 375 t.Errorf("Test %d: sealed key mismatch: got '%v' - want '%v'", i, sealedKey.Key, test.SealedKey.Key) 376 } 377 } 378 379 defer func() { 380 if err := recover(); err == nil || err != logger.ErrCritical { 381 t.Errorf("Expected '%s' panic for invalid seal algorithm but got '%s'", logger.ErrCritical, err) 382 } 383 }() 384 _ = SSEC.CreateMetadata(nil, SealedKey{Algorithm: InsecureSealAlgorithm}) 385 } 386 387 var isETagSealedTests = []struct { 388 ETag string 389 IsSealed bool 390 }{ 391 {ETag: "", IsSealed: false}, // 0 392 {ETag: "90682b8e8cc7609c4671e1d64c73fc30", IsSealed: false}, // 1 393 {ETag: "f201040c9dc593e39ea004dc1323699bcd", IsSealed: true}, // 2 not valid ciphertext but looks like sealed ETag 394 {ETag: "20000f00fba2ee2ae4845f725964eeb9e092edfabc7ab9f9239e8344341f769a51ce99b4801b0699b92b16a72fa94972", IsSealed: true}, // 3 395 } 396 397 func TestIsETagSealed(t *testing.T) { 398 for i, test := range isETagSealedTests { 399 etag, err := hex.DecodeString(test.ETag) 400 if err != nil { 401 t.Errorf("Test %d: failed to decode etag: %s", i, err) 402 } 403 if sealed := IsETagSealed(etag); sealed != test.IsSealed { 404 t.Errorf("Test %d: got %v - want %v", i, sealed, test.IsSealed) 405 } 406 } 407 } 408 409 var removeInternalEntriesTests = []struct { 410 Metadata, Expected map[string]string 411 }{ 412 { // 0 413 Metadata: map[string]string{ 414 MetaMultipart: "", 415 MetaIV: "", 416 MetaAlgorithm: "", 417 MetaSealedKeySSEC: "", 418 MetaSealedKeyS3: "", 419 MetaKeyID: "", 420 MetaDataEncryptionKey: "", 421 }, 422 Expected: map[string]string{}, 423 }, 424 { // 1 425 Metadata: map[string]string{ 426 MetaMultipart: "", 427 MetaIV: "", 428 "X-Amz-Meta-A": "X", 429 "X-Minio-Internal-B": "Y", 430 }, 431 Expected: map[string]string{ 432 "X-Amz-Meta-A": "X", 433 "X-Minio-Internal-B": "Y", 434 }, 435 }, 436 } 437 438 func TestRemoveInternalEntries(t *testing.T) { 439 isEqual := func(x, y map[string]string) bool { 440 if len(x) != len(y) { 441 return false 442 } 443 for k, v := range x { 444 if u, ok := y[k]; !ok || v != u { 445 return false 446 } 447 } 448 return true 449 } 450 451 for i, test := range removeInternalEntriesTests { 452 RemoveInternalEntries(test.Metadata) 453 if !isEqual(test.Metadata, test.Expected) { 454 t.Errorf("Test %d: got %v - want %v", i, test.Metadata, test.Expected) 455 } 456 } 457 }