github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/redhat/parse_rpm_db_test.go (about) 1 package redhat 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 11 "github.com/anchore/syft/syft/file" 12 "github.com/anchore/syft/syft/pkg" 13 "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" 14 ) 15 16 var _ file.Resolver = (*rpmdbTestFileResolverMock)(nil) 17 18 type rpmdbTestFileResolverMock struct { 19 ignorePaths bool 20 } 21 22 func (r rpmdbTestFileResolverMock) FilesByExtension(extensions ...string) ([]file.Location, error) { 23 panic("not implemented") 24 } 25 26 func (r rpmdbTestFileResolverMock) FilesByBasename(filenames ...string) ([]file.Location, error) { 27 panic("not implemented") 28 } 29 30 func (r rpmdbTestFileResolverMock) FilesByBasenameGlob(globs ...string) ([]file.Location, error) { 31 panic("not implemented") 32 } 33 34 func (r rpmdbTestFileResolverMock) FileContentsByLocation(location file.Location) (io.ReadCloser, error) { 35 panic("not implemented") 36 } 37 38 func (r rpmdbTestFileResolverMock) AllLocations(_ context.Context) <-chan file.Location { 39 panic("not implemented") 40 } 41 42 func (r rpmdbTestFileResolverMock) FileMetadataByLocation(location file.Location) (file.Metadata, error) { 43 panic("not implemented") 44 } 45 46 func newTestFileResolver(ignorePaths bool) *rpmdbTestFileResolverMock { 47 return &rpmdbTestFileResolverMock{ 48 ignorePaths: ignorePaths, 49 } 50 } 51 52 func (r rpmdbTestFileResolverMock) HasPath(path string) bool { 53 return !r.ignorePaths 54 } 55 56 func (r *rpmdbTestFileResolverMock) FilesByPath(paths ...string) ([]file.Location, error) { 57 if r.ignorePaths { 58 // act as if no paths exist 59 return nil, nil 60 } 61 // act as if all files exist 62 var locations = make([]file.Location, len(paths)) 63 for i, p := range paths { 64 locations[i] = file.NewLocation(p) 65 } 66 return locations, nil 67 } 68 69 func (r *rpmdbTestFileResolverMock) FilesByGlob(...string) ([]file.Location, error) { 70 return nil, fmt.Errorf("not implemented") 71 } 72 73 func (r *rpmdbTestFileResolverMock) RelativeFileByPath(file.Location, string) *file.Location { 74 panic(fmt.Errorf("not implemented")) 75 return nil 76 } 77 78 func (r *rpmdbTestFileResolverMock) FilesByMIMEType(...string) ([]file.Location, error) { 79 return nil, fmt.Errorf("not implemented") 80 } 81 82 func TestParseRpmDB(t *testing.T) { 83 packagesLocation := file.NewLocation("test-fixtures/Packages") 84 tests := []struct { 85 fixture string 86 expected []pkg.Package 87 ignorePaths bool 88 }{ 89 { 90 fixture: "test-fixtures/Packages", 91 // we only surface package paths for files that exist (here we DO NOT expect a path) 92 ignorePaths: true, 93 expected: []pkg.Package{ 94 { 95 Name: "dive", 96 Version: "0.9.2-1", 97 PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm", 98 Locations: file.NewLocationSet(file.NewLocation("test-fixtures/Packages")), 99 Type: pkg.RpmPkg, 100 Licenses: pkg.NewLicenseSet( 101 pkg.NewLicenseFromLocations("MIT", packagesLocation), 102 ), 103 Metadata: pkg.RpmDBEntry{ 104 Name: "dive", 105 Epoch: nil, 106 Arch: "x86_64", 107 Release: "1", 108 Version: "0.9.2", 109 SourceRpm: "dive-0.9.2-1.src.rpm", 110 Size: 12406784, 111 Vendor: "", 112 ModularityLabel: strRef(""), 113 Provides: []string{"dive"}, 114 Files: []pkg.RpmFileRecord{}, 115 }, 116 }, 117 }, 118 }, 119 { 120 fixture: "test-fixtures/Packages", 121 // we only surface package paths for files that exist (here we expect a path) 122 ignorePaths: false, 123 expected: []pkg.Package{ 124 { 125 Name: "dive", 126 Version: "0.9.2-1", 127 PURL: "pkg:rpm/dive@0.9.2-1?arch=x86_64&upstream=dive-0.9.2-1.src.rpm", 128 Locations: file.NewLocationSet(packagesLocation), 129 Type: pkg.RpmPkg, 130 Licenses: pkg.NewLicenseSet( 131 pkg.NewLicenseFromLocations("MIT", packagesLocation), 132 ), 133 Metadata: pkg.RpmDBEntry{ 134 Name: "dive", 135 Epoch: nil, 136 Arch: "x86_64", 137 Release: "1", 138 Version: "0.9.2", 139 SourceRpm: "dive-0.9.2-1.src.rpm", 140 Size: 12406784, 141 Vendor: "", 142 ModularityLabel: strRef(""), 143 Provides: []string{"dive"}, 144 Files: []pkg.RpmFileRecord{ 145 { 146 Path: "/usr/local/bin/dive", 147 Mode: 33261, 148 Size: 12406784, 149 Digest: file.Digest{ 150 Algorithm: "sha256", 151 Value: "81d29f327ba23096b3c52ff6fe1c425641e618bc87b5c05ee377edc650afaa55", 152 }, 153 // note: there is no username, groupname, or flags for this RPM 154 }, 155 }, 156 }, 157 }, 158 }, 159 }, 160 } 161 162 for _, test := range tests { 163 t.Run(test.fixture, func(t *testing.T) { 164 pkgtest.NewCatalogTester(). 165 WithResolver(newTestFileResolver(test.ignorePaths)). 166 FromFile(t, test.fixture). 167 Expects(test.expected, nil). 168 TestParser(t, parseRpmDB) 169 }) 170 } 171 } 172 173 func TestToElVersion(t *testing.T) { 174 tests := []struct { 175 name string 176 entry pkg.RpmDBEntry 177 expected string 178 }{ 179 { 180 name: "no epoch", 181 entry: pkg.RpmDBEntry{ 182 Version: "1.2.3-4", 183 Release: "el7", 184 Arch: "x86-64", 185 }, 186 expected: "1.2.3-4-el7", 187 }, 188 { 189 name: "with 0 epoch", 190 entry: pkg.RpmDBEntry{ 191 Version: "1.2.3-4", 192 Release: "el7", 193 Arch: "x86-64", 194 Epoch: intRef(0), 195 }, 196 expected: "0:1.2.3-4-el7", 197 }, 198 { 199 name: "with non-zero epoch", 200 entry: pkg.RpmDBEntry{ 201 Version: "1.2.3-4", 202 Release: "el7", 203 Arch: "x86-64", 204 Epoch: intRef(12), 205 }, 206 expected: "12:1.2.3-4-el7", 207 }, 208 } 209 210 for _, test := range tests { 211 t.Run(test.name, func(t *testing.T) { 212 assert.Equal(t, test.expected, toELVersion(test.entry.Epoch, test.entry.Version, test.entry.Release)) 213 }) 214 } 215 } 216 217 func intRef(i int) *int { 218 return &i 219 } 220 221 func strRef(s string) *string { 222 return &s 223 }