github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/registry/remote/referrers_test.go (about) 1 /* 2 Copyright The ORAS Authors. 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 16 package remote 17 18 import ( 19 "reflect" 20 "testing" 21 22 "github.com/opencontainers/go-digest" 23 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 24 ) 25 26 func Test_buildReferrersTag(t *testing.T) { 27 tests := []struct { 28 name string 29 desc ocispec.Descriptor 30 want string 31 }{ 32 { 33 name: "zero digest", 34 desc: ocispec.Descriptor{ 35 Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000", 36 }, 37 want: "sha256-0000000000000000000000000000000000000000000000000000000000000000", 38 }, 39 { 40 name: "sha256", 41 desc: ocispec.Descriptor{ 42 Digest: "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 43 }, 44 want: "sha256-9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", 45 }, 46 { 47 name: "sha512", 48 desc: ocispec.Descriptor{ 49 Digest: "sha512:ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", 50 }, 51 want: "sha512-ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff", 52 }, 53 } 54 for _, tt := range tests { 55 t.Run(tt.name, func(t *testing.T) { 56 if got := buildReferrersTag(tt.desc); got != tt.want { 57 t.Errorf("getReferrersTag() = %v, want %v", got, tt.want) 58 } 59 }) 60 } 61 } 62 63 func Test_isReferrersFilterApplied(t *testing.T) { 64 tests := []struct { 65 name string 66 annotations map[string]string 67 requested string 68 want bool 69 }{ 70 { 71 name: "single filter applied, specified filter matches", 72 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "artifactType"}, 73 requested: "artifactType", 74 want: true, 75 }, 76 { 77 name: "single filter applied, specified filter does not match", 78 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo"}, 79 requested: "artifactType", 80 want: false, 81 }, 82 { 83 name: "multiple filters applied, specified filter matches", 84 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo,artifactType"}, 85 requested: "artifactType", 86 want: true, 87 }, 88 { 89 name: "multiple filters applied, specified filter does not match", 90 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo,bar"}, 91 requested: "artifactType", 92 want: false, 93 }, 94 { 95 name: "single filter applied, specified filter empty", 96 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: "foo"}, 97 requested: "", 98 want: false, 99 }, 100 { 101 name: "no filter applied", 102 annotations: map[string]string{}, 103 requested: "artifactType", 104 want: false, 105 }, 106 { 107 name: "empty filter applied", 108 annotations: map[string]string{ocispec.AnnotationReferrersFiltersApplied: ""}, 109 requested: "artifactType", 110 want: false, 111 }, 112 { 113 name: "no filter applied, specified filter empty", 114 annotations: map[string]string{}, 115 requested: "", 116 want: false, 117 }, 118 } 119 for _, tt := range tests { 120 t.Run(tt.name, func(t *testing.T) { 121 if got := isReferrersFilterApplied(tt.annotations, tt.requested); got != tt.want { 122 t.Errorf("isReferrersFilterApplied() = %v, want %v", got, tt.want) 123 } 124 }) 125 } 126 } 127 128 func Test_filterReferrers(t *testing.T) { 129 refs := []ocispec.Descriptor{ 130 { 131 MediaType: ocispec.MediaTypeArtifactManifest, 132 Size: 1, 133 Digest: digest.FromString("1"), 134 ArtifactType: "application/vnd.test", 135 }, 136 { 137 MediaType: ocispec.MediaTypeArtifactManifest, 138 Size: 2, 139 Digest: digest.FromString("2"), 140 ArtifactType: "application/vnd.foo", 141 }, 142 { 143 MediaType: ocispec.MediaTypeArtifactManifest, 144 Size: 3, 145 Digest: digest.FromString("3"), 146 ArtifactType: "application/vnd.bar", 147 }, 148 { 149 MediaType: ocispec.MediaTypeArtifactManifest, 150 Size: 4, 151 Digest: digest.FromString("4"), 152 ArtifactType: "application/vnd.test", 153 }, 154 { 155 MediaType: ocispec.MediaTypeArtifactManifest, 156 Size: 5, 157 Digest: digest.FromString("5"), 158 ArtifactType: "application/vnd.baz", 159 }, 160 } 161 got := filterReferrers(refs, "application/vnd.test") 162 want := []ocispec.Descriptor{ 163 { 164 MediaType: ocispec.MediaTypeArtifactManifest, 165 Size: 1, 166 Digest: digest.FromString("1"), 167 ArtifactType: "application/vnd.test", 168 }, 169 { 170 MediaType: ocispec.MediaTypeArtifactManifest, 171 Size: 4, 172 Digest: digest.FromString("4"), 173 ArtifactType: "application/vnd.test", 174 }, 175 } 176 if !reflect.DeepEqual(got, want) { 177 t.Errorf("filterReferrers() = %v, want %v", got, want) 178 } 179 } 180 181 func Test_filterReferrers_allMatch(t *testing.T) { 182 refs := []ocispec.Descriptor{ 183 { 184 MediaType: ocispec.MediaTypeArtifactManifest, 185 Size: 1, 186 Digest: digest.FromString("1"), 187 ArtifactType: "application/vnd.test", 188 }, 189 { 190 MediaType: ocispec.MediaTypeArtifactManifest, 191 Size: 4, 192 Digest: digest.FromString("2"), 193 ArtifactType: "application/vnd.test", 194 }, 195 { 196 MediaType: ocispec.MediaTypeArtifactManifest, 197 Size: 5, 198 Digest: digest.FromString("3"), 199 ArtifactType: "application/vnd.test", 200 }, 201 } 202 got := filterReferrers(refs, "application/vnd.test") 203 if !reflect.DeepEqual(got, refs) { 204 t.Errorf("filterReferrers() = %v, want %v", got, refs) 205 } 206 } 207 208 func Test_applyReferrerChanges(t *testing.T) { 209 descs := []ocispec.Descriptor{ 210 { 211 MediaType: ocispec.MediaTypeDescriptor, 212 Digest: "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", 213 Size: 3, 214 ArtifactType: "foo", 215 Annotations: map[string]string{"name": "foo"}, 216 }, 217 { 218 MediaType: ocispec.MediaTypeDescriptor, 219 Digest: "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", 220 Size: 3, 221 ArtifactType: "bar", 222 Annotations: map[string]string{"name": "bar"}, 223 }, 224 { 225 MediaType: ocispec.MediaTypeDescriptor, 226 Digest: "sha256:baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096", 227 Size: 3, 228 ArtifactType: "baz", 229 Annotations: map[string]string{"name": "baz"}, 230 }, 231 { 232 MediaType: ocispec.MediaTypeDescriptor, 233 Digest: "sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", 234 Size: 5, 235 ArtifactType: "hello", 236 Annotations: map[string]string{"name": "hello"}, 237 }, 238 { 239 MediaType: ocispec.MediaTypeDescriptor, 240 Digest: "sha256:82e35a63ceba37e9646434c5dd412ea577147f1e4a41ccde1614253187e3dbf9", 241 Size: 7, 242 ArtifactType: "goodbye", 243 Annotations: map[string]string{"name": "goodbye"}, 244 }, 245 } 246 247 tests := []struct { 248 name string 249 referrers []ocispec.Descriptor 250 referrerChanges []referrerChange 251 want []ocispec.Descriptor 252 wantErr error 253 }{ 254 { 255 name: "add to an empty list", 256 referrers: []ocispec.Descriptor{}, 257 referrerChanges: []referrerChange{ 258 {descs[0], referrerOperationAdd}, // add new 259 {descs[1], referrerOperationAdd}, // add new 260 {descs[2], referrerOperationAdd}, // add new 261 }, 262 want: []ocispec.Descriptor{ 263 descs[0], 264 descs[1], 265 descs[2], 266 }, 267 wantErr: nil, 268 }, 269 { 270 name: "add to a non-empty list", 271 referrers: []ocispec.Descriptor{ 272 descs[0], 273 descs[1], 274 }, 275 referrerChanges: []referrerChange{ 276 {descs[2], referrerOperationAdd}, // add new 277 {descs[1], referrerOperationAdd}, // add existing 278 {descs[1], referrerOperationAdd}, // add duplicate existing 279 {descs[3], referrerOperationAdd}, // add new 280 {descs[2], referrerOperationAdd}, // add duplicate new 281 }, 282 want: []ocispec.Descriptor{ 283 descs[0], 284 descs[1], 285 descs[2], 286 descs[3], 287 }, 288 wantErr: nil, 289 }, 290 { 291 name: "partially remove", 292 referrers: []ocispec.Descriptor{ 293 descs[0], 294 descs[1], 295 descs[2], 296 }, 297 referrerChanges: []referrerChange{ 298 {descs[2], referrerOperationRemove}, // remove existing 299 {descs[1], referrerOperationRemove}, // remove existing 300 {descs[3], referrerOperationRemove}, // remove non-existing 301 {descs[2], referrerOperationRemove}, // remove duplicate existing 302 {descs[4], referrerOperationRemove}, // remove non-existing 303 }, 304 want: []ocispec.Descriptor{ 305 descs[0], 306 }, 307 wantErr: nil, 308 }, 309 { 310 name: "remove all", 311 referrers: []ocispec.Descriptor{ 312 descs[0], 313 descs[1], 314 descs[2], 315 }, 316 referrerChanges: []referrerChange{ 317 {descs[2], referrerOperationRemove}, // remove existing 318 {descs[0], referrerOperationRemove}, // remove existing 319 {descs[1], referrerOperationRemove}, // remove existing 320 }, 321 want: []ocispec.Descriptor{}, 322 wantErr: nil, 323 }, 324 { 325 name: "add a new one and remove it", 326 referrers: []ocispec.Descriptor{ 327 descs[0], 328 descs[1], 329 descs[2], 330 }, 331 referrerChanges: []referrerChange{ 332 {descs[1], referrerOperationAdd}, // add existing 333 {descs[3], referrerOperationAdd}, // add new 334 {descs[3], referrerOperationAdd}, // add duplicate new 335 {descs[3], referrerOperationRemove}, // remove new 336 {descs[4], referrerOperationAdd}, // add new 337 }, 338 want: []ocispec.Descriptor{ 339 descs[0], 340 descs[1], 341 descs[2], 342 descs[4], 343 }, 344 wantErr: nil, 345 }, 346 { 347 name: "remove a new one and add it back", 348 referrers: []ocispec.Descriptor{ 349 descs[0], 350 descs[1], 351 descs[2], 352 }, 353 referrerChanges: []referrerChange{ 354 {descs[1], referrerOperationAdd}, // add existing 355 {descs[3], referrerOperationAdd}, // add new 356 {descs[3], referrerOperationRemove}, // remove new, 357 {descs[3], referrerOperationAdd}, // add new back 358 {descs[4], referrerOperationAdd}, // add new 359 }, 360 want: []ocispec.Descriptor{ 361 descs[0], 362 descs[1], 363 descs[2], 364 descs[3], 365 descs[4], 366 }, 367 wantErr: nil, 368 }, 369 { 370 name: "remove an existing one and add it back", 371 referrers: []ocispec.Descriptor{ 372 descs[0], 373 descs[1], 374 descs[2], 375 }, 376 referrerChanges: []referrerChange{ 377 {descs[2], referrerOperationRemove}, // remove existing 378 {descs[3], referrerOperationAdd}, // add new 379 {descs[2], referrerOperationAdd}, // add existing back 380 }, 381 want: []ocispec.Descriptor{ 382 descs[0], 383 descs[1], 384 descs[3], 385 descs[2], 386 }, 387 wantErr: nil, 388 }, 389 { 390 name: "list containing duplicate entries", 391 referrers: []ocispec.Descriptor{ 392 descs[0], 393 descs[1], 394 descs[0], // duplicate 395 descs[2], 396 descs[3], 397 descs[1], // duplicate 398 }, 399 referrerChanges: []referrerChange{ 400 {descs[2], referrerOperationAdd}, // add new 401 {descs[2], referrerOperationAdd}, // add duplicate new 402 {descs[3], referrerOperationRemove}, // remove existing 403 }, 404 want: []ocispec.Descriptor{ 405 descs[0], 406 descs[1], 407 descs[2], 408 }, 409 wantErr: nil, 410 }, 411 { 412 name: "list containing bad entries", 413 referrers: []ocispec.Descriptor{ 414 descs[0], 415 {}, 416 descs[1], 417 }, 418 referrerChanges: []referrerChange{ 419 {descs[2], referrerOperationAdd}, // add new 420 {descs[1], referrerOperationRemove}, // remove existing 421 }, 422 want: []ocispec.Descriptor{ 423 descs[0], 424 descs[2], 425 }, 426 wantErr: nil, 427 }, 428 { 429 name: "no update: same order", 430 referrers: []ocispec.Descriptor{ 431 descs[0], 432 descs[1], 433 descs[2], 434 }, 435 referrerChanges: []referrerChange{ 436 {descs[3], referrerOperationAdd}, // add new 437 {descs[2], referrerOperationRemove}, // remove existing 438 {descs[4], referrerOperationAdd}, // add new 439 {descs[4], referrerOperationRemove}, // remove new 440 {descs[2], referrerOperationAdd}, // add existing back 441 {descs[3], referrerOperationRemove}, // remove new 442 }, 443 want: nil, 444 wantErr: errNoReferrerUpdate, 445 }, 446 { 447 name: "no update: different order", 448 referrers: []ocispec.Descriptor{ 449 descs[0], 450 descs[1], 451 descs[2], 452 }, 453 referrerChanges: []referrerChange{ 454 {descs[2], referrerOperationRemove}, // remove existing 455 {descs[0], referrerOperationRemove}, // remove existing 456 {descs[0], referrerOperationAdd}, // add existing back 457 {descs[2], referrerOperationAdd}, // add existing back 458 }, 459 want: nil, 460 wantErr: errNoReferrerUpdate, // internal result: 2, 1, 0 461 }, 462 { 463 name: "no update: list containing duplicate entries", 464 referrers: []ocispec.Descriptor{ 465 descs[0], 466 descs[1], 467 descs[0], // duplicate 468 descs[2], 469 descs[1], // duplicate 470 }, 471 referrerChanges: []referrerChange{ 472 {descs[2], referrerOperationRemove}, // remove existing 473 {descs[0], referrerOperationRemove}, // remove existing 474 {descs[0], referrerOperationAdd}, // add existing back 475 {descs[2], referrerOperationAdd}, // add existing back 476 }, 477 want: []ocispec.Descriptor{ 478 descs[1], 479 descs[0], 480 descs[2], 481 }, 482 wantErr: nil, 483 }, 484 } 485 for _, tt := range tests { 486 t.Run(tt.name, func(t *testing.T) { 487 got, err := applyReferrerChanges(tt.referrers, tt.referrerChanges) 488 if err != tt.wantErr { 489 t.Errorf("applyReferrerChanges() error = %v, wantErr %v", err, tt.wantErr) 490 } 491 if !reflect.DeepEqual(got, tt.want) { 492 t.Errorf("applyReferrerChanges() = %v, want %v", got, tt.want) 493 } 494 }) 495 } 496 } 497 498 func Test_removeEmptyDescriptors(t *testing.T) { 499 descs := []ocispec.Descriptor{ 500 { 501 MediaType: ocispec.MediaTypeDescriptor, 502 Digest: "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", 503 Size: 3, 504 ArtifactType: "foo", 505 Annotations: map[string]string{"name": "foo"}, 506 }, 507 { 508 MediaType: ocispec.MediaTypeDescriptor, 509 Digest: "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", 510 Size: 3, 511 ArtifactType: "bar", 512 Annotations: map[string]string{"name": "bar"}, 513 }, 514 { 515 MediaType: ocispec.MediaTypeDescriptor, 516 Digest: "sha256:baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096", 517 Size: 3, 518 ArtifactType: "baz", 519 Annotations: map[string]string{"name": "baz"}, 520 }, 521 } 522 tests := []struct { 523 name string 524 descs []ocispec.Descriptor 525 hint int 526 want []ocispec.Descriptor 527 }{ 528 { 529 name: "empty list", 530 descs: []ocispec.Descriptor{}, 531 hint: 0, 532 want: []ocispec.Descriptor{}, 533 }, 534 { 535 name: "all non-empty", 536 descs: descs, 537 hint: len(descs), 538 want: descs, 539 }, 540 { 541 name: "all empty", 542 descs: []ocispec.Descriptor{ 543 {}, 544 {}, 545 {}, 546 }, 547 hint: 0, 548 want: []ocispec.Descriptor{}, 549 }, 550 { 551 name: "empty rear", 552 descs: []ocispec.Descriptor{ 553 descs[0], 554 {}, 555 descs[2], 556 {}, 557 {}, 558 }, 559 hint: 2, 560 want: []ocispec.Descriptor{ 561 descs[0], 562 descs[2], 563 }, 564 }, 565 { 566 name: "empty head", 567 descs: []ocispec.Descriptor{ 568 {}, 569 descs[0], 570 descs[1], 571 {}, 572 {}, 573 descs[2], 574 }, 575 hint: 3, 576 want: []ocispec.Descriptor{ 577 descs[0], 578 descs[1], 579 descs[2], 580 }, 581 }, 582 } 583 for _, tt := range tests { 584 t.Run(tt.name, func(t *testing.T) { 585 if got := removeEmptyDescriptors(tt.descs, tt.hint); !reflect.DeepEqual(got, tt.want) { 586 t.Errorf("removeEmptyDescriptors() = %v, want %v", got, tt.want) 587 } 588 }) 589 } 590 }