github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/cmd/syft/internal/commands/packages_test.go (about)

     1  package commands
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/go-multierror"
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	"github.com/anchore/syft/internal/task"
    12  )
    13  
    14  func Test_filterExpressionErrors_expressionErrorsHelp(t *testing.T) {
    15  	tests := []struct {
    16  		name        string
    17  		err         error
    18  		wantExpErrs []task.ErrInvalidExpression
    19  		wantErr     assert.ErrorAssertionFunc
    20  		wantHelp    string
    21  	}{
    22  		{
    23  			name:        "no errors",
    24  			err:         nil,
    25  			wantExpErrs: nil,
    26  			wantErr:     assert.NoError,
    27  			wantHelp:    "",
    28  		},
    29  		{
    30  			name: "single non-expression error is retained",
    31  			err:  errors.New("foo"),
    32  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
    33  				return assert.Equal(t, "foo", err.Error())
    34  			},
    35  			wantHelp: "",
    36  		},
    37  		{
    38  			name: "multiple non-expression sibling errors are retained",
    39  			err: func() error {
    40  				var err error
    41  				err = multierror.Append(err, errors.New("foo"))
    42  				err = multierror.Append(err, errors.New("bar"))
    43  				return err
    44  			}(),
    45  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
    46  				// note: this is the default formatting from the hashicorp multierror object
    47  				expected := `2 errors occurred:
    48  	* foo
    49  	* bar
    50  
    51  `
    52  				return assert.Equal(t, expected, err.Error())
    53  			},
    54  			wantHelp: "",
    55  		},
    56  		{
    57  			name: "has multiple expression errors (with sibling errors)",
    58  			err: func() error {
    59  				var err error
    60  				err = multierror.Append(err, errors.New("foo"))
    61  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed})
    62  				err = multierror.Append(err, errors.New("bar"))
    63  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed})
    64  				err = multierror.Append(err, errors.New("last"))
    65  				return err
    66  			}(),
    67  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
    68  				expected := `5 errors occurred:
    69  	* foo
    70  	* invalid expression: "foo": tags are not allowed with this operation (must use exact names)
    71  	* bar
    72  	* invalid expression: "bar": names are not allowed with this operation (must use tags)
    73  	* last
    74  
    75  `
    76  				return assert.Equal(t, expected, err.Error())
    77  			},
    78  			wantExpErrs: []task.ErrInvalidExpression{
    79  				{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed},
    80  				{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed},
    81  			},
    82  			wantHelp: `Suggestions:
    83  
    84   ❖ Given expression "--select-catalogers foo"
    85     However, tags are not allowed with this operation (must use exact names).
    86     Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs).
    87     If you are certain this is what you want to do, use "--override-default-catalogers foo" instead.
    88  
    89   ❖ Given expression "--select-catalogers bar"
    90     However, names are not allowed with this operation (must use tags).
    91     It seems like you are intending to add a cataloger in addition to the default set.
    92     ... Did you mean "--select-catalogers +bar" instead?
    93  `,
    94  		},
    95  		{
    96  			name: "has multiple expression errors (with error chains and sibling errors)",
    97  			err: func() error {
    98  				var err error
    99  				err = multierror.Append(err, fmt.Errorf("foo: %w", fmt.Errorf("bar: %w", errors.New("last"))))
   100  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed})
   101  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed})
   102  				err = multierror.Append(err, errors.New("bottom"))
   103  
   104  				return fmt.Errorf("top: %w", fmt.Errorf("middle: %w", err))
   105  			}(),
   106  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
   107  				expected := `top: middle: 4 errors occurred:
   108  	* foo: bar: last
   109  	* invalid expression: "foo": tags are not allowed with this operation (must use exact names)
   110  	* invalid expression: "bar": names are not allowed with this operation (must use tags)
   111  	* bottom
   112  
   113  `
   114  				return assert.Equal(t, expected, err.Error())
   115  			},
   116  			wantExpErrs: []task.ErrInvalidExpression{
   117  				{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed},
   118  				{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed},
   119  			},
   120  			wantHelp: `Suggestions:
   121  
   122   ❖ Given expression "--select-catalogers foo"
   123     However, tags are not allowed with this operation (must use exact names).
   124     Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs).
   125     If you are certain this is what you want to do, use "--override-default-catalogers foo" instead.
   126  
   127   ❖ Given expression "--select-catalogers bar"
   128     However, names are not allowed with this operation (must use tags).
   129     It seems like you are intending to add a cataloger in addition to the default set.
   130     ... Did you mean "--select-catalogers +bar" instead?
   131  `,
   132  		},
   133  		{
   134  			name: "has multiple expression errors (with error chains and sibling errors)",
   135  			err: func() error {
   136  				var err error
   137  				err = multierror.Append(err, fmt.Errorf("foo: %w", fmt.Errorf("bar: %w", errors.New("last"))))
   138  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed})
   139  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed})
   140  				err = multierror.Append(err, errors.New("bottom"))
   141  
   142  				// note we wrap the top error in a chain
   143  				return fmt.Errorf("top: %w", fmt.Errorf("middle: %w", err))
   144  			}(),
   145  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
   146  				expected := `top: middle: 4 errors occurred:
   147  	* foo: bar: last
   148  	* invalid expression: "foo": tags are not allowed with this operation (must use exact names)
   149  	* invalid expression: "bar": names are not allowed with this operation (must use tags)
   150  	* bottom
   151  
   152  `
   153  				return assert.Equal(t, expected, err.Error())
   154  			},
   155  			wantExpErrs: []task.ErrInvalidExpression{
   156  				{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed},
   157  				{Expression: "bar", Operation: task.SubSelectOperation, Err: task.ErrNamesNotAllowed},
   158  			},
   159  			wantHelp: `Suggestions:
   160  
   161   ❖ Given expression "--select-catalogers foo"
   162     However, tags are not allowed with this operation (must use exact names).
   163     Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs).
   164     If you are certain this is what you want to do, use "--override-default-catalogers foo" instead.
   165  
   166   ❖ Given expression "--select-catalogers bar"
   167     However, names are not allowed with this operation (must use tags).
   168     It seems like you are intending to add a cataloger in addition to the default set.
   169     ... Did you mean "--select-catalogers +bar" instead?
   170  `,
   171  		},
   172  		{
   173  			name: "preserve for any errors within ErrInvalidExpression types",
   174  			err: func() error {
   175  				var err error
   176  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed})
   177  				err = multierror.Append(err, task.ErrInvalidExpression{Expression: "bar", Operation: task.SubSelectOperation, Err: errors.New("explanation")}) // this is what makes this test different...
   178  
   179  				return err
   180  			}(),
   181  			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
   182  				// note: the errors are removed and the help text shows the enriched error help
   183  				expected := `2 errors occurred:
   184  	* invalid expression: "foo": tags are not allowed with this operation (must use exact names)
   185  	* invalid expression: "bar": explanation
   186  
   187  `
   188  				return assert.Equal(t, expected, err.Error())
   189  			},
   190  			wantExpErrs: []task.ErrInvalidExpression{
   191  				{Expression: "foo", Operation: task.AddOperation, Err: task.ErrTagsNotAllowed},
   192  				{Expression: "bar", Operation: task.SubSelectOperation, Err: errors.New("explanation")},
   193  			},
   194  			wantHelp: `Suggestions:
   195  
   196   ❖ Given expression "--select-catalogers foo"
   197     However, tags are not allowed with this operation (must use exact names).
   198     Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs).
   199     If you are certain this is what you want to do, use "--override-default-catalogers foo" instead.
   200  
   201  `,
   202  		},
   203  	}
   204  	for _, tt := range tests {
   205  		t.Run(tt.name, func(t *testing.T) {
   206  			gotExpErrs := filterExpressionErrors(tt.err)
   207  			tt.wantErr(t, tt.err) // ensure the error still remains
   208  			assert.Equal(t, tt.wantExpErrs, gotExpErrs)
   209  
   210  			gotHelp := expressionErrorsHelp(gotExpErrs)
   211  			assert.Equal(t, tt.wantHelp, gotHelp)
   212  		})
   213  	}
   214  }
   215  
   216  func Test_expressionSuggestions(t *testing.T) {
   217  	tests := []struct {
   218  		name   string
   219  		expErr task.ErrInvalidExpression
   220  		want   string
   221  	}{
   222  		{
   223  			name: "no embedded error",
   224  			expErr: task.ErrInvalidExpression{
   225  				Expression: "example",
   226  			},
   227  			want: ``,
   228  		},
   229  		{
   230  			name: "general error",
   231  			expErr: task.ErrInvalidExpression{
   232  				Err:        errors.New("general error message"),
   233  				Expression: "example",
   234  			},
   235  			want: ``,
   236  		},
   237  		{
   238  			name: "ErrUnknownNameOrTag with add operation",
   239  			expErr: task.ErrInvalidExpression{
   240  				Err:        task.ErrUnknownNameOrTag,
   241  				Operation:  task.AddOperation,
   242  				Expression: "+example",
   243  			},
   244  			want: ``,
   245  		},
   246  		{
   247  			name: "ErrUnknownNameOrTag with subselect operation",
   248  			expErr: task.ErrInvalidExpression{
   249  				Err:        task.ErrUnknownNameOrTag,
   250  				Operation:  task.SubSelectOperation,
   251  				Expression: "example",
   252  			},
   253  			want: ``,
   254  		},
   255  		{
   256  			name: "ErrNamesNotAllowed with subselect operator",
   257  			expErr: task.ErrInvalidExpression{
   258  				Err:        task.ErrNamesNotAllowed,
   259  				Operation:  task.SubSelectOperation,
   260  				Expression: "example",
   261  			},
   262  			want: ` ❖ Given expression "--select-catalogers example"
   263     However, names are not allowed with this operation (must use tags).
   264     It seems like you are intending to add a cataloger in addition to the default set.
   265     ... Did you mean "--select-catalogers +example" instead?
   266  `,
   267  		},
   268  		{
   269  			name: "ErrTagsNotAllowed with add operation",
   270  			expErr: task.ErrInvalidExpression{
   271  				Err:        task.ErrTagsNotAllowed,
   272  				Operation:  task.AddOperation,
   273  				Expression: "+example",
   274  			},
   275  			want: ` ❖ Given expression "--select-catalogers +example"
   276     However, tags are not allowed with this operation (must use exact names).
   277     Adding groups of catalogers may result in surprising behavior (create inaccurate SBOMs).
   278     If you are certain this is what you want to do, use "--override-default-catalogers example" instead.
   279  `,
   280  		},
   281  		{
   282  			name: "ErrAllNotAllowed with subselect operation",
   283  			expErr: task.ErrInvalidExpression{
   284  				Err:        task.ErrAllNotAllowed,
   285  				Operation:  task.SubSelectOperation,
   286  				Expression: "example",
   287  			},
   288  			want: ` ❖ Given expression "--select-catalogers example"
   289     However, you cannot use the 'all' operand in this context.
   290     It seems like you are intending to use all catalogers (which is not recommended).
   291     ... Did you mean "--override-default-catalogers example" instead?
   292  `,
   293  		},
   294  	}
   295  	for _, tt := range tests {
   296  		t.Run(tt.name, func(t *testing.T) {
   297  			assert.Equal(t, tt.want, expressionSuggetions(tt.expErr))
   298  		})
   299  	}
   300  }