...

Source file src/edge-infra.dev/pkg/sds/emergencyaccess/rules/storage/database/banner_rules_test.go

Documentation: edge-infra.dev/pkg/sds/emergencyaccess/rules/storage/database

     1  package database
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"testing"
     8  
     9  	"github.com/DATA-DOG/go-sqlmock"
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"edge-infra.dev/pkg/lib/fog"
    13  	rulesengine "edge-infra.dev/pkg/sds/emergencyaccess/rules"
    14  	datasql "edge-infra.dev/pkg/sds/emergencyaccess/rules/storage/database/sql"
    15  )
    16  
    17  // assert.ErrorAssertionFunc
    18  func EqualError(message string) func(assert.TestingT, error, ...interface{}) bool {
    19  	return func(tt assert.TestingT, err error, i ...interface{}) bool {
    20  		return assert.EqualError(tt, err, message, i...)
    21  	}
    22  }
    23  
    24  func TestAddBannerRulesDefer(t *testing.T) {
    25  	// Tests Defer function correctly sets the return values
    26  	t.Parallel()
    27  
    28  	ctx := fog.IntoContext(context.Background(), fog.New(fog.To(io.Discard)))
    29  
    30  	tests := map[string]struct {
    31  		inputRules []rulesengine.RuleSegment
    32  		expRes     rulesengine.AddRuleResult
    33  
    34  		expectations   func(mock sqlmock.Sqlmock)
    35  		errorAssertion assert.ErrorAssertionFunc
    36  	}{
    37  		"Successful commit": {
    38  			inputRules: nil,
    39  			expectations: func(mock sqlmock.Sqlmock) {
    40  				mock.ExpectBegin()
    41  				mock.ExpectCommit().WillReturnError(nil)
    42  			},
    43  			errorAssertion: assert.NoError,
    44  		},
    45  		"Commit Returns Error": {
    46  			inputRules: nil,
    47  			expectations: func(mock sqlmock.Sqlmock) {
    48  				mock.ExpectBegin()
    49  				mock.ExpectCommit().WillReturnError(fmt.Errorf("commit error"))
    50  			},
    51  			errorAssertion: EqualError("error committing transaction: commit error"),
    52  		},
    53  		"Rollback on error": {
    54  			inputRules: []rulesengine.RuleSegment{{Banner: rulesengine.Banner{BannerName: "a"}, Command: rulesengine.Command{Name: "b"}, Privilege: rulesengine.Privilege{Name: "c"}}},
    55  			expectations: func(mock sqlmock.Sqlmock) {
    56  				// Return an error immediately from the insert query
    57  				mock.ExpectBegin()
    58  				mock.ExpectExec(datasql.InsertBannerRule).WithArgs("a", "b", "c").WillReturnResult(sqlmock.NewErrorResult(fmt.Errorf("insert error")))
    59  				// Rollback is successful
    60  				mock.ExpectRollback()
    61  			},
    62  			errorAssertion: EqualError("error getting query result: insert error"),
    63  		},
    64  		"Rollback on error returns an error": {
    65  			inputRules: []rulesengine.RuleSegment{{Banner: rulesengine.Banner{BannerName: "a"}, Command: rulesengine.Command{Name: "b"}, Privilege: rulesengine.Privilege{Name: "c"}}},
    66  			expectations: func(mock sqlmock.Sqlmock) {
    67  				mock.ExpectBegin()
    68  				// Returns an error
    69  				mock.ExpectExec(datasql.InsertBannerRule).WithArgs("a", "b", "c").WillReturnResult(sqlmock.NewErrorResult(fmt.Errorf("insert error")))
    70  				mock.ExpectRollback().WillReturnError(fmt.Errorf("rollback error"))
    71  			},
    72  			// Contains both insert error and rollback error
    73  			errorAssertion: EqualError("error rolling back transaction due to application error (error getting query result: insert error): rollback error"),
    74  		},
    75  		"Rollback on conflicts": {
    76  			inputRules: []rulesengine.RuleSegment{{Banner: rulesengine.Banner{BannerName: "a"}, Command: rulesengine.Command{Name: "b"}, Privilege: rulesengine.Privilege{Name: "c"}}},
    77  			expRes:     rulesengine.AddRuleResult{Errors: []rulesengine.Error{{Privilege: "c", Type: rulesengine.UnknownPrivilege}}},
    78  
    79  			expectations: func(mock sqlmock.Sqlmock) {
    80  				mock.ExpectBegin()
    81  				mock.ExpectExec(datasql.InsertBannerRule).
    82  					WithArgs("a", "b", "c").
    83  					WillReturnResult(sqlmock.NewResult(0, 0))
    84  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
    85  					WithArgs("a", "b", "c").
    86  					WillReturnRows(
    87  						// Missing the privilege row
    88  						sqlmock.NewRows([]string{"type", "id"}).
    89  							AddRow("banner", "banner-uuid").
    90  							AddRow("command", "command-uuid"),
    91  					)
    92  				mock.ExpectRollback()
    93  			},
    94  			errorAssertion: assert.NoError,
    95  		},
    96  		"Rollback on Conflicts returns error": {
    97  			inputRules: []rulesengine.RuleSegment{{Banner: rulesengine.Banner{BannerName: "a"}, Command: rulesengine.Command{Name: "b"}, Privilege: rulesengine.Privilege{Name: "c"}}},
    98  			expRes:     rulesengine.AddRuleResult{Errors: []rulesengine.Error{{Privilege: "c", Type: rulesengine.UnknownPrivilege}}},
    99  
   100  			expectations: func(mock sqlmock.Sqlmock) {
   101  				mock.ExpectBegin()
   102  				mock.ExpectExec(datasql.InsertBannerRule).
   103  					WithArgs("a", "b", "c").
   104  					WillReturnResult(sqlmock.NewResult(0, 0))
   105  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   106  					WithArgs("a", "b", "c").
   107  					WillReturnRows(
   108  						// Missing the privilege row
   109  						sqlmock.NewRows([]string{"type", "id"}).
   110  							AddRow("banner", "banner-uuid").
   111  							AddRow("command", "command-uuid"),
   112  					)
   113  				mock.ExpectRollback().WillReturnError(fmt.Errorf("rollback error"))
   114  			},
   115  			errorAssertion: EqualError("error rolling back transaction due to errors: rollback error"),
   116  		},
   117  	}
   118  
   119  	for name, tc := range tests {
   120  		tc := tc
   121  		t.Run(name, func(t *testing.T) {
   122  			db, mock := initMockDB(t)
   123  			defer db.Close()
   124  
   125  			ds := Dataset{db: db}
   126  
   127  			tc.expectations(mock)
   128  
   129  			res, err := ds.AddBannerRules(ctx, tc.inputRules)
   130  			tc.errorAssertion(t, err)
   131  			assert.Equal(t, tc.expRes, res)
   132  
   133  			assert.NoError(t, mock.ExpectationsWereMet())
   134  		})
   135  	}
   136  }
   137  
   138  func TestPopulateBannerSegmentIDs(t *testing.T) {
   139  	t.Parallel()
   140  
   141  	tests := map[string]struct {
   142  		segment      rulesengine.RuleSegment
   143  		expectations func(mock sqlmock.Sqlmock)
   144  		expRes       rulesengine.RuleSegment
   145  		expErr       assert.ErrorAssertionFunc
   146  	}{
   147  		"Query error": {
   148  			segment: rulesengine.RuleSegment{
   149  				Banner:    rulesengine.Banner{BannerName: "b"},
   150  				Command:   rulesengine.Command{Name: "c"},
   151  				Privilege: rulesengine.Privilege{Name: "p"},
   152  			},
   153  			expectations: func(mock sqlmock.Sqlmock) {
   154  				// Unfortunate I have to duplicate this
   155  				mock.ExpectBegin()
   156  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   157  					WithArgs("b", "c", "p").
   158  					WillReturnError(fmt.Errorf("an error"))
   159  			},
   160  			expRes: rulesengine.RuleSegment{
   161  				Banner:    rulesengine.Banner{BannerName: "b"},
   162  				Command:   rulesengine.Command{Name: "c"},
   163  				Privilege: rulesengine.Privilege{Name: "p"},
   164  			},
   165  			expErr: EqualError("error finding ID's (banner b, command c, privilege p): an error"),
   166  		},
   167  
   168  		"Success": {
   169  			segment: rulesengine.RuleSegment{
   170  				Banner:    rulesengine.Banner{BannerName: "a"},
   171  				Command:   rulesengine.Command{Name: "b"},
   172  				Privilege: rulesengine.Privilege{Name: "c"},
   173  			},
   174  			expectations: func(mock sqlmock.Sqlmock) {
   175  				// Unfortunate I have to duplicate this
   176  				mock.ExpectBegin()
   177  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   178  					WithArgs("a", "b", "c").
   179  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}).
   180  						AddRow("banner", "i").
   181  						AddRow("command", "j").
   182  						AddRow("privilege", "k"),
   183  					)
   184  			},
   185  			expRes: rulesengine.RuleSegment{
   186  				Banner:    rulesengine.Banner{BannerName: "a", BannerID: "i"},
   187  				Command:   rulesengine.Command{Name: "b", ID: "j"},
   188  				Privilege: rulesengine.Privilege{Name: "c", ID: "k"},
   189  			},
   190  			expErr: assert.NoError,
   191  		},
   192  
   193  		"Missing banner ID": {
   194  			segment: rulesengine.RuleSegment{
   195  				Banner:    rulesengine.Banner{BannerName: "b"},
   196  				Command:   rulesengine.Command{Name: "c"},
   197  				Privilege: rulesengine.Privilege{Name: "p"},
   198  			},
   199  			expectations: func(mock sqlmock.Sqlmock) {
   200  				// Unfortunate I have to duplicate this
   201  				mock.ExpectBegin()
   202  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   203  					WithArgs("b", "c", "p").
   204  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}).
   205  						AddRow("command", "j").
   206  						AddRow("privilege", "k"),
   207  					)
   208  			},
   209  			expRes: rulesengine.RuleSegment{
   210  				Banner:    rulesengine.Banner{BannerName: "b"},
   211  				Command:   rulesengine.Command{Name: "c", ID: "j"},
   212  				Privilege: rulesengine.Privilege{Name: "p", ID: "k"},
   213  			},
   214  
   215  			expErr: assert.NoError,
   216  		},
   217  		"Missing All IDs": {
   218  			segment: rulesengine.RuleSegment{
   219  				Banner:    rulesengine.Banner{BannerName: "b"},
   220  				Command:   rulesengine.Command{Name: "c"},
   221  				Privilege: rulesengine.Privilege{Name: "p"},
   222  			},
   223  			expectations: func(mock sqlmock.Sqlmock) {
   224  				// Unfortunate I have to duplicate this
   225  				mock.ExpectBegin()
   226  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   227  					WithArgs("b", "c", "p").
   228  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}))
   229  			},
   230  			expRes: rulesengine.RuleSegment{
   231  				Banner:    rulesengine.Banner{BannerName: "b"},
   232  				Command:   rulesengine.Command{Name: "c"},
   233  				Privilege: rulesengine.Privilege{Name: "p"},
   234  			},
   235  
   236  			expErr: assert.NoError,
   237  		},
   238  	}
   239  
   240  	for name, tc := range tests {
   241  		tc := tc
   242  		t.Run(name, func(t *testing.T) {
   243  			t.Parallel()
   244  
   245  			ctx := context.Background()
   246  
   247  			db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   248  			assert.NoError(t, err)
   249  
   250  			tc.expectations(mock)
   251  
   252  			// TODO I guess the populateBannerSegmentIDs could be a function not a method
   253  			ds := Dataset{}
   254  
   255  			tx, err := db.BeginTx(ctx, nil)
   256  			assert.NoError(t, err)
   257  
   258  			res, err := ds.populateBannerSegmentIDs(ctx, tx, tc.segment)
   259  			tc.expErr(t, err)
   260  
   261  			assert.Equal(t, tc.expRes, res)
   262  
   263  			assert.NoError(t, mock.ExpectationsWereMet())
   264  		})
   265  	}
   266  }
   267  
   268  func TestReadRulesForBanner(t *testing.T) {
   269  	db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   270  	assert.NoError(t, err)
   271  
   272  	mock.ExpectQuery(datasql.SelectAllRulesForBanner).
   273  		WithArgs("myBanner").
   274  		WillReturnRows(sqlmock.NewRows([]string{"com", "priv", "cip", "pid"}).
   275  			AddRow("command_name", "privilege_name", "command_id", "privilege_id"),
   276  		)
   277  
   278  	ds := Dataset{db: db}
   279  
   280  	res, err := ds.ReadRulesForBanner(context.Background(), "myBanner")
   281  	assert.NoError(t, err)
   282  
   283  	expRules := []rulesengine.RuleSegment{
   284  		{
   285  			Command:   rulesengine.Command{Name: "command_name", ID: "command_id"},
   286  			Privilege: rulesengine.Privilege{Name: "privilege_name", ID: "privilege_id"},
   287  		},
   288  	}
   289  	assert.Equal(t, expRules, res)
   290  }
   291  
   292  func TestReadBannerRulesForCommand(t *testing.T) {
   293  	t.Parallel()
   294  
   295  	tests := map[string]struct {
   296  		commandName string
   297  
   298  		expectations func(mock sqlmock.Sqlmock)
   299  
   300  		expRes []rulesengine.RuleSegment
   301  		expErr assert.ErrorAssertionFunc
   302  	}{
   303  		"No values": {
   304  			commandName: "ls",
   305  			expectations: func(mock sqlmock.Sqlmock) {
   306  				mock.ExpectQuery(datasql.SelectAllBannerRulesForCommand).
   307  					WithArgs("ls").
   308  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}))
   309  			},
   310  			expRes: nil,
   311  			expErr: assert.NoError,
   312  		},
   313  		"One returned value": {
   314  			commandName: "ls",
   315  			expectations: func(mock sqlmock.Sqlmock) {
   316  				mock.ExpectQuery(datasql.SelectAllBannerRulesForCommand).
   317  					WithArgs("ls").
   318  					WillReturnRows(sqlmock.NewRows([]string{"banner_name", "command_name", "privilege_name", "banner_edge_id", "command_id", "privilege_id"}).
   319  						AddRow("banner", "ls", "read", "bid", "cid", "pid"),
   320  					)
   321  			},
   322  			expRes: []rulesengine.RuleSegment{
   323  				{Command: rulesengine.Command{ID: "cid", Name: "ls"}, Banner: rulesengine.Banner{BannerName: "banner", BannerID: "bid"}, Privilege: rulesengine.Privilege{Name: "read", ID: "pid"}},
   324  			},
   325  			expErr: assert.NoError,
   326  		},
   327  	}
   328  
   329  	for name, tc := range tests {
   330  		tc := tc
   331  		t.Run(name, func(t *testing.T) {
   332  			t.Parallel()
   333  
   334  			ctx := context.Background()
   335  
   336  			db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   337  			assert.NoError(t, err)
   338  
   339  			tc.expectations(mock)
   340  
   341  			ds := Dataset{db: db}
   342  
   343  			res, err := ds.ReadBannerRulesForCommand(ctx, tc.commandName)
   344  			tc.expErr(t, err)
   345  
   346  			assert.Equal(t, tc.expRes, res)
   347  
   348  			assert.NoError(t, mock.ExpectationsWereMet())
   349  		})
   350  	}
   351  }
   352  
   353  func TestReadBannerRulesForCommandAndBanner(t *testing.T) {
   354  	t.Parallel()
   355  
   356  	tests := map[string]struct {
   357  		bannerName  string
   358  		commandName string
   359  
   360  		expectations func(mock sqlmock.Sqlmock)
   361  
   362  		expRes []rulesengine.RuleSegment
   363  		expErr assert.ErrorAssertionFunc
   364  	}{
   365  		"No values": {
   366  			bannerName:  "myBanner",
   367  			commandName: "ls",
   368  			expectations: func(mock sqlmock.Sqlmock) {
   369  				mock.ExpectQuery(datasql.SelectBannerRulesForCommandAndBanner).
   370  					WithArgs("myBanner", "ls").
   371  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}))
   372  			},
   373  			expRes: nil,
   374  			expErr: assert.NoError,
   375  		},
   376  		"One returned value": {
   377  			bannerName:  "banner",
   378  			commandName: "ls",
   379  			expectations: func(mock sqlmock.Sqlmock) {
   380  				mock.ExpectQuery(datasql.SelectBannerRulesForCommandAndBanner).
   381  					WithArgs("banner", "ls").
   382  					WillReturnRows(sqlmock.NewRows([]string{"banner_name", "command_name", "privilege_name", "banner_edge_id", "command_id", "privilege_id"}).
   383  						AddRow("banner", "ls", "read", "bid", "cid", "pid"),
   384  					)
   385  			},
   386  			expRes: []rulesengine.RuleSegment{
   387  				{Command: rulesengine.Command{ID: "cid", Name: "ls"}, Banner: rulesengine.Banner{BannerName: "banner", BannerID: "bid"}, Privilege: rulesengine.Privilege{Name: "read", ID: "pid"}},
   388  			},
   389  			expErr: assert.NoError,
   390  		},
   391  	}
   392  
   393  	for name, tc := range tests {
   394  		tc := tc
   395  		t.Run(name, func(t *testing.T) {
   396  			t.Parallel()
   397  
   398  			ctx := context.Background()
   399  
   400  			db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   401  			assert.NoError(t, err)
   402  
   403  			tc.expectations(mock)
   404  
   405  			ds := Dataset{db: db}
   406  
   407  			res, err := ds.ReadBannerRulesForCommandAndBanner(ctx, tc.bannerName, tc.commandName)
   408  			tc.expErr(t, err)
   409  
   410  			assert.Equal(t, tc.expRes, res)
   411  
   412  			assert.NoError(t, mock.ExpectationsWereMet())
   413  		})
   414  	}
   415  }
   416  
   417  func TestDeletePrivilegeFromBannerRule(t *testing.T) {
   418  	t.Parallel()
   419  
   420  	tests := map[string]struct {
   421  		bannerName, commandName, privilegeName string
   422  
   423  		expectations func(mock sqlmock.Sqlmock)
   424  
   425  		expRes rulesengine.DeleteResult
   426  		expErr assert.ErrorAssertionFunc
   427  	}{
   428  		"Deleted": {
   429  			bannerName:    "a",
   430  			commandName:   "b",
   431  			privilegeName: "c",
   432  
   433  			expectations: func(mock sqlmock.Sqlmock) {
   434  				mock.ExpectExec(datasql.DeletePrivilegeFromBannerRule).
   435  					WithArgs("a", "b", "c").
   436  					WillReturnResult(sqlmock.NewResult(0, 1))
   437  			},
   438  
   439  			expRes: rulesengine.DeleteResult{RowsAffected: 1},
   440  			expErr: assert.NoError,
   441  		},
   442  		// TODO add test for conflict case? DB shouldn't return a case, but missing cover
   443  
   444  		"Unknown Banner": {
   445  			bannerName:    "unknownBanner",
   446  			commandName:   "b",
   447  			privilegeName: "c",
   448  
   449  			expectations: func(mock sqlmock.Sqlmock) {
   450  				mock.ExpectExec(datasql.DeletePrivilegeFromBannerRule).
   451  					WithArgs("unknownBanner", "b", "c").
   452  					WillReturnResult(sqlmock.NewResult(0, 0))
   453  
   454  				mock.ExpectBegin()
   455  
   456  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   457  					WithArgs("unknownBanner", "b", "c").
   458  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}).
   459  						// Banner row is not returned
   460  						AddRow("command", "j").
   461  						AddRow("privilege", "k"),
   462  					)
   463  				mock.ExpectCommit()
   464  			},
   465  
   466  			expRes: rulesengine.DeleteResult{RowsAffected: 0, Errors: []rulesengine.Error{{Type: rulesengine.UnknownBanner, Banner: "unknownBanner"}}},
   467  			expErr: assert.NoError,
   468  		},
   469  		"Unknown everything": {
   470  			bannerName:    "unknownBanner",
   471  			commandName:   "unknownCommand",
   472  			privilegeName: "unknownPrivilege",
   473  
   474  			expectations: func(mock sqlmock.Sqlmock) {
   475  				mock.ExpectExec(datasql.DeletePrivilegeFromBannerRule).
   476  					WithArgs("unknownBanner", "unknownCommand", "unknownPrivilege").
   477  					WillReturnResult(sqlmock.NewResult(0, 0))
   478  				mock.ExpectBegin()
   479  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   480  					// 0 rows are returned
   481  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}))
   482  				mock.ExpectCommit()
   483  			},
   484  
   485  			expRes: rulesengine.DeleteResult{RowsAffected: 0, Errors: []rulesengine.Error{
   486  				{Type: rulesengine.UnknownCommand, Command: "unknownCommand"},
   487  				{Type: rulesengine.UnknownPrivilege, Privilege: "unknownPrivilege"},
   488  				{Type: rulesengine.UnknownBanner, Banner: "unknownBanner"},
   489  			}},
   490  			expErr: assert.NoError,
   491  		},
   492  
   493  		"Unknown Rule association": {
   494  			bannerName:    "a",
   495  			commandName:   "b",
   496  			privilegeName: "c",
   497  
   498  			expectations: func(mock sqlmock.Sqlmock) {
   499  				mock.ExpectExec(datasql.DeletePrivilegeFromBannerRule).
   500  					WillReturnResult(sqlmock.NewResult(0, 0))
   501  				mock.ExpectBegin()
   502  				mock.ExpectQuery(datasql.GetIDsForBannerSegment).
   503  					// All rows are returned
   504  					WillReturnRows(sqlmock.NewRows([]string{"type", "id"}).
   505  						AddRow("banner", "i").
   506  						AddRow("command", "j").
   507  						AddRow("privilege", "k"),
   508  					)
   509  				mock.ExpectCommit()
   510  			},
   511  
   512  			expRes: rulesengine.DeleteResult{RowsAffected: 0, Errors: []rulesengine.Error{
   513  				{Type: rulesengine.UnknownRule, Command: "b", Banner: "a", Privilege: "c"},
   514  			}},
   515  			expErr: assert.NoError,
   516  		},
   517  	}
   518  
   519  	for name, tc := range tests {
   520  		tc := tc
   521  		t.Run(name, func(t *testing.T) {
   522  			t.Parallel()
   523  
   524  			ctx := context.Background()
   525  
   526  			db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   527  			assert.NoError(t, err)
   528  
   529  			tc.expectations(mock)
   530  
   531  			ds := Dataset{db: db}
   532  
   533  			res, err := ds.DeletePrivilegeFromBannerRule(ctx, tc.bannerName, tc.commandName, tc.privilegeName)
   534  			tc.expErr(t, err)
   535  
   536  			assert.Equal(t, tc.expRes, res)
   537  
   538  			assert.NoError(t, mock.ExpectationsWereMet())
   539  		})
   540  	}
   541  }
   542  
   543  func TestReadRulesForAllBanners(t *testing.T) {
   544  	t.Parallel()
   545  
   546  	tests := map[string]struct {
   547  		rows   *sqlmock.Rows
   548  		expRes []rulesengine.RuleSegment
   549  	}{
   550  		"No rules": {
   551  			rows:   sqlmock.NewRows(nil),
   552  			expRes: nil,
   553  		},
   554  		"Single Rule": {
   555  			rows: sqlmock.NewRows([]string{"banner_name", "name", "name", "banner_edge_id", "id", "id"}).
   556  				AddRow("a", "b", "c", "i", "j", "k"),
   557  			expRes: []rulesengine.RuleSegment{{
   558  				Banner:    rulesengine.Banner{BannerName: "a", BannerID: "i"},
   559  				Command:   rulesengine.Command{Name: "b", ID: "j"},
   560  				Privilege: rulesengine.Privilege{Name: "c", ID: "k"},
   561  			}},
   562  		},
   563  		"Multiple Rules": {
   564  			rows: sqlmock.NewRows([]string{"banner_name", "name", "name", "banner_edge_id", "id", "id"}).
   565  				AddRow("a", "b", "c", "r", "s", "t").
   566  				AddRow("d", "e", "f", "u", "v", "w").
   567  				AddRow("g", "h", "i", "x", "y", "z"),
   568  			expRes: []rulesengine.RuleSegment{
   569  				{
   570  					Banner:    rulesengine.Banner{BannerName: "a", BannerID: "r"},
   571  					Command:   rulesengine.Command{Name: "b", ID: "s"},
   572  					Privilege: rulesengine.Privilege{Name: "c", ID: "t"},
   573  				},
   574  				{
   575  					Banner:    rulesengine.Banner{BannerName: "d", BannerID: "u"},
   576  					Command:   rulesengine.Command{Name: "e", ID: "v"},
   577  					Privilege: rulesengine.Privilege{Name: "f", ID: "w"},
   578  				},
   579  				{
   580  					Banner:    rulesengine.Banner{BannerName: "g", BannerID: "x"},
   581  					Command:   rulesengine.Command{Name: "h", ID: "y"},
   582  					Privilege: rulesengine.Privilege{Name: "i", ID: "z"},
   583  				},
   584  			},
   585  		},
   586  	}
   587  
   588  	for name, tc := range tests {
   589  		tc := tc
   590  		t.Run(name, func(t *testing.T) {
   591  			t.Parallel()
   592  
   593  			db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
   594  			assert.NoError(t, err)
   595  
   596  			mock.ExpectQuery(datasql.SelectAllRulesFarAllBanners).
   597  				WillReturnRows(tc.rows)
   598  
   599  			ctx := context.Background()
   600  
   601  			ds := Dataset{db: db}
   602  
   603  			res, err := ds.ReadRulesForAllBanners(ctx)
   604  			assert.NoError(t, err)
   605  
   606  			assert.Equal(t, tc.expRes, res)
   607  
   608  			assert.NoError(t, mock.ExpectationsWereMet())
   609  		})
   610  	}
   611  }
   612  
   613  func TestCreateErrors(t *testing.T) {
   614  	t.Parallel()
   615  
   616  	tests := map[string]struct {
   617  		rule rulesengine.RuleSegment
   618  
   619  		Errors    []rulesengine.Error
   620  		missingID bool
   621  	}{
   622  		"Populated": {
   623  			rule: rulesengine.RuleSegment{
   624  				Command:   rulesengine.Command{Name: "a", ID: "id"},
   625  				Privilege: rulesengine.Privilege{Name: "b", ID: "id"},
   626  				Banner:    rulesengine.Banner{BannerName: "c", BannerID: "id"},
   627  			},
   628  
   629  			Errors:    nil,
   630  			missingID: false,
   631  		},
   632  		"Missing Command": {
   633  			rule: rulesengine.RuleSegment{
   634  				Command:   rulesengine.Command{Name: "a"},
   635  				Privilege: rulesengine.Privilege{Name: "b", ID: "id"},
   636  				Banner:    rulesengine.Banner{BannerName: "c", BannerID: "id"},
   637  			},
   638  
   639  			Errors:    []rulesengine.Error{{Command: "a", Type: rulesengine.UnknownCommand}},
   640  			missingID: true,
   641  		},
   642  		"Missing Privilege": {
   643  			rule: rulesengine.RuleSegment{
   644  				Command:   rulesengine.Command{Name: "a", ID: "id"},
   645  				Privilege: rulesengine.Privilege{Name: "b"},
   646  				Banner:    rulesengine.Banner{BannerName: "c", BannerID: "id"},
   647  			},
   648  
   649  			Errors:    []rulesengine.Error{{Privilege: "b", Type: rulesengine.UnknownPrivilege}},
   650  			missingID: true,
   651  		},
   652  		"Missing Banner": {
   653  			rule: rulesengine.RuleSegment{
   654  				Command:   rulesengine.Command{Name: "a", ID: "id"},
   655  				Privilege: rulesengine.Privilege{Name: "b", ID: "id"},
   656  				Banner:    rulesengine.Banner{BannerName: "c"},
   657  			},
   658  
   659  			Errors:    []rulesengine.Error{{Banner: "c", Type: rulesengine.UnknownBanner}},
   660  			missingID: true,
   661  		},
   662  		"Missing All": {
   663  			rule: rulesengine.RuleSegment{
   664  				Command:   rulesengine.Command{Name: "a"},
   665  				Privilege: rulesengine.Privilege{Name: "b"},
   666  				Banner:    rulesengine.Banner{BannerName: "c"},
   667  			},
   668  
   669  			Errors: []rulesengine.Error{
   670  				{Banner: "c", Type: rulesengine.UnknownBanner},
   671  				{Command: "a", Type: rulesengine.UnknownCommand},
   672  				{Privilege: "b", Type: rulesengine.UnknownPrivilege},
   673  			},
   674  			missingID: true,
   675  		},
   676  	}
   677  
   678  	for name, tc := range tests {
   679  		tc := tc
   680  		t.Run(name, func(t *testing.T) {
   681  			t.Parallel()
   682  
   683  			errors, missingID := createErrors(tc.rule)
   684  			assert.Equal(t, tc.Errors, errors)
   685  			assert.Equal(t, tc.missingID, missingID)
   686  		})
   687  	}
   688  }
   689  

View as plain text