github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/format/syftjson/to_format_model_test.go (about) 1 package syftjson 2 3 import ( 4 "encoding/json" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/google/go-cmp/cmp/cmpopts" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 stereoscopeFile "github.com/anchore/stereoscope/pkg/file" 13 "github.com/anchore/syft/syft/file" 14 "github.com/anchore/syft/syft/format/syftjson/model" 15 "github.com/anchore/syft/syft/internal/sourcemetadata" 16 "github.com/anchore/syft/syft/pkg" 17 "github.com/anchore/syft/syft/source" 18 ) 19 20 func Test_toSourceModel_IgnoreBase(t *testing.T) { 21 tests := []struct { 22 name string 23 src source.Description 24 }{ 25 { 26 name: "directory", 27 src: source.Description{ 28 ID: "test-id", 29 Metadata: source.DirectoryMetadata{ 30 Path: "some/path", 31 Base: "some/base", 32 }, 33 }, 34 }, 35 } 36 for _, test := range tests { 37 t.Run(test.name, func(t *testing.T) { 38 // assert the model transformation is correct 39 actual := toSourceModel(test.src) 40 41 by, err := json.Marshal(actual) 42 require.NoError(t, err) 43 assert.NotContains(t, string(by), "some/base") 44 }) 45 } 46 } 47 48 func Test_toSourceModel(t *testing.T) { 49 tracker := sourcemetadata.NewCompletionTester(t) 50 51 tests := []struct { 52 name string 53 src source.Description 54 expected model.Source 55 }{ 56 { 57 name: "directory", 58 src: source.Description{ 59 ID: "test-id", 60 Name: "some-name", 61 Version: "some-version", 62 Metadata: source.DirectoryMetadata{ 63 Path: "some/path", 64 Base: "some/base", 65 }, 66 }, 67 expected: model.Source{ 68 ID: "test-id", 69 Name: "some-name", 70 Version: "some-version", 71 Type: "directory", 72 Metadata: source.DirectoryMetadata{ 73 Path: "some/path", 74 Base: "some/base", 75 }, 76 }, 77 }, 78 { 79 name: "file", 80 src: source.Description{ 81 ID: "test-id", 82 Name: "some-name", 83 Version: "some-version", 84 Metadata: source.FileMetadata{ 85 Path: "some/path", 86 Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}}, 87 MIMEType: "text/plain", 88 }, 89 }, 90 expected: model.Source{ 91 ID: "test-id", 92 Name: "some-name", 93 Version: "some-version", 94 Type: "file", 95 Metadata: source.FileMetadata{ 96 Path: "some/path", 97 Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}}, 98 MIMEType: "text/plain", 99 }, 100 }, 101 }, 102 { 103 name: "image", 104 src: source.Description{ 105 ID: "test-id", 106 Name: "some-name", 107 Version: "some-version", 108 Metadata: source.ImageMetadata{ 109 UserInput: "user-input", 110 ID: "id...", 111 ManifestDigest: "digest...", 112 MediaType: "type...", 113 }, 114 }, 115 expected: model.Source{ 116 ID: "test-id", 117 Name: "some-name", 118 Version: "some-version", 119 Type: "image", 120 Metadata: source.ImageMetadata{ 121 UserInput: "user-input", 122 ID: "id...", 123 ManifestDigest: "digest...", 124 MediaType: "type...", 125 RepoDigests: []string{}, 126 Tags: []string{}, 127 }, 128 }, 129 }, 130 // below are regression tests for when the name/version are not provided 131 // historically we've hoisted up the name/version from the metadata, now it is a simple pass-through 132 { 133 name: "directory - no name/version", 134 src: source.Description{ 135 ID: "test-id", 136 Metadata: source.DirectoryMetadata{ 137 Path: "some/path", 138 Base: "some/base", 139 }, 140 }, 141 expected: model.Source{ 142 ID: "test-id", 143 Type: "directory", 144 Metadata: source.DirectoryMetadata{ 145 Path: "some/path", 146 Base: "some/base", 147 }, 148 }, 149 }, 150 { 151 name: "file - no name/version", 152 src: source.Description{ 153 ID: "test-id", 154 Metadata: source.FileMetadata{ 155 Path: "some/path", 156 Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}}, 157 MIMEType: "text/plain", 158 }, 159 }, 160 expected: model.Source{ 161 ID: "test-id", 162 Type: "file", 163 Metadata: source.FileMetadata{ 164 Path: "some/path", 165 Digests: []file.Digest{{Algorithm: "sha256", Value: "some-digest"}}, 166 MIMEType: "text/plain", 167 }, 168 }, 169 }, 170 { 171 name: "image - no name/version", 172 src: source.Description{ 173 ID: "test-id", 174 Metadata: source.ImageMetadata{ 175 UserInput: "user-input", 176 ID: "id...", 177 ManifestDigest: "digest...", 178 MediaType: "type...", 179 }, 180 }, 181 expected: model.Source{ 182 ID: "test-id", 183 Type: "image", 184 Metadata: source.ImageMetadata{ 185 UserInput: "user-input", 186 ID: "id...", 187 ManifestDigest: "digest...", 188 MediaType: "type...", 189 RepoDigests: []string{}, 190 Tags: []string{}, 191 }, 192 }, 193 }, 194 } 195 for _, test := range tests { 196 t.Run(test.name, func(t *testing.T) { 197 // assert the model transformation is correct 198 actual := toSourceModel(test.src) 199 assert.Equal(t, test.expected, actual) 200 201 // track each scheme tested (passed or not) 202 tracker.Tested(t, test.expected.Metadata) 203 }) 204 } 205 } 206 207 func Test_toFileType(t *testing.T) { 208 209 badType := stereoscopeFile.Type(0x1337) 210 var allTypesTested []stereoscopeFile.Type 211 tests := []struct { 212 ty stereoscopeFile.Type 213 name string 214 }{ 215 { 216 ty: stereoscopeFile.TypeRegular, 217 name: "RegularFile", 218 }, 219 { 220 ty: stereoscopeFile.TypeDirectory, 221 name: "Directory", 222 }, 223 { 224 ty: stereoscopeFile.TypeSymLink, 225 name: "SymbolicLink", 226 }, 227 { 228 ty: stereoscopeFile.TypeHardLink, 229 name: "HardLink", 230 }, 231 { 232 ty: stereoscopeFile.TypeSocket, 233 name: "Socket", 234 }, 235 { 236 ty: stereoscopeFile.TypeCharacterDevice, 237 name: "CharacterDevice", 238 }, 239 { 240 ty: stereoscopeFile.TypeBlockDevice, 241 name: "BlockDevice", 242 }, 243 { 244 ty: stereoscopeFile.TypeFIFO, 245 name: "FIFONode", 246 }, 247 { 248 ty: stereoscopeFile.TypeIrregular, 249 name: "IrregularFile", 250 }, 251 { 252 ty: badType, 253 name: "Unknown", 254 }, 255 } 256 for _, tt := range tests { 257 t.Run(tt.name, func(t *testing.T) { 258 assert.Equalf(t, tt.name, toFileType(tt.ty), "toFileType(%v)", tt.ty) 259 if tt.ty != badType { 260 allTypesTested = append(allTypesTested, tt.ty) 261 } 262 }) 263 } 264 265 assert.ElementsMatch(t, allTypesTested, stereoscopeFile.AllTypes(), "not all file.Types are under test") 266 } 267 268 func Test_toFileMetadataEntry(t *testing.T) { 269 coords := file.Coordinates{ 270 RealPath: "/path", 271 FileSystemID: "x", 272 } 273 tests := []struct { 274 name string 275 metadata *file.Metadata 276 want *model.FileMetadataEntry 277 }{ 278 { 279 name: "no metadata", 280 }, 281 { 282 name: "no file info", 283 metadata: &file.Metadata{ 284 FileInfo: nil, 285 }, 286 want: &model.FileMetadataEntry{ 287 Type: stereoscopeFile.TypeRegular.String(), 288 }, 289 }, 290 { 291 name: "with file info", 292 metadata: &file.Metadata{ 293 FileInfo: &stereoscopeFile.ManualInfo{ 294 ModeValue: 1, 295 }, 296 }, 297 want: &model.FileMetadataEntry{ 298 Mode: 1, 299 Type: stereoscopeFile.TypeRegular.String(), 300 }, 301 }, 302 } 303 for _, tt := range tests { 304 t.Run(tt.name, func(t *testing.T) { 305 assert.Equal(t, tt.want, toFileMetadataEntry(coords, tt.metadata)) 306 }) 307 } 308 } 309 310 func Test_toPackageModel_metadataType(t *testing.T) { 311 tests := []struct { 312 name string 313 p pkg.Package 314 cfg EncoderConfig 315 want model.Package 316 }{ 317 { 318 name: "empty config", 319 p: pkg.Package{ 320 Metadata: pkg.RpmDBEntry{}, 321 }, 322 cfg: EncoderConfig{}, 323 want: model.Package{ 324 PackageCustomData: model.PackageCustomData{ 325 MetadataType: "rpm-db-entry", 326 Metadata: pkg.RpmDBEntry{}, 327 }, 328 }, 329 }, 330 { 331 name: "legacy config", 332 p: pkg.Package{ 333 Metadata: pkg.RpmDBEntry{}, 334 }, 335 cfg: EncoderConfig{ 336 Legacy: true, 337 }, 338 want: model.Package{ 339 PackageCustomData: model.PackageCustomData{ 340 MetadataType: "RpmMetadata", 341 Metadata: pkg.RpmDBEntry{}, 342 }, 343 }, 344 }, 345 } 346 for _, tt := range tests { 347 t.Run(tt.name, func(t *testing.T) { 348 if d := cmp.Diff(tt.want, toPackageModel(tt.p, tt.cfg), cmpopts.EquateEmpty()); d != "" { 349 t.Errorf("unexpected package (-want +got):\n%s", d) 350 } 351 }) 352 } 353 }