github.com/anchore/syft@v1.38.2/syft/pkg/license_test.go (about) 1 package pkg 2 3 import ( 4 "context" 5 "sort" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/anchore/syft/syft/artifact" 12 "github.com/anchore/syft/syft/file" 13 "github.com/anchore/syft/syft/license" 14 ) 15 16 func Test_Hash(t *testing.T) { 17 ctx := context.TODO() 18 loc1 := file.NewLocation("place!") 19 loc1.FileSystemID = "fs1" 20 loc2 := file.NewLocation("place!") 21 loc2.FileSystemID = "fs2" // important! there is a different file system ID 22 23 lic1 := NewLicenseFromFieldsWithContext(ctx, "MIT", "foo", &loc1) 24 lic2 := NewLicenseFromFieldsWithContext(ctx, "MIT", "bar", &loc2) 25 26 hash1, err := artifact.IDByHash(lic1) 27 require.NoError(t, err) 28 29 hash2, err := artifact.IDByHash(lic2) 30 require.NoError(t, err) 31 32 assert.Equal(t, hash1, hash2) 33 } 34 35 func Test_Sort(t *testing.T) { 36 ctx := context.TODO() 37 tests := []struct { 38 name string 39 licenses Licenses 40 expected Licenses 41 }{ 42 { 43 name: "empty", 44 licenses: []License{}, 45 expected: []License{}, 46 }, 47 { 48 name: "single", 49 licenses: []License{ 50 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 51 }, 52 expected: []License{ 53 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 54 }, 55 }, 56 { 57 name: "multiple", 58 licenses: []License{ 59 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 60 NewLicenseFromURLsWithContext(ctx, "MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), 61 NewLicenseFromLocationsWithContext(ctx, "Apache", file.NewLocation("area!")), 62 NewLicenseFromLocationsWithContext(ctx, "gpl2+", file.NewLocation("area!")), 63 }, 64 expected: Licenses{ 65 NewLicenseFromLocationsWithContext(ctx, "Apache", file.NewLocation("area!")), 66 NewLicenseFromURLsWithContext(ctx, "MIT", "https://github.com/anchore/syft/blob/main/LICENSE"), 67 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 68 NewLicenseFromLocationsWithContext(ctx, "gpl2+", file.NewLocation("area!")), 69 }, 70 }, 71 { 72 name: "multiple with location variants", 73 licenses: []License{ 74 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 75 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("park!")), 76 NewLicenseWithContext(ctx, "MIT"), 77 NewLicenseWithContext(ctx, "AAL"), 78 NewLicenseWithContext(ctx, "Adobe-2006"), 79 NewLicenseFromLocationsWithContext(ctx, "Apache", file.NewLocation("area!")), 80 }, 81 expected: Licenses{ 82 NewLicenseWithContext(ctx, "AAL"), 83 NewLicenseWithContext(ctx, "Adobe-2006"), 84 NewLicenseFromLocationsWithContext(ctx, "Apache", file.NewLocation("area!")), 85 NewLicenseWithContext(ctx, "MIT"), 86 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("park!")), 87 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("place!")), 88 }, 89 }, 90 { 91 name: "multiple licenses with only contents are still sorted by their computed lic.value references", 92 licenses: []License{ 93 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement")), 94 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/Knuth-CTAN")), 95 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0")), 96 }, 97 expected: Licenses{ 98 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0")), 99 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement")), 100 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/Knuth-CTAN")), 101 }, 102 }, 103 } 104 105 for _, test := range tests { 106 t.Run(test.name, func(t *testing.T) { 107 sort.Sort(test.licenses) 108 assert.Equal(t, test.expected, test.licenses) 109 }) 110 111 } 112 } 113 114 func TestLicense_Merge(t *testing.T) { 115 locA := file.NewLocation("a") 116 locB := file.NewLocation("b") 117 118 tests := []struct { 119 name string 120 subject License 121 other License 122 want License 123 wantErr require.ErrorAssertionFunc 124 }{ 125 { 126 name: "valid merge", 127 subject: License{ 128 Value: "MIT", 129 SPDXExpression: "MIT", 130 Type: license.Declared, 131 URLs: []string{ 132 "b", "a", 133 }, 134 Locations: file.NewLocationSet(locA), 135 }, 136 other: License{ 137 Value: "MIT", 138 SPDXExpression: "MIT", 139 Type: license.Declared, 140 URLs: []string{ 141 "c", "d", 142 }, 143 Locations: file.NewLocationSet(locB), 144 }, 145 want: License{ 146 Value: "MIT", 147 SPDXExpression: "MIT", 148 Type: license.Declared, 149 URLs: []string{ 150 "a", "b", "c", "d", 151 }, 152 Locations: file.NewLocationSet(locA, locB), 153 }, 154 }, 155 { 156 name: "mismatched value", 157 subject: License{ 158 Value: "DIFFERENT!!", 159 SPDXExpression: "MIT", 160 Type: license.Declared, 161 URLs: []string{ 162 "b", "a", 163 }, 164 Locations: file.NewLocationSet(locA), 165 }, 166 other: License{ 167 Value: "MIT", 168 SPDXExpression: "MIT", 169 Type: license.Declared, 170 URLs: []string{ 171 "c", "d", 172 }, 173 Locations: file.NewLocationSet(locB), 174 }, 175 wantErr: require.Error, 176 }, 177 { 178 name: "mismatched spdx expression", 179 subject: License{ 180 Value: "MIT", 181 SPDXExpression: "DIFFERENT!!", 182 Type: license.Declared, 183 URLs: []string{ 184 "b", "a", 185 }, 186 Locations: file.NewLocationSet(locA), 187 }, 188 other: License{ 189 Value: "MIT", 190 SPDXExpression: "MIT", 191 Type: license.Declared, 192 URLs: []string{ 193 "c", "d", 194 }, 195 Locations: file.NewLocationSet(locB), 196 }, 197 wantErr: require.Error, 198 }, 199 { 200 name: "mismatched type", 201 subject: License{ 202 Value: "MIT", 203 SPDXExpression: "MIT", 204 Type: license.Concluded, 205 URLs: []string{ 206 "b", "a", 207 }, 208 Locations: file.NewLocationSet(locA), 209 }, 210 other: License{ 211 Value: "MIT", 212 SPDXExpression: "MIT", 213 Type: license.Declared, 214 URLs: []string{ 215 "c", "d", 216 }, 217 Locations: file.NewLocationSet(locB), 218 }, 219 wantErr: require.Error, 220 }, 221 } 222 for _, tt := range tests { 223 t.Run(tt.name, func(t *testing.T) { 224 if tt.wantErr == nil { 225 tt.wantErr = require.NoError 226 } 227 228 subjectLocationLen := len(tt.subject.Locations.ToSlice()) 229 subjectURLLen := len(tt.subject.URLs) 230 231 got, err := tt.subject.Merge(tt.other) 232 tt.wantErr(t, err) 233 if err != nil { 234 return 235 } 236 require.NotNilf(t, got, "expected a non-nil license") 237 assert.Equal(t, tt.want, *got) 238 // prove we don't modify the subject 239 assert.Equal(t, subjectLocationLen, len(tt.subject.Locations.ToSlice())) 240 assert.Equal(t, subjectURLLen, len(tt.subject.URLs)) 241 }) 242 } 243 } 244 245 func TestFullText(t *testing.T) { 246 ctx := context.TODO() 247 fullText := `I am a license with full text 248 my authors put new line characters in metadata for labeling a license` 249 tests := []struct { 250 name string 251 value string 252 want License 253 }{ 254 { 255 name: "Full Text field is populated with the correct full text and contents are given a sha256 as value", 256 value: fullText, 257 want: License{ 258 Value: "sha256:108067fa71229a2b98b9696af0ce21cd11d9639634c8bc94bda70ebedf291e5a", 259 Type: license.Declared, 260 Contents: fullText, 261 }, 262 }, 263 } 264 265 for _, tt := range tests { 266 t.Run(tt.name, func(t *testing.T) { 267 got := NewLicenseWithContext(ctx, tt.value) 268 assert.Equal(t, tt.want, got) 269 }) 270 } 271 } 272 273 func TestLicenseConstructors(t *testing.T) { 274 ctx := context.TODO() 275 type input struct { 276 value string 277 urls []string 278 } 279 tests := []struct { 280 name string 281 input input 282 expected License 283 }{ 284 { 285 name: "License URLs are stripped of newlines and tabs", 286 input: input{ 287 value: "New BSD License", 288 urls: []string{ 289 ` 290 http://user-agent-utils.googlecode.com/svn/trunk/UserAgentUtils/LICENSE.txt 291 `}, 292 }, 293 expected: License{ 294 Value: "New BSD License", 295 Type: license.Declared, 296 URLs: []string{"http://user-agent-utils.googlecode.com/svn/trunk/UserAgentUtils/LICENSE.txt"}, 297 }, 298 }, 299 { 300 name: "License URLs without value", 301 input: input{ 302 value: "", 303 urls: []string{"http://user-agent-utils.googlecode.com/svn/trunk/UserAgentUtils/LICENSE.txt"}, 304 }, 305 expected: License{ 306 Type: license.Declared, 307 URLs: []string{"http://user-agent-utils.googlecode.com/svn/trunk/UserAgentUtils/LICENSE.txt"}, 308 }, 309 }, 310 } 311 312 for _, test := range tests { 313 t.Run(test.name, func(t *testing.T) { 314 got := NewLicenseFromURLsWithContext(ctx, test.input.value, test.input.urls...) 315 assert.Equal(t, test.expected, got) 316 }) 317 } 318 }