github.com/anchore/syft@v1.38.2/syft/format/internal/spdxutil/helpers/license_test.go (about)

     1  package helpers
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/anchore/syft/internal/spdxlicense"
    11  	"github.com/anchore/syft/syft/pkg"
    12  )
    13  
    14  func Test_License(t *testing.T) {
    15  	ctx := context.TODO()
    16  	type expected struct {
    17  		concluded string
    18  		declared  string
    19  	}
    20  	tests := []struct {
    21  		name     string
    22  		input    pkg.Package
    23  		expected expected
    24  	}{
    25  		{
    26  			name:  "no licenses",
    27  			input: pkg.Package{},
    28  			expected: expected{
    29  				concluded: "NOASSERTION",
    30  				declared:  "NOASSERTION",
    31  			},
    32  		},
    33  		{
    34  			name: "no SPDX licenses",
    35  			input: pkg.Package{
    36  				Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "made-up")),
    37  			},
    38  			expected: expected{
    39  				concluded: "NOASSERTION",
    40  				declared:  "LicenseRef-made-up",
    41  			},
    42  		},
    43  		{
    44  			name: "with SPDX license",
    45  			input: pkg.Package{
    46  				Licenses: pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, "MIT")),
    47  			},
    48  			expected: struct {
    49  				concluded string
    50  				declared  string
    51  			}{
    52  				concluded: "NOASSERTION",
    53  				declared:  "MIT",
    54  			},
    55  		},
    56  		{
    57  			name: "with SPDX license expression",
    58  			input: pkg.Package{
    59  				Licenses: pkg.NewLicenseSet(
    60  					pkg.NewLicenseWithContext(ctx, "MIT"),
    61  					pkg.NewLicenseWithContext(ctx, "GPL-3.0-only"),
    62  				),
    63  			},
    64  			expected: expected{
    65  				concluded: "NOASSERTION",
    66  				// because we sort licenses alphabetically GPL ends up at the start
    67  				declared: "GPL-3.0-only AND MIT",
    68  			},
    69  		},
    70  		{
    71  			name: "includes valid LicenseRef-",
    72  			input: pkg.Package{
    73  				Licenses: pkg.NewLicenseSet(
    74  					pkg.NewLicenseWithContext(ctx, "one thing first"),
    75  					pkg.NewLicenseWithContext(ctx, "two things/#$^second"),
    76  					pkg.NewLicenseWithContext(ctx, "MIT"),
    77  				),
    78  			},
    79  			expected: expected{
    80  				concluded: "NOASSERTION",
    81  				// because we separate licenses between valid SPDX and non valid, valid ID always end at the front
    82  				declared: "MIT AND LicenseRef-one-thing-first AND LicenseRef-two-things----second",
    83  			},
    84  		},
    85  		{
    86  			name: "join parentheses correctly",
    87  			input: pkg.Package{
    88  				Licenses: pkg.NewLicenseSet(
    89  					pkg.NewLicenseWithContext(ctx, "one thing first"),
    90  					pkg.NewLicenseWithContext(ctx, "MIT AND GPL-3.0-only"),
    91  					pkg.NewLicenseWithContext(ctx, "MIT OR APACHE-2.0"),
    92  				),
    93  			},
    94  			expected: expected{
    95  				concluded: "NOASSERTION",
    96  				// because we separate licenses between valid SPDX and non valid, valid ID always end at the front
    97  				declared: "(MIT AND GPL-3.0-only) AND (MIT OR APACHE-2.0) AND LicenseRef-one-thing-first",
    98  			},
    99  		},
   100  	}
   101  	for _, test := range tests {
   102  		t.Run(test.name, func(t *testing.T) {
   103  			c, d, _ := License(test.input)
   104  			assert.Equal(t, test.expected.concluded, c)
   105  			assert.Equal(t, test.expected.declared, d)
   106  		})
   107  	}
   108  }
   109  
   110  func TestGenerateLicenseID(t *testing.T) {
   111  	tests := []struct {
   112  		name     string
   113  		license  pkg.License
   114  		expected string
   115  	}{
   116  		{
   117  			name: "SPDX expression is preferred",
   118  			license: pkg.License{
   119  				SPDXExpression: "Apache-2.0",
   120  				Value:          "SomeValue",
   121  				Contents:       "Some text",
   122  			},
   123  			expected: "Apache-2.0",
   124  		},
   125  		{
   126  			name: "Uses value if no SPDX expression",
   127  			license: pkg.License{
   128  				Value: "my-sweet-custom-license",
   129  			},
   130  			expected: spdxlicense.LicenseRefPrefix + "my-sweet-custom-license",
   131  		},
   132  		{
   133  			// note: this is an oversight of the SPDX spec. It does NOT allow "+" in the ID even though they are
   134  			//  significant to the licenses in the expressions below
   135  			name: "Long value is sanitized correctly",
   136  			license: pkg.License{
   137  				Value: "LGPLv2+ and LGPLv2+ with exceptions and GPLv2+ and GPLv2+ with exceptions and BSD and Inner-Net and ISC and Public Domain and GFDL",
   138  			},
   139  			expected: spdxlicense.LicenseRefPrefix +
   140  				"LGPLv2--and-LGPLv2--with-exceptions-and-GPLv2--and-GPLv2--with-exceptions-and-BSD-and-Inner-Net-and-ISC-and-Public-Domain-and-GFDL",
   141  		},
   142  	}
   143  
   144  	for _, tt := range tests {
   145  		t.Run(tt.name, func(t *testing.T) {
   146  			id := generateLicenseID(tt.license)
   147  			if tt.expected == "" {
   148  				assert.True(t, len(id) > len(spdxlicense.LicenseRefPrefix))
   149  				assert.Contains(t, id, spdxlicense.LicenseRefPrefix)
   150  			} else {
   151  				assert.Equal(t, tt.expected, id)
   152  			}
   153  		})
   154  	}
   155  }
   156  
   157  func Test_joinLicenses(t *testing.T) {
   158  	tests := []struct {
   159  		name string
   160  		args []SPDXLicense
   161  		want string
   162  	}{
   163  		{
   164  			name: "multiple licenses",
   165  			args: []SPDXLicense{{ID: "MIT"}, {ID: "GPL-3.0-only"}},
   166  			want: "MIT AND GPL-3.0-only",
   167  		},
   168  		{
   169  			name: "multiple licenses with complex expressions",
   170  			args: []SPDXLicense{{ID: "MIT AND Apache"}, {ID: "GPL-3.0-only"}},
   171  			want: "(MIT AND Apache) AND GPL-3.0-only",
   172  		},
   173  	}
   174  	for _, tt := range tests {
   175  		t.Run(tt.name, func(t *testing.T) {
   176  			assert.Equalf(t, tt.want, joinLicenses(tt.args), "joinLicenses(%v)", tt.args)
   177  		})
   178  	}
   179  }
   180  
   181  func TestCreateSPDXLicenseAndGenerateLicenseID(t *testing.T) {
   182  	tests := []struct {
   183  		name     string
   184  		input    pkg.License
   185  		expected SPDXLicense
   186  	}{
   187  		{
   188  			name: "SPDX expression used as ID",
   189  			input: pkg.License{
   190  				SPDXExpression: "MIT",
   191  				Value:          "MIT",
   192  				Contents:       "",
   193  			},
   194  			expected: SPDXLicense{
   195  				ID:          "MIT",
   196  				LicenseName: "MIT",
   197  				FullText:    "NOASSERTION",
   198  			},
   199  		},
   200  		{
   201  			name: "LicenseRef with contents",
   202  			input: pkg.License{
   203  				Value:    "sha256:123abc",
   204  				Contents: "license contents here",
   205  			},
   206  			expected: SPDXLicense{
   207  				ID:          "LicenseRef-123abc",
   208  				LicenseName: "sha256:123abc",
   209  				FullText:    "license contents here",
   210  			},
   211  		},
   212  		{
   213  			name: "LicenseRef without contents",
   214  			input: pkg.License{
   215  				Value:    "custom-license",
   216  				Contents: "",
   217  			},
   218  			expected: SPDXLicense{
   219  				ID:          "LicenseRef-custom-license",
   220  				LicenseName: "custom-license",
   221  				FullText:    "NOASSERTION",
   222  			},
   223  		},
   224  		{
   225  			name: "URL is passed through",
   226  			input: pkg.License{
   227  				SPDXExpression: "MIT",
   228  				URLs: []string{
   229  					"https://example.com/license",
   230  				},
   231  			},
   232  			expected: SPDXLicense{
   233  				ID:       "MIT",
   234  				FullText: "NOASSERTION",
   235  				URLs:     []string{"https://example.com/license"},
   236  			},
   237  		},
   238  	}
   239  
   240  	for _, tt := range tests {
   241  		t.Run(tt.name, func(t *testing.T) {
   242  			license := createSPDXLicense(tt.input)
   243  			if d := cmp.Diff(tt.expected, license); d != "" {
   244  				t.Errorf("createSPDXLicense() mismatch (-want +got):\n%s", d)
   245  			}
   246  		})
   247  	}
   248  }