github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/alpine/parse_apk_db_test.go (about) 1 package alpine 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "path/filepath" 8 "strings" 9 "testing" 10 11 "github.com/google/go-cmp/cmp" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 "github.com/anchore/syft/syft/file" 16 "github.com/anchore/syft/syft/linux" 17 "github.com/anchore/syft/syft/pkg" 18 "github.com/anchore/syft/syft/pkg/cataloger/generic" 19 "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" 20 ) 21 22 func TestExtraFileAttributes(t *testing.T) { 23 tests := []struct { 24 name string 25 expected pkg.ApkDBEntry 26 }{ 27 { 28 name: "test extra file attributes (checksum) are ignored", 29 expected: pkg.ApkDBEntry{ 30 Files: []pkg.ApkFileRecord{ 31 { 32 Path: "/usr", 33 }, 34 { 35 Path: "/usr/lib", 36 }, 37 { 38 Path: "/usr/lib/jvm", 39 }, 40 { 41 Path: "/usr/lib/jvm/java-1.8-openjdk", 42 }, 43 { 44 Path: "/usr/lib/jvm/java-1.8-openjdk/bin", 45 }, 46 { 47 Path: "/usr/lib/jvm/java-1.8-openjdk/bin/policytool", 48 OwnerUID: "0", 49 OwnerGID: "0", 50 Permissions: "755", 51 Digest: &file.Digest{ 52 Algorithm: "'Q1'+base64(sha1)", 53 Value: "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=", 54 }, 55 }, 56 }, 57 }, 58 }, 59 } 60 61 for _, test := range tests { 62 t.Run(test.name, func(t *testing.T) { 63 fixturePath := "test-fixtures/extra-file-attributes" 64 lrc := newLocationReadCloser(t, fixturePath) 65 66 pkgs, _, err := parseApkDB(context.Background(), nil, new(generic.Environment), lrc) 67 assert.NoError(t, err) 68 require.Len(t, pkgs, 1) 69 metadata := pkgs[0].Metadata.(pkg.ApkDBEntry) 70 71 if diff := cmp.Diff(test.expected.Files, metadata.Files); diff != "" { 72 t.Errorf("Files mismatch (-want +got):\n%s", diff) 73 } 74 }) 75 } 76 } 77 78 func TestSinglePackageDetails(t *testing.T) { 79 tests := []struct { 80 fixture string 81 expected pkg.Package 82 }{ 83 { 84 fixture: "test-fixtures/single", 85 expected: pkg.Package{ 86 Name: "musl-utils", 87 Version: "1.1.24-r2", 88 Licenses: pkg.NewLicenseSet( 89 pkg.NewLicense("MIT"), 90 pkg.NewLicense("BSD"), 91 pkg.NewLicense("GPL2+"), 92 ), 93 Type: pkg.ApkPkg, 94 Metadata: pkg.ApkDBEntry{ 95 Package: "musl-utils", 96 OriginPackage: "musl", 97 Version: "1.1.24-r2", 98 Description: "the musl c library (libc) implementation", 99 Maintainer: "Timo Teräs <timo.teras@iki.fi>", 100 Architecture: "x86_64", 101 URL: "https://musl.libc.org/", 102 Size: 37944, 103 InstalledSize: 151552, 104 Dependencies: []string{"scanelf", "so:libc.musl-x86_64.so.1"}, 105 Provides: []string{"cmd:getconf", "cmd:getent", "cmd:iconv", "cmd:ldconfig", "cmd:ldd"}, 106 Checksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", 107 GitCommit: "4024cc3b29ad4c65544ad068b8f59172b5494306", 108 Files: []pkg.ApkFileRecord{ 109 { 110 Path: "/sbin", 111 }, 112 { 113 Path: "/sbin/ldconfig", 114 OwnerUID: "0", 115 OwnerGID: "0", 116 Permissions: "755", 117 Digest: &file.Digest{ 118 Algorithm: "'Q1'+base64(sha1)", 119 Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", 120 }, 121 }, 122 { 123 Path: "/usr", 124 }, 125 { 126 Path: "/usr/bin", 127 }, 128 { 129 Path: "/usr/bin/iconv", 130 OwnerUID: "0", 131 OwnerGID: "0", 132 Permissions: "755", 133 Digest: &file.Digest{ 134 Algorithm: "'Q1'+base64(sha1)", 135 Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", 136 }, 137 }, 138 { 139 Path: "/usr/bin/ldd", 140 OwnerUID: "0", 141 OwnerGID: "0", 142 Permissions: "755", 143 Digest: &file.Digest{ 144 Algorithm: "'Q1'+base64(sha1)", 145 Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", 146 }, 147 }, 148 { 149 Path: "/usr/bin/getconf", 150 OwnerUID: "0", 151 OwnerGID: "0", 152 Permissions: "755", 153 Digest: &file.Digest{ 154 Algorithm: "'Q1'+base64(sha1)", 155 Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", 156 }, 157 }, 158 { 159 Path: "/usr/bin/getent", 160 OwnerUID: "0", 161 OwnerGID: "0", 162 Permissions: "755", 163 Digest: &file.Digest{ 164 Algorithm: "'Q1'+base64(sha1)", 165 Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", 166 }, 167 }, 168 }, 169 }, 170 }, 171 }, 172 { 173 fixture: "test-fixtures/empty-deps-and-provides", 174 expected: pkg.Package{ 175 Name: "alpine-baselayout-data", 176 Version: "3.4.0-r0", 177 Licenses: pkg.NewLicenseSet( 178 pkg.NewLicense("GPL-2.0-only"), 179 ), 180 Type: pkg.ApkPkg, 181 Metadata: pkg.ApkDBEntry{ 182 Package: "alpine-baselayout-data", 183 OriginPackage: "alpine-baselayout", 184 Version: "3.4.0-r0", 185 Description: "Alpine base dir structure and init scripts", 186 Maintainer: "Natanael Copa <ncopa@alpinelinux.org>", 187 Architecture: "x86_64", 188 URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", 189 Size: 11664, 190 InstalledSize: 77824, 191 Dependencies: []string{}, 192 Provides: []string{}, 193 Checksum: "Q15ffjKT28lB7iSXjzpI/eDdYRCwM=", 194 GitCommit: "bd965a7ebf7fd8f07d7a0cc0d7375bf3e4eb9b24", 195 Files: []pkg.ApkFileRecord{ 196 {Path: "/etc"}, 197 {Path: "/etc/fstab"}, 198 {Path: "/etc/group"}, 199 {Path: "/etc/hostname"}, 200 {Path: "/etc/hosts"}, 201 {Path: "/etc/inittab"}, 202 {Path: "/etc/modules"}, 203 {Path: "/etc/mtab", OwnerUID: "0", OwnerGID: "0", Permissions: "0777"}, 204 {Path: "/etc/nsswitch.conf"}, 205 {Path: "/etc/passwd"}, 206 {Path: "/etc/profile"}, 207 {Path: "/etc/protocols"}, 208 {Path: "/etc/services"}, 209 {Path: "/etc/shadow", OwnerUID: "0", OwnerGID: "148", Permissions: "0640"}, 210 {Path: "/etc/shells"}, 211 {Path: "/etc/sysctl.conf"}, 212 }, 213 }, 214 }, 215 }, 216 { 217 fixture: "test-fixtures/base", 218 expected: pkg.Package{ 219 Name: "alpine-baselayout", 220 Version: "3.2.0-r6", 221 Licenses: pkg.NewLicenseSet( 222 pkg.NewLicense("GPL-2.0-only"), 223 ), 224 Type: pkg.ApkPkg, 225 PURL: "", 226 Metadata: pkg.ApkDBEntry{ 227 Package: "alpine-baselayout", 228 OriginPackage: "alpine-baselayout", 229 Version: "3.2.0-r6", 230 Description: "Alpine base dir structure and init scripts", 231 Maintainer: "Natanael Copa <ncopa@alpinelinux.org>", 232 Architecture: "x86_64", 233 URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", 234 Size: 19917, 235 InstalledSize: 409600, 236 Dependencies: []string{"/bin/sh", "so:libc.musl-x86_64.so.1"}, 237 Provides: []string{"cmd:mkmntdirs"}, 238 Checksum: "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=", 239 GitCommit: "e1c51734fa96fa4bac92e9f14a474324c67916fc", 240 Files: []pkg.ApkFileRecord{ 241 { 242 Path: "/dev", 243 }, 244 { 245 Path: "/dev/pts", 246 }, 247 { 248 Path: "/dev/shm", 249 }, 250 { 251 Path: "/etc", 252 }, 253 { 254 Path: "/etc/fstab", 255 Digest: &file.Digest{ 256 Algorithm: "'Q1'+base64(sha1)", 257 Value: "Q11Q7hNe8QpDS531guqCdrXBzoA/o=", 258 }, 259 }, 260 { 261 Path: "/etc/group", 262 Digest: &file.Digest{ 263 Algorithm: "'Q1'+base64(sha1)", 264 Value: "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=", 265 }, 266 }, 267 { 268 Path: "/etc/hostname", 269 Digest: &file.Digest{ 270 Algorithm: "'Q1'+base64(sha1)", 271 Value: "Q16nVwYVXP/tChvUPdukVD2ifXOmc=", 272 }, 273 }, 274 { 275 Path: "/etc/hosts", 276 Digest: &file.Digest{ 277 Algorithm: "'Q1'+base64(sha1)", 278 Value: "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=", 279 }, 280 }, 281 { 282 Path: "/etc/inittab", 283 Digest: &file.Digest{ 284 Algorithm: "'Q1'+base64(sha1)", 285 Value: "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=", 286 }, 287 }, 288 { 289 Path: "/etc/modules", 290 Digest: &file.Digest{ 291 Algorithm: "'Q1'+base64(sha1)", 292 Value: "Q1toogjUipHGcMgECgPJX64SwUT1M=", 293 }, 294 }, 295 { 296 Path: "/etc/motd", 297 Digest: &file.Digest{ 298 Algorithm: "'Q1'+base64(sha1)", 299 Value: "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=", 300 }, 301 }, 302 { 303 Path: "/etc/mtab", 304 OwnerUID: "0", 305 OwnerGID: "0", 306 Permissions: "777", 307 Digest: &file.Digest{ 308 Algorithm: "'Q1'+base64(sha1)", 309 Value: "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=", 310 }, 311 }, 312 { 313 Path: "/etc/passwd", 314 Digest: &file.Digest{ 315 Algorithm: "'Q1'+base64(sha1)", 316 Value: "Q1TchuuLUfur0izvfZQZxgN/LJhB8=", 317 }, 318 }, 319 { 320 Path: "/etc/profile", 321 Digest: &file.Digest{ 322 Algorithm: "'Q1'+base64(sha1)", 323 Value: "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=", 324 }, 325 }, 326 { 327 Path: "/etc/protocols", 328 Digest: &file.Digest{ 329 Algorithm: "'Q1'+base64(sha1)", 330 Value: "Q13FqXUnvuOpMDrH/6rehxuYAEE34=", 331 }, 332 }, 333 { 334 Path: "/etc/services", 335 Digest: &file.Digest{ 336 Algorithm: "'Q1'+base64(sha1)", 337 Value: "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=", 338 }, 339 }, 340 { 341 Path: "/etc/shadow", 342 OwnerUID: "0", 343 OwnerGID: "42", 344 Permissions: "640", 345 Digest: &file.Digest{ 346 Algorithm: "'Q1'+base64(sha1)", 347 Value: "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=", 348 }, 349 }, 350 { 351 Path: "/etc/shells", 352 Digest: &file.Digest{ 353 Algorithm: "'Q1'+base64(sha1)", 354 Value: "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=", 355 }, 356 }, 357 { 358 Path: "/etc/sysctl.conf", 359 Digest: &file.Digest{ 360 Algorithm: "'Q1'+base64(sha1)", 361 Value: "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=", 362 }, 363 }, 364 { 365 Path: "/etc/apk", 366 }, 367 { 368 Path: "/etc/conf.d", 369 }, 370 { 371 Path: "/etc/crontabs", 372 }, 373 { 374 Path: "/etc/crontabs/root", 375 OwnerUID: "0", 376 OwnerGID: "0", 377 Permissions: "600", 378 Digest: &file.Digest{ 379 Algorithm: "'Q1'+base64(sha1)", 380 Value: "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=", 381 }, 382 }, 383 { 384 Path: "/etc/init.d", 385 }, 386 { 387 Path: "/etc/modprobe.d", 388 }, 389 { 390 Path: "/etc/modprobe.d/aliases.conf", 391 Digest: &file.Digest{ 392 Algorithm: "'Q1'+base64(sha1)", 393 Value: "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=", 394 }, 395 }, 396 { 397 Path: "/etc/modprobe.d/blacklist.conf", 398 Digest: &file.Digest{ 399 Algorithm: "'Q1'+base64(sha1)", 400 Value: "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=", 401 }, 402 }, 403 { 404 Path: "/etc/modprobe.d/i386.conf", 405 Digest: &file.Digest{ 406 Algorithm: "'Q1'+base64(sha1)", 407 Value: "Q1pnay/njn6ol9cCssL7KiZZ8etlc=", 408 }, 409 }, 410 { 411 Path: "/etc/modprobe.d/kms.conf", 412 Digest: &file.Digest{ 413 Algorithm: "'Q1'+base64(sha1)", 414 Value: "Q1ynbLn3GYDpvajba/ldp1niayeog=", 415 }, 416 }, 417 { 418 Path: "/etc/modules-load.d", 419 }, 420 { 421 Path: "/etc/network", 422 }, 423 { 424 Path: "/etc/network/if-down.d", 425 }, 426 { 427 Path: "/etc/network/if-post-down.d", 428 }, 429 { 430 Path: "/etc/network/if-pre-up.d", 431 }, 432 { 433 Path: "/etc/network/if-up.d", 434 }, 435 { 436 Path: "/etc/opt", 437 }, 438 { 439 Path: "/etc/periodic", 440 }, 441 { 442 Path: "/etc/periodic/15min", 443 }, 444 { 445 Path: "/etc/periodic/daily", 446 }, 447 { 448 Path: "/etc/periodic/hourly", 449 }, 450 { 451 Path: "/etc/periodic/monthly", 452 }, 453 { 454 Path: "/etc/periodic/weekly", 455 }, 456 { 457 Path: "/etc/profile.d", 458 }, 459 { 460 Path: "/etc/profile.d/color_prompt", 461 Digest: &file.Digest{ 462 Algorithm: "'Q1'+base64(sha1)", 463 Value: "Q10wL23GuSCVfumMRgakabUI6EsSk=", 464 }, 465 }, 466 { 467 Path: "/etc/profile.d/locale", 468 Digest: &file.Digest{ 469 Algorithm: "'Q1'+base64(sha1)", 470 Value: "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=", 471 }, 472 }, 473 { 474 Path: "/etc/sysctl.d", 475 }, 476 { 477 Path: "/home", 478 }, 479 { 480 Path: "/lib", 481 }, 482 { 483 Path: "/lib/firmware", 484 }, 485 { 486 Path: "/lib/mdev", 487 }, 488 { 489 Path: "/lib/modules-load.d", 490 }, 491 { 492 Path: "/lib/sysctl.d", 493 }, 494 { 495 Path: "/lib/sysctl.d/00-alpine.conf", 496 Digest: &file.Digest{ 497 Algorithm: "'Q1'+base64(sha1)", 498 Value: "Q1HpElzW1xEgmKfERtTy7oommnq6c=", 499 }, 500 }, 501 { 502 Path: "/media", 503 }, 504 { 505 Path: "/media/cdrom", 506 }, 507 { 508 Path: "/media/floppy", 509 }, 510 { 511 Path: "/media/usb", 512 }, 513 { 514 Path: "/mnt", 515 }, 516 { 517 Path: "/opt", 518 }, 519 { 520 Path: "/proc", 521 }, 522 { 523 Path: "/root", 524 OwnerUID: "0", 525 OwnerGID: "0", 526 Permissions: "700", 527 }, 528 { 529 Path: "/run", 530 }, 531 { 532 Path: "/sbin", 533 }, 534 { 535 Path: "/sbin/mkmntdirs", 536 OwnerUID: "0", 537 OwnerGID: "0", 538 Permissions: "755", 539 Digest: &file.Digest{ 540 Algorithm: "'Q1'+base64(sha1)", 541 Value: "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=", 542 }, 543 }, 544 { 545 Path: "/srv", 546 }, 547 { 548 Path: "/sys", 549 }, 550 { 551 Path: "/tmp", 552 OwnerUID: "0", 553 OwnerGID: "0", 554 Permissions: "1777", 555 }, 556 { 557 Path: "/usr", 558 }, 559 { 560 Path: "/usr/lib", 561 }, 562 { 563 Path: "/usr/lib/modules-load.d", 564 }, 565 { 566 Path: "/usr/local", 567 }, 568 { 569 Path: "/usr/local/bin", 570 }, 571 { 572 Path: "/usr/local/lib", 573 }, 574 { 575 Path: "/usr/local/share", 576 }, 577 { 578 Path: "/usr/sbin", 579 }, 580 { 581 Path: "/usr/share", 582 }, 583 { 584 Path: "/usr/share/man", 585 }, 586 { 587 Path: "/usr/share/misc", 588 }, 589 { 590 Path: "/var", 591 }, 592 { 593 Path: "/var/run", 594 OwnerUID: "0", 595 OwnerGID: "0", 596 Permissions: "777", 597 Digest: &file.Digest{ 598 Algorithm: "'Q1'+base64(sha1)", 599 Value: "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=", 600 }, 601 }, 602 { 603 Path: "/var/cache", 604 }, 605 { 606 Path: "/var/cache/misc", 607 }, 608 { 609 Path: "/var/empty", 610 OwnerUID: "0", 611 OwnerGID: "0", 612 Permissions: "555", 613 }, 614 { 615 Path: "/var/lib", 616 }, 617 { 618 Path: "/var/lib/misc", 619 }, 620 { 621 Path: "/var/local", 622 }, 623 { 624 Path: "/var/lock", 625 }, 626 { 627 Path: "/var/lock/subsys", 628 }, 629 { 630 Path: "/var/log", 631 }, 632 { 633 Path: "/var/mail", 634 }, 635 { 636 Path: "/var/opt", 637 }, 638 { 639 Path: "/var/spool", 640 }, 641 { 642 Path: "/var/spool/mail", 643 OwnerUID: "0", 644 OwnerGID: "0", 645 Permissions: "777", 646 Digest: &file.Digest{ 647 Algorithm: "'Q1'+base64(sha1)", 648 Value: "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=", 649 }, 650 }, 651 { 652 Path: "/var/spool/cron", 653 }, 654 { 655 Path: "/var/spool/cron/crontabs", 656 OwnerUID: "0", 657 OwnerGID: "0", 658 Permissions: "777", 659 Digest: &file.Digest{ 660 Algorithm: "'Q1'+base64(sha1)", 661 Value: "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=", 662 }, 663 }, 664 { 665 Path: "/var/tmp", 666 OwnerUID: "0", 667 OwnerGID: "0", 668 Permissions: "1777", 669 }, 670 }, 671 }, 672 }, 673 }, 674 } 675 676 for _, test := range tests { 677 t.Run(test.fixture, func(t *testing.T) { 678 fixtureLocation := file.NewLocation(test.fixture) 679 test.expected.Locations = file.NewLocationSet(fixtureLocation) 680 licenses := test.expected.Licenses.ToSlice() 681 for i := range licenses { 682 licenses[i].Locations.Add(fixtureLocation) 683 } 684 test.expected.Licenses = pkg.NewLicenseSet(licenses...) 685 pkgtest.TestFileParser(t, test.fixture, parseApkDB, []pkg.Package{test.expected}, nil) 686 }) 687 } 688 } 689 690 func Test_processChecksum(t *testing.T) { 691 tests := []struct { 692 name string 693 value string 694 want file.Digest 695 }{ 696 { 697 name: "md5", 698 value: "38870ede8700535d7382ff66a46fcc2f", 699 want: file.Digest{ 700 Algorithm: "md5", 701 Value: "38870ede8700535d7382ff66a46fcc2f", 702 }, 703 }, 704 { 705 name: "sha1", 706 value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", 707 want: file.Digest{ 708 Algorithm: "'Q1'+base64(sha1)", 709 Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", 710 }, 711 }, 712 } 713 714 for _, test := range tests { 715 t.Run(test.name, func(t *testing.T) { 716 assert.Equal(t, &test.want, processChecksum(test.value)) 717 }) 718 } 719 } 720 721 func Test_parseApkDB_expectedPkgNames(t *testing.T) { 722 tests := []struct { 723 fixture string 724 wantPkgNames []string 725 wantErr assert.ErrorAssertionFunc 726 }{ 727 { 728 fixture: "very-large-entries", 729 wantPkgNames: []string{ 730 "ca-certificates-bundle", 731 "glibc-locale-posix", 732 "wolfi-baselayout", 733 "glibc", 734 "libcrypto3", 735 "libssl3", 736 "zlib", 737 "apk-tools", 738 "ncurses-terminfo-base", 739 "ncurses", 740 "bash", 741 "libcap", 742 "bubblewrap", 743 "busybox", 744 "libbrotlicommon1", 745 "libbrotlidec1", 746 "libnghttp2-14", 747 "libcurl4", 748 "curl", 749 "expat", 750 "libpcre2-8-0", 751 "git", 752 "binutils", 753 "libstdc++-dev", 754 "libgcc", 755 "libstdc++", 756 "gmp", 757 "isl", 758 "mpfr", 759 "mpc", 760 "gcc", 761 "linux-headers", 762 "glibc-dev", 763 "make", 764 "pkgconf", 765 "build-base", 766 "go", 767 "tree", 768 "sdk", 769 }, 770 wantErr: assert.NoError, 771 }, 772 } 773 774 for _, test := range tests { 775 t.Run(test.fixture, func(t *testing.T) { 776 fixturePath := filepath.Join("test-fixtures", test.fixture) 777 lrc := newLocationReadCloser(t, fixturePath) 778 779 pkgs, _, err := parseApkDB(context.Background(), nil, new(generic.Environment), lrc) 780 test.wantErr(t, err) 781 782 names := toPackageNames(pkgs) 783 if diff := cmp.Diff(test.wantPkgNames, names); diff != "" { 784 t.Errorf("Packages mismatch (-want +got):\n%s", diff) 785 } 786 }) 787 } 788 } 789 790 func toPackageNames(pkgs []pkg.Package) []string { 791 names := make([]string, 0, len(pkgs)) 792 for _, p := range pkgs { 793 names = append(names, p.Name) 794 } 795 796 return names 797 } 798 799 func newLocationReadCloser(t *testing.T, path string) file.LocationReadCloser { 800 f, err := os.Open(path) 801 require.NoError(t, err) 802 t.Cleanup(func() { f.Close() }) 803 804 return file.NewLocationReadCloser(file.NewLocation(path), f) 805 } 806 807 func TestParseReleasesFromAPKRepository(t *testing.T) { 808 tests := []struct { 809 repos string 810 want []linux.Release 811 desc string 812 }{ 813 { 814 "https://foo.alpinelinux.org/alpine/v3.14/main", 815 []linux.Release{ 816 {Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"}, 817 }, 818 "single repo", 819 }, 820 { 821 `https://foo.alpinelinux.org/alpine/v3.14/main 822 https://foo.alpinelinux.org/alpine/v3.14/community`, 823 []linux.Release{ 824 {Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"}, 825 {Name: "Alpine Linux", ID: "alpine", VersionID: "3.14"}, 826 }, 827 "multiple repos", 828 }, 829 { 830 ``, 831 nil, 832 "empty", 833 }, 834 { 835 `https://foo.bar.org/alpine/v3.14/main 836 https://foo.them.org/alpine/v3.14/community`, 837 nil, 838 "invalid repos", 839 }, 840 } 841 for _, tt := range tests { 842 t.Run(tt.desc, func(t *testing.T) { 843 reposReader := io.NopCloser(strings.NewReader(tt.repos)) 844 got := parseReleasesFromAPKRepository(file.LocationReadCloser{ 845 Location: file.NewLocation("test"), 846 ReadCloser: reposReader, 847 }) 848 assert.Equal(t, tt.want, got) 849 }) 850 } 851 }