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