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