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  }