github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/acl_test.go (about)

     1  package structs
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/nomad/ci"
     9  	"github.com/hashicorp/nomad/helper/pointer"
    10  	"github.com/hashicorp/nomad/helper/uuid"
    11  	"github.com/shoenig/test/must"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestACLToken_Canonicalize(t *testing.T) {
    16  	testCases := []struct {
    17  		name   string
    18  		testFn func()
    19  	}{
    20  		{
    21  			name: "token with accessor",
    22  			testFn: func() {
    23  				mockToken := &ACLToken{
    24  					AccessorID:  uuid.Generate(),
    25  					SecretID:    uuid.Generate(),
    26  					Name:        "my cool token " + uuid.Generate(),
    27  					Type:        "client",
    28  					Policies:    []string{"foo", "bar"},
    29  					Roles:       []*ACLTokenRoleLink{},
    30  					Global:      false,
    31  					CreateTime:  time.Now().UTC(),
    32  					CreateIndex: 10,
    33  					ModifyIndex: 20,
    34  				}
    35  				mockToken.SetHash()
    36  				copiedMockToken := mockToken.Copy()
    37  
    38  				mockToken.Canonicalize()
    39  				require.Equal(t, copiedMockToken, mockToken)
    40  			},
    41  		},
    42  		{
    43  			name: "token without accessor",
    44  			testFn: func() {
    45  				mockToken := &ACLToken{
    46  					Name:     "my cool token " + uuid.Generate(),
    47  					Type:     "client",
    48  					Policies: []string{"foo", "bar"},
    49  					Global:   false,
    50  				}
    51  
    52  				mockToken.Canonicalize()
    53  				require.NotEmpty(t, mockToken.AccessorID)
    54  				require.NotEmpty(t, mockToken.SecretID)
    55  				require.NotEmpty(t, mockToken.CreateTime)
    56  			},
    57  		},
    58  		{
    59  			name: "token with ttl without accessor",
    60  			testFn: func() {
    61  				mockToken := &ACLToken{
    62  					Name:          "my cool token " + uuid.Generate(),
    63  					Type:          "client",
    64  					Policies:      []string{"foo", "bar"},
    65  					Global:        false,
    66  					ExpirationTTL: 10 * time.Hour,
    67  				}
    68  
    69  				mockToken.Canonicalize()
    70  				require.NotEmpty(t, mockToken.AccessorID)
    71  				require.NotEmpty(t, mockToken.SecretID)
    72  				require.NotEmpty(t, mockToken.CreateTime)
    73  				require.NotEmpty(t, mockToken.ExpirationTime)
    74  			},
    75  		},
    76  	}
    77  
    78  	for _, tc := range testCases {
    79  		t.Run(tc.name, func(t *testing.T) {
    80  			tc.testFn()
    81  		})
    82  	}
    83  }
    84  
    85  func TestACLTokenValidate(t *testing.T) {
    86  	ci.Parallel(t)
    87  
    88  	testCases := []struct {
    89  		name                  string
    90  		inputACLToken         *ACLToken
    91  		inputExistingACLToken *ACLToken
    92  		expectedErrorContains string
    93  	}{
    94  		{
    95  			name:                  "missing type",
    96  			inputACLToken:         &ACLToken{},
    97  			inputExistingACLToken: nil,
    98  			expectedErrorContains: "client or management",
    99  		},
   100  		{
   101  			name: "missing policies or roles",
   102  			inputACLToken: &ACLToken{
   103  				Type: ACLClientToken,
   104  			},
   105  			inputExistingACLToken: nil,
   106  			expectedErrorContains: "missing policies or roles",
   107  		},
   108  		{
   109  			name: "invalid policies",
   110  			inputACLToken: &ACLToken{
   111  				Type:     ACLManagementToken,
   112  				Policies: []string{"foo"},
   113  			},
   114  			inputExistingACLToken: nil,
   115  			expectedErrorContains: "associated with policies or roles",
   116  		},
   117  		{
   118  			name: "invalid roles",
   119  			inputACLToken: &ACLToken{
   120  				Type:  ACLManagementToken,
   121  				Roles: []*ACLTokenRoleLink{{Name: "foo"}},
   122  			},
   123  			inputExistingACLToken: nil,
   124  			expectedErrorContains: "associated with policies or roles",
   125  		},
   126  		{
   127  			name: "name too long",
   128  			inputACLToken: &ACLToken{
   129  				Type: ACLManagementToken,
   130  				Name: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() +
   131  					uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(),
   132  			},
   133  			inputExistingACLToken: nil,
   134  			expectedErrorContains: "name too long",
   135  		},
   136  		{
   137  			name: "negative TTL",
   138  			inputACLToken: &ACLToken{
   139  				Type:          ACLManagementToken,
   140  				Name:          "foo",
   141  				ExpirationTTL: -1 * time.Hour,
   142  			},
   143  			inputExistingACLToken: nil,
   144  			expectedErrorContains: "should not be negative",
   145  		},
   146  		{
   147  			name: "TTL too small",
   148  			inputACLToken: &ACLToken{
   149  				Type:           ACLManagementToken,
   150  				Name:           "foo",
   151  				CreateTime:     time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
   152  				ExpirationTime: pointer.Of(time.Date(2022, time.July, 11, 16, 23, 10, 0, time.UTC)),
   153  			},
   154  			inputExistingACLToken: nil,
   155  			expectedErrorContains: "expiration time cannot be less than",
   156  		},
   157  		{
   158  			name: "TTL too large",
   159  			inputACLToken: &ACLToken{
   160  				Type:           ACLManagementToken,
   161  				Name:           "foo",
   162  				CreateTime:     time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC),
   163  				ExpirationTime: pointer.Of(time.Date(2042, time.July, 11, 16, 23, 0, 0, time.UTC)),
   164  			},
   165  			inputExistingACLToken: nil,
   166  			expectedErrorContains: "expiration time cannot be more than",
   167  		},
   168  		{
   169  			name: "valid management",
   170  			inputACLToken: &ACLToken{
   171  				Type: ACLManagementToken,
   172  				Name: "foo",
   173  			},
   174  			inputExistingACLToken: nil,
   175  			expectedErrorContains: "",
   176  		},
   177  		{
   178  			name: "valid client",
   179  			inputACLToken: &ACLToken{
   180  				Type:     ACLClientToken,
   181  				Name:     "foo",
   182  				Policies: []string{"foo"},
   183  			},
   184  			inputExistingACLToken: nil,
   185  			expectedErrorContains: "",
   186  		},
   187  	}
   188  
   189  	for _, tc := range testCases {
   190  		t.Run(tc.name, func(t *testing.T) {
   191  			actualOutputError := tc.inputACLToken.Validate(1*time.Minute, 24*time.Hour, tc.inputExistingACLToken)
   192  			if tc.expectedErrorContains != "" {
   193  				require.ErrorContains(t, actualOutputError, tc.expectedErrorContains)
   194  			} else {
   195  				require.NoError(t, actualOutputError)
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func TestACLToken_HasExpirationTime(t *testing.T) {
   202  	testCases := []struct {
   203  		name           string
   204  		inputACLToken  *ACLToken
   205  		expectedOutput bool ``
   206  	}{
   207  		{
   208  			name:           "nil acl token",
   209  			inputACLToken:  nil,
   210  			expectedOutput: false,
   211  		},
   212  		{
   213  			name:           "default empty value",
   214  			inputACLToken:  &ACLToken{},
   215  			expectedOutput: false,
   216  		},
   217  		{
   218  			name: "expiration set to now",
   219  			inputACLToken: &ACLToken{
   220  				ExpirationTime: pointer.Of(time.Now().UTC()),
   221  			},
   222  			expectedOutput: true,
   223  		},
   224  		{
   225  			name: "expiration set to past",
   226  			inputACLToken: &ACLToken{
   227  				ExpirationTime: pointer.Of(time.Date(2022, time.February, 21, 19, 35, 0, 0, time.UTC)),
   228  			},
   229  			expectedOutput: true,
   230  		},
   231  		{
   232  			name: "expiration set to future",
   233  			inputACLToken: &ACLToken{
   234  				ExpirationTime: pointer.Of(time.Date(2087, time.April, 25, 12, 0, 0, 0, time.UTC)),
   235  			},
   236  			expectedOutput: true,
   237  		},
   238  	}
   239  
   240  	for _, tc := range testCases {
   241  		t.Run(tc.name, func(t *testing.T) {
   242  			actualOutput := tc.inputACLToken.HasExpirationTime()
   243  			require.Equal(t, tc.expectedOutput, actualOutput)
   244  		})
   245  	}
   246  }
   247  
   248  func TestACLToken_IsExpired(t *testing.T) {
   249  	testCases := []struct {
   250  		name           string
   251  		inputACLToken  *ACLToken
   252  		inputTime      time.Time
   253  		expectedOutput bool
   254  	}{
   255  		{
   256  			name:           "token without expiry",
   257  			inputACLToken:  &ACLToken{},
   258  			inputTime:      time.Now().UTC(),
   259  			expectedOutput: false,
   260  		},
   261  		{
   262  			name:           "empty input time",
   263  			inputACLToken:  &ACLToken{},
   264  			inputTime:      time.Time{},
   265  			expectedOutput: false,
   266  		},
   267  		{
   268  			name: "token not expired",
   269  			inputACLToken: &ACLToken{
   270  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   271  			},
   272  			inputTime:      time.Date(2022, time.May, 9, 10, 26, 0, 0, time.UTC),
   273  			expectedOutput: false,
   274  		},
   275  		{
   276  			name: "token expired",
   277  			inputACLToken: &ACLToken{
   278  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   279  			},
   280  			inputTime:      time.Date(2022, time.May, 9, 10, 28, 0, 0, time.UTC),
   281  			expectedOutput: true,
   282  		},
   283  		{
   284  			name: "empty input time",
   285  			inputACLToken: &ACLToken{
   286  				ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)),
   287  			},
   288  			inputTime:      time.Time{},
   289  			expectedOutput: true,
   290  		},
   291  	}
   292  
   293  	for _, tc := range testCases {
   294  		t.Run(tc.name, func(t *testing.T) {
   295  			actualOutput := tc.inputACLToken.IsExpired(tc.inputTime)
   296  			require.Equal(t, tc.expectedOutput, actualOutput)
   297  		})
   298  	}
   299  }
   300  
   301  func TestACLToken_HasRoles(t *testing.T) {
   302  	testCases := []struct {
   303  		name           string
   304  		inputToken     *ACLToken
   305  		inputRoleIDs   []string
   306  		expectedOutput bool
   307  	}{
   308  		{
   309  			name: "client token request all subset",
   310  			inputToken: &ACLToken{
   311  				Type: ACLClientToken,
   312  				Roles: []*ACLTokenRoleLink{
   313  					{ID: "foo"},
   314  					{ID: "bar"},
   315  					{ID: "baz"},
   316  				},
   317  			},
   318  			inputRoleIDs:   []string{"foo", "bar", "baz"},
   319  			expectedOutput: true,
   320  		},
   321  		{
   322  			name: "client token request partial subset",
   323  			inputToken: &ACLToken{
   324  				Type: ACLClientToken,
   325  				Roles: []*ACLTokenRoleLink{
   326  					{ID: "foo"},
   327  					{ID: "bar"},
   328  					{ID: "baz"},
   329  				},
   330  			},
   331  			inputRoleIDs:   []string{"foo", "baz"},
   332  			expectedOutput: true,
   333  		},
   334  		{
   335  			name: "client token request one subset",
   336  			inputToken: &ACLToken{
   337  				Type: ACLClientToken,
   338  				Roles: []*ACLTokenRoleLink{
   339  					{ID: "foo"},
   340  					{ID: "bar"},
   341  					{ID: "baz"},
   342  				},
   343  			},
   344  			inputRoleIDs:   []string{"baz"},
   345  			expectedOutput: true,
   346  		},
   347  		{
   348  			name: "client token request no subset",
   349  			inputToken: &ACLToken{
   350  				Type: ACLClientToken,
   351  				Roles: []*ACLTokenRoleLink{
   352  					{ID: "foo"},
   353  					{ID: "bar"},
   354  					{ID: "baz"},
   355  				},
   356  			},
   357  			inputRoleIDs:   []string{"new"},
   358  			expectedOutput: false,
   359  		},
   360  	}
   361  
   362  	for _, tc := range testCases {
   363  		t.Run(tc.name, func(t *testing.T) {
   364  			actualOutput := tc.inputToken.HasRoles(tc.inputRoleIDs)
   365  			require.Equal(t, tc.expectedOutput, actualOutput)
   366  		})
   367  	}
   368  }
   369  
   370  func TestACLRole_SetHash(t *testing.T) {
   371  	testCases := []struct {
   372  		name           string
   373  		inputACLRole   *ACLRole
   374  		expectedOutput []byte
   375  	}{
   376  		{
   377  			name: "no hash set",
   378  			inputACLRole: &ACLRole{
   379  				Name:        "acl-role",
   380  				Description: "mocked-test-acl-role",
   381  				Policies: []*ACLRolePolicyLink{
   382  					{Name: "mocked-test-policy-1"},
   383  					{Name: "mocked-test-policy-2"},
   384  				},
   385  				CreateIndex: 10,
   386  				ModifyIndex: 10,
   387  				Hash:        []byte{},
   388  			},
   389  			expectedOutput: []byte{
   390  				122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   391  				171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   392  			},
   393  		},
   394  		{
   395  			name: "hash set with change",
   396  			inputACLRole: &ACLRole{
   397  				Name:        "acl-role",
   398  				Description: "mocked-test-acl-role",
   399  				Policies: []*ACLRolePolicyLink{
   400  					{Name: "mocked-test-policy-1"},
   401  					{Name: "mocked-test-policy-2"},
   402  				},
   403  				CreateIndex: 10,
   404  				ModifyIndex: 10,
   405  				Hash: []byte{
   406  					137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
   407  					106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
   408  				},
   409  			},
   410  			expectedOutput: []byte{
   411  				122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   412  				171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   413  			},
   414  		},
   415  	}
   416  
   417  	for _, tc := range testCases {
   418  		t.Run(tc.name, func(t *testing.T) {
   419  			actualOutput := tc.inputACLRole.SetHash()
   420  			require.Equal(t, tc.expectedOutput, actualOutput)
   421  			require.Equal(t, tc.inputACLRole.Hash, actualOutput)
   422  		})
   423  	}
   424  }
   425  
   426  func TestACLRole_Validate(t *testing.T) {
   427  	testCases := []struct {
   428  		name                  string
   429  		inputACLRole          *ACLRole
   430  		expectedError         bool
   431  		expectedErrorContains string
   432  	}{
   433  		{
   434  			name: "role name too long",
   435  			inputACLRole: &ACLRole{
   436  				Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   437  			},
   438  			expectedError:         true,
   439  			expectedErrorContains: "invalid name",
   440  		},
   441  		{
   442  			name: "role name too short",
   443  			inputACLRole: &ACLRole{
   444  				Name: "",
   445  			},
   446  			expectedError:         true,
   447  			expectedErrorContains: "invalid name",
   448  		},
   449  		{
   450  			name: "role name with invalid characters",
   451  			inputACLRole: &ACLRole{
   452  				Name: "--#$%$^%_%%_?>",
   453  			},
   454  			expectedError:         true,
   455  			expectedErrorContains: "invalid name",
   456  		},
   457  		{
   458  			name: "description too long",
   459  			inputACLRole: &ACLRole{
   460  				Name:        "acl-role",
   461  				Description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
   462  			},
   463  			expectedError:         true,
   464  			expectedErrorContains: "description longer than",
   465  		},
   466  		{
   467  			name: "no policies",
   468  			inputACLRole: &ACLRole{
   469  				Name:        "acl-role",
   470  				Description: "",
   471  			},
   472  			expectedError:         true,
   473  			expectedErrorContains: "at least one policy should be specified",
   474  		},
   475  		{
   476  			name: "valid",
   477  			inputACLRole: &ACLRole{
   478  				Name:        "acl-role",
   479  				Description: "",
   480  				Policies: []*ACLRolePolicyLink{
   481  					{Name: "policy-1"},
   482  				},
   483  			},
   484  			expectedError:         false,
   485  			expectedErrorContains: "",
   486  		},
   487  	}
   488  
   489  	for _, tc := range testCases {
   490  		t.Run(tc.name, func(t *testing.T) {
   491  			actualOutput := tc.inputACLRole.Validate()
   492  			if tc.expectedError {
   493  				require.ErrorContains(t, actualOutput, tc.expectedErrorContains)
   494  			} else {
   495  				require.NoError(t, actualOutput)
   496  			}
   497  		})
   498  	}
   499  }
   500  
   501  func TestACLRole_Canonicalize(t *testing.T) {
   502  	testCases := []struct {
   503  		name         string
   504  		inputACLRole *ACLRole
   505  	}{
   506  		{
   507  			name:         "no ID set",
   508  			inputACLRole: &ACLRole{},
   509  		},
   510  		{
   511  			name:         "id set",
   512  			inputACLRole: &ACLRole{ID: "some-random-uuid"},
   513  		},
   514  	}
   515  
   516  	for _, tc := range testCases {
   517  		t.Run(tc.name, func(t *testing.T) {
   518  			existing := tc.inputACLRole.Copy()
   519  			tc.inputACLRole.Canonicalize()
   520  			if existing.ID == "" {
   521  				require.NotEmpty(t, tc.inputACLRole.ID)
   522  			} else {
   523  				require.Equal(t, existing.ID, tc.inputACLRole.ID)
   524  			}
   525  		})
   526  	}
   527  }
   528  
   529  func TestACLRole_Equals(t *testing.T) {
   530  	testCases := []struct {
   531  		name            string
   532  		composedACLRole *ACLRole
   533  		inputACLRole    *ACLRole
   534  		expectedOutput  bool
   535  	}{
   536  		{
   537  			name: "equal with hash set",
   538  			composedACLRole: &ACLRole{
   539  				Name:        "acl-role-",
   540  				Description: "mocked-test-acl-role",
   541  				Policies: []*ACLRolePolicyLink{
   542  					{Name: "mocked-test-policy-1"},
   543  					{Name: "mocked-test-policy-2"},
   544  				},
   545  				CreateIndex: 10,
   546  				ModifyIndex: 10,
   547  				Hash: []byte{
   548  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   549  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   550  				},
   551  			},
   552  			inputACLRole: &ACLRole{
   553  				Name:        "acl-role",
   554  				Description: "mocked-test-acl-role",
   555  				Policies: []*ACLRolePolicyLink{
   556  					{Name: "mocked-test-policy-1"},
   557  					{Name: "mocked-test-policy-2"},
   558  				},
   559  				CreateIndex: 10,
   560  				ModifyIndex: 10,
   561  				Hash: []byte{
   562  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   563  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   564  				},
   565  			},
   566  			expectedOutput: true,
   567  		},
   568  		{
   569  			name: "equal without hash set",
   570  			composedACLRole: &ACLRole{
   571  				Name:        "acl-role",
   572  				Description: "mocked-test-acl-role",
   573  				Policies: []*ACLRolePolicyLink{
   574  					{Name: "mocked-test-policy-1"},
   575  					{Name: "mocked-test-policy-2"},
   576  				},
   577  				CreateIndex: 10,
   578  				ModifyIndex: 10,
   579  				Hash:        []byte{},
   580  			},
   581  			inputACLRole: &ACLRole{
   582  				Name:        "acl-role",
   583  				Description: "mocked-test-acl-role",
   584  				Policies: []*ACLRolePolicyLink{
   585  					{Name: "mocked-test-policy-1"},
   586  					{Name: "mocked-test-policy-2"},
   587  				},
   588  				CreateIndex: 10,
   589  				ModifyIndex: 10,
   590  				Hash:        []byte{},
   591  			},
   592  			expectedOutput: true,
   593  		},
   594  		{
   595  			name:            "both nil",
   596  			composedACLRole: nil,
   597  			inputACLRole:    nil,
   598  			expectedOutput:  true,
   599  		},
   600  		{
   601  			name:            "not equal composed nil",
   602  			composedACLRole: nil,
   603  			inputACLRole: &ACLRole{
   604  				Name:        "acl-role",
   605  				Description: "mocked-test-acl-role",
   606  				Policies: []*ACLRolePolicyLink{
   607  					{Name: "mocked-test-policy-1"},
   608  					{Name: "mocked-test-policy-2"},
   609  				},
   610  				CreateIndex: 10,
   611  				ModifyIndex: 10,
   612  				Hash: []byte{
   613  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   614  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   615  				},
   616  			},
   617  			expectedOutput: false,
   618  		},
   619  		{
   620  			name: "not equal input nil",
   621  			composedACLRole: &ACLRole{
   622  				Name:        "acl-role",
   623  				Description: "mocked-test-acl-role",
   624  				Policies: []*ACLRolePolicyLink{
   625  					{Name: "mocked-test-policy-1"},
   626  					{Name: "mocked-test-policy-2"},
   627  				},
   628  				CreateIndex: 10,
   629  				ModifyIndex: 10,
   630  				Hash: []byte{
   631  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   632  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   633  				},
   634  			},
   635  			inputACLRole:   nil,
   636  			expectedOutput: false,
   637  		},
   638  		{
   639  			name: "not equal with hash set",
   640  			composedACLRole: &ACLRole{
   641  				Name:        "acl-role",
   642  				Description: "mocked-test-acl-role",
   643  				Policies: []*ACLRolePolicyLink{
   644  					{Name: "mocked-test-policy-1"},
   645  					{Name: "mocked-test-policy-2"},
   646  				},
   647  				CreateIndex: 10,
   648  				ModifyIndex: 10,
   649  				Hash: []byte{
   650  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   651  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   652  				},
   653  			},
   654  			inputACLRole: &ACLRole{
   655  				Name:        "acl-role",
   656  				Description: "mocked-test-acl-role",
   657  				Policies: []*ACLRolePolicyLink{
   658  					{Name: "mocked-test-policy-1"},
   659  				},
   660  				CreateIndex: 10,
   661  				ModifyIndex: 10,
   662  				Hash: []byte{
   663  					137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34,
   664  					106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5,
   665  				},
   666  			},
   667  			expectedOutput: false,
   668  		},
   669  		{
   670  			name: "not equal without hash set",
   671  			composedACLRole: &ACLRole{
   672  				Name:        "acl-role",
   673  				Description: "mocked-test-acl-role",
   674  				Policies: []*ACLRolePolicyLink{
   675  					{Name: "mocked-test-policy-1"},
   676  					{Name: "mocked-test-policy-2"},
   677  				},
   678  				CreateIndex: 10,
   679  				ModifyIndex: 10,
   680  				Hash:        []byte{},
   681  			},
   682  			inputACLRole: &ACLRole{
   683  				Name:        "acl-role",
   684  				Description: "mocked-test-acl-role",
   685  				Policies: []*ACLRolePolicyLink{
   686  					{Name: "mocked-test-policy-1"},
   687  				},
   688  				CreateIndex: 10,
   689  				ModifyIndex: 10,
   690  				Hash:        []byte{},
   691  			},
   692  			expectedOutput: false,
   693  		},
   694  	}
   695  
   696  	for _, tc := range testCases {
   697  		t.Run(tc.name, func(t *testing.T) {
   698  			actualOutput := tc.composedACLRole.Equal(tc.inputACLRole)
   699  			require.Equal(t, tc.expectedOutput, actualOutput)
   700  		})
   701  	}
   702  }
   703  
   704  func TestACLRole_Copy(t *testing.T) {
   705  	testCases := []struct {
   706  		name         string
   707  		inputACLRole *ACLRole
   708  	}{
   709  		{
   710  			name:         "nil input",
   711  			inputACLRole: nil,
   712  		},
   713  		{
   714  			name: "general 1",
   715  			inputACLRole: &ACLRole{
   716  				Name:        fmt.Sprintf("acl-role"),
   717  				Description: "mocked-test-acl-role",
   718  				Policies: []*ACLRolePolicyLink{
   719  					{Name: "mocked-test-policy-1"},
   720  					{Name: "mocked-test-policy-2"},
   721  				},
   722  				CreateIndex: 10,
   723  				ModifyIndex: 10,
   724  				Hash: []byte{
   725  					122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160,
   726  					171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102,
   727  				},
   728  			},
   729  		},
   730  	}
   731  
   732  	for _, tc := range testCases {
   733  		t.Run(tc.name, func(t *testing.T) {
   734  			actualOutput := tc.inputACLRole.Copy()
   735  			require.Equal(t, tc.inputACLRole, actualOutput)
   736  		})
   737  	}
   738  }
   739  
   740  func TestACLRole_Stub(t *testing.T) {
   741  	testCases := []struct {
   742  		name           string
   743  		inputACLRole   *ACLRole
   744  		expectedOutput *ACLRoleListStub
   745  	}{
   746  		{
   747  			name: "partially hydrated",
   748  			inputACLRole: &ACLRole{
   749  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   750  				Name:        "my-lovely-role",
   751  				Description: "",
   752  				Policies: []*ACLRolePolicyLink{
   753  					{Name: "my-lovely-policy"},
   754  				},
   755  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   756  				CreateIndex: 24,
   757  				ModifyIndex: 24,
   758  			},
   759  			expectedOutput: &ACLRoleListStub{
   760  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   761  				Name:        "my-lovely-role",
   762  				Description: "",
   763  				Policies: []*ACLRolePolicyLink{
   764  					{Name: "my-lovely-policy"},
   765  				},
   766  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   767  				CreateIndex: 24,
   768  				ModifyIndex: 24,
   769  			},
   770  		},
   771  		{
   772  			name: "hully hydrated",
   773  			inputACLRole: &ACLRole{
   774  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   775  				Name:        "my-lovely-role",
   776  				Description: "this-is-my-lovely-role",
   777  				Policies: []*ACLRolePolicyLink{
   778  					{Name: "my-lovely-policy"},
   779  				},
   780  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   781  				CreateIndex: 24,
   782  				ModifyIndex: 24,
   783  			},
   784  			expectedOutput: &ACLRoleListStub{
   785  				ID:          "1d6332c8-02d7-325e-f675-a9bb4aff0c51",
   786  				Name:        "my-lovely-role",
   787  				Description: "this-is-my-lovely-role",
   788  				Policies: []*ACLRolePolicyLink{
   789  					{Name: "my-lovely-policy"},
   790  				},
   791  				Hash:        []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
   792  				CreateIndex: 24,
   793  				ModifyIndex: 24,
   794  			},
   795  		},
   796  	}
   797  
   798  	for _, tc := range testCases {
   799  		t.Run(tc.name, func(t *testing.T) {
   800  			actualOutput := tc.inputACLRole.Stub()
   801  			require.Equal(t, tc.expectedOutput, actualOutput)
   802  		})
   803  	}
   804  }
   805  
   806  func Test_ACLRolesUpsertRequest(t *testing.T) {
   807  	req := ACLRolesUpsertRequest{}
   808  	require.False(t, req.IsRead())
   809  }
   810  
   811  func Test_ACLRolesDeleteByIDRequest(t *testing.T) {
   812  	req := ACLRolesDeleteByIDRequest{}
   813  	require.False(t, req.IsRead())
   814  }
   815  
   816  func Test_ACLRolesListRequest(t *testing.T) {
   817  	req := ACLRolesListRequest{}
   818  	require.True(t, req.IsRead())
   819  }
   820  
   821  func Test_ACLRolesByIDRequest(t *testing.T) {
   822  	req := ACLRolesByIDRequest{}
   823  	require.True(t, req.IsRead())
   824  }
   825  
   826  func Test_ACLRoleByIDRequest(t *testing.T) {
   827  	req := ACLRoleByIDRequest{}
   828  	require.True(t, req.IsRead())
   829  }
   830  
   831  func Test_ACLRoleByNameRequest(t *testing.T) {
   832  	req := ACLRoleByNameRequest{}
   833  	require.True(t, req.IsRead())
   834  }
   835  
   836  func Test_ACLAuthMethodListRequest(t *testing.T) {
   837  	req := ACLAuthMethodListRequest{}
   838  	must.True(t, req.IsRead())
   839  }
   840  
   841  func Test_ACLAuthMethodGetRequest(t *testing.T) {
   842  	req := ACLAuthMethodGetRequest{}
   843  	must.True(t, req.IsRead())
   844  }
   845  
   846  func TestACLAuthMethodSetHash(t *testing.T) {
   847  	ci.Parallel(t)
   848  
   849  	am := &ACLAuthMethod{
   850  		Name: "foo",
   851  		Type: "bad type",
   852  	}
   853  	out1 := am.SetHash()
   854  	must.NotNil(t, out1)
   855  	must.NotNil(t, am.Hash)
   856  	must.Eq(t, out1, am.Hash)
   857  
   858  	am.Type = "good type"
   859  	out2 := am.SetHash()
   860  	must.NotNil(t, out2)
   861  	must.NotNil(t, am.Hash)
   862  	must.Eq(t, out2, am.Hash)
   863  	must.NotEq(t, out1, out2)
   864  }
   865  
   866  func TestACLAuthMethod_Stub(t *testing.T) {
   867  	ci.Parallel(t)
   868  
   869  	maxTokenTTL, _ := time.ParseDuration("3600s")
   870  	am := ACLAuthMethod{
   871  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   872  		Type:          "acl-auth-mock-type",
   873  		TokenLocality: "locality",
   874  		MaxTokenTTL:   maxTokenTTL,
   875  		Default:       true,
   876  		Config: &ACLAuthMethodConfig{
   877  			OIDCDiscoveryURL:    "http://example.com",
   878  			OIDCClientID:        "mock",
   879  			OIDCClientSecret:    "very secret secret",
   880  			BoundAudiences:      []string{"audience1", "audience2"},
   881  			AllowedRedirectURIs: []string{"foo", "bar"},
   882  			DiscoveryCaPem:      []string{"foo"},
   883  			SigningAlgs:         []string{"bar"},
   884  			ClaimMappings:       map[string]string{"foo": "bar"},
   885  			ListClaimMappings:   map[string]string{"foo": "bar"},
   886  		},
   887  		CreateTime:  time.Now().UTC(),
   888  		CreateIndex: 10,
   889  		ModifyIndex: 10,
   890  	}
   891  	am.SetHash()
   892  
   893  	must.Eq(t, am.Stub(), &ACLAuthMethodStub{
   894  		Name:        am.Name,
   895  		Type:        am.Type,
   896  		Default:     am.Default,
   897  		Hash:        am.Hash,
   898  		CreateIndex: am.CreateIndex,
   899  		ModifyIndex: am.ModifyIndex,
   900  	})
   901  
   902  	nilAuthMethod := &ACLAuthMethod{}
   903  	must.Eq(t, nilAuthMethod.Stub(), &ACLAuthMethodStub{})
   904  }
   905  
   906  func TestACLAuthMethod_Equal(t *testing.T) {
   907  	ci.Parallel(t)
   908  
   909  	maxTokenTTL, _ := time.ParseDuration("3600s")
   910  	am1 := &ACLAuthMethod{
   911  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   912  		Type:          "acl-auth-mock-type",
   913  		TokenLocality: "locality",
   914  		MaxTokenTTL:   maxTokenTTL,
   915  		Default:       true,
   916  		Config: &ACLAuthMethodConfig{
   917  			OIDCDiscoveryURL:    "http://example.com",
   918  			OIDCClientID:        "mock",
   919  			OIDCClientSecret:    "very secret secret",
   920  			BoundAudiences:      []string{"audience1", "audience2"},
   921  			AllowedRedirectURIs: []string{"foo", "bar"},
   922  			DiscoveryCaPem:      []string{"foo"},
   923  			SigningAlgs:         []string{"bar"},
   924  			ClaimMappings:       map[string]string{"foo": "bar"},
   925  			ListClaimMappings:   map[string]string{"foo": "bar"},
   926  		},
   927  		CreateTime:  time.Now().UTC(),
   928  		CreateIndex: 10,
   929  		ModifyIndex: 10,
   930  	}
   931  	am1.SetHash()
   932  
   933  	// am2 differs from am1 by 1 nested conf field
   934  	am2 := am1.Copy()
   935  	am2.Config.OIDCClientID = "mock2"
   936  	am2.SetHash()
   937  
   938  	tests := []struct {
   939  		name    string
   940  		method1 *ACLAuthMethod
   941  		method2 *ACLAuthMethod
   942  		want    bool
   943  	}{
   944  		{"one nil", am1, &ACLAuthMethod{}, false},
   945  		{"both nil", &ACLAuthMethod{}, &ACLAuthMethod{}, true},
   946  		{"one is different than the other", am1, am2, false},
   947  		{"equal", am1, am1.Copy(), true},
   948  	}
   949  	for _, tt := range tests {
   950  		t.Run(tt.name, func(t *testing.T) {
   951  			got := tt.method1.Equal(tt.method2)
   952  			must.Eq(t, got, tt.want, must.Sprintf(
   953  				"ACLAuthMethod.Equal() got %v, want %v, test case: %s", got, tt.want, tt.name))
   954  		})
   955  	}
   956  }
   957  
   958  func TestACLAuthMethod_Copy(t *testing.T) {
   959  	ci.Parallel(t)
   960  
   961  	maxTokenTTL, _ := time.ParseDuration("3600s")
   962  	am1 := &ACLAuthMethod{
   963  		Name:          fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
   964  		Type:          "acl-auth-mock-type",
   965  		TokenLocality: "locality",
   966  		MaxTokenTTL:   maxTokenTTL,
   967  		Default:       true,
   968  		Config: &ACLAuthMethodConfig{
   969  			OIDCDiscoveryURL:    "http://example.com",
   970  			OIDCClientID:        "mock",
   971  			OIDCClientSecret:    "very secret secret",
   972  			BoundAudiences:      []string{"audience1", "audience2"},
   973  			AllowedRedirectURIs: []string{"foo", "bar"},
   974  			DiscoveryCaPem:      []string{"foo"},
   975  			SigningAlgs:         []string{"bar"},
   976  			ClaimMappings:       map[string]string{"foo": "bar"},
   977  			ListClaimMappings:   map[string]string{"foo": "bar"},
   978  		},
   979  		CreateTime:  time.Now().UTC(),
   980  		CreateIndex: 10,
   981  		ModifyIndex: 10,
   982  	}
   983  	am1.SetHash()
   984  
   985  	am2 := am1.Copy()
   986  	am2.SetHash()
   987  	must.Eq(t, am1, am2)
   988  
   989  	am3 := am1.Copy()
   990  	am3.Config.AllowedRedirectURIs = []string{"new", "urls"}
   991  	am3.SetHash()
   992  	must.NotEq(t, am1, am3)
   993  }
   994  
   995  func TestACLAuthMethod_Validate(t *testing.T) {
   996  	ci.Parallel(t)
   997  
   998  	goodTTL, _ := time.ParseDuration("3600s")
   999  	badTTL, _ := time.ParseDuration("3600h")
  1000  
  1001  	tests := []struct {
  1002  		name        string
  1003  		method      *ACLAuthMethod
  1004  		wantErr     bool
  1005  		errContains string
  1006  	}{
  1007  		{
  1008  			"valid method",
  1009  			&ACLAuthMethod{
  1010  				Name:          "mock-auth-method",
  1011  				Type:          "OIDC",
  1012  				TokenLocality: "local",
  1013  				MaxTokenTTL:   goodTTL,
  1014  			},
  1015  			false,
  1016  			"",
  1017  		},
  1018  		{"invalid name", &ACLAuthMethod{Name: "is this name invalid?"}, true, "invalid name"},
  1019  		{"invalid token locality", &ACLAuthMethod{TokenLocality: "regional"}, true, "invalid token locality"},
  1020  		{"invalid type", &ACLAuthMethod{Type: "groovy"}, true, "invalid token type"},
  1021  		{"invalid max ttl", &ACLAuthMethod{MaxTokenTTL: badTTL}, true, "invalid token type"},
  1022  	}
  1023  	for _, tt := range tests {
  1024  		t.Run(tt.name, func(t *testing.T) {
  1025  			minTTL, _ := time.ParseDuration("10s")
  1026  			maxTTL, _ := time.ParseDuration("10h")
  1027  			got := tt.method.Validate(minTTL, maxTTL)
  1028  			if tt.wantErr {
  1029  				must.Error(t, got, must.Sprintf(
  1030  					"ACLAuthMethod.Validate() got error, didn't expect it; test case: %s", tt.name))
  1031  				must.StrContains(t, got.Error(), tt.errContains, must.Sprintf(
  1032  					"ACLAuthMethod.Validate() got %v error message, expected %v; test case: %s",
  1033  					got, tt.errContains, tt.name))
  1034  			} else {
  1035  				must.NoError(t, got, must.Sprintf(
  1036  					"ACLAuthMethod.Validate() expected an error but didn't get one; test case: %s", tt.name))
  1037  			}
  1038  		})
  1039  	}
  1040  }
  1041  
  1042  func TestACLAuthMethodConfig_Copy(t *testing.T) {
  1043  	ci.Parallel(t)
  1044  
  1045  	amc1 := &ACLAuthMethodConfig{
  1046  		OIDCDiscoveryURL:    "http://example.com",
  1047  		OIDCClientID:        "mock",
  1048  		OIDCClientSecret:    "very secret secret",
  1049  		BoundAudiences:      []string{"audience1", "audience2"},
  1050  		AllowedRedirectURIs: []string{"foo", "bar"},
  1051  		DiscoveryCaPem:      []string{"foo"},
  1052  		SigningAlgs:         []string{"bar"},
  1053  		ClaimMappings:       map[string]string{"foo": "bar"},
  1054  		ListClaimMappings:   map[string]string{"foo": "bar"},
  1055  	}
  1056  
  1057  	amc2 := amc1.Copy()
  1058  	must.Eq(t, amc1, amc2)
  1059  
  1060  	amc3 := amc1.Copy()
  1061  	amc3.AllowedRedirectURIs = []string{"new", "urls"}
  1062  	must.NotEq(t, amc1, amc3)
  1063  }
  1064  
  1065  func TestACLAuthMethod_Canonicalize(t *testing.T) {
  1066  	now := time.Now().UTC()
  1067  	tests := []struct {
  1068  		name        string
  1069  		inputMethod *ACLAuthMethod
  1070  	}{
  1071  		{
  1072  			"no create time or modify time set",
  1073  			&ACLAuthMethod{},
  1074  		},
  1075  		{
  1076  			"create time set to now, modify time not set",
  1077  			&ACLAuthMethod{CreateTime: now},
  1078  		},
  1079  		{
  1080  			"both create time and modify time set",
  1081  			&ACLAuthMethod{CreateTime: now, ModifyTime: now.Add(time.Hour)},
  1082  		},
  1083  	}
  1084  	for _, tt := range tests {
  1085  		t.Run(tt.name, func(t *testing.T) {
  1086  			existing := tt.inputMethod.Copy()
  1087  			tt.inputMethod.Canonicalize()
  1088  			if existing.CreateTime.IsZero() {
  1089  				must.NotEq(t, time.Time{}, tt.inputMethod.CreateTime)
  1090  			} else {
  1091  				must.Eq(t, existing.CreateTime, tt.inputMethod.CreateTime)
  1092  			}
  1093  			if existing.ModifyTime.IsZero() {
  1094  				must.NotEq(t, time.Time{}, tt.inputMethod.ModifyTime)
  1095  			}
  1096  		})
  1097  	}
  1098  }
  1099  
  1100  func TestACLBindingRule_Canonicalize(t *testing.T) {
  1101  	ci.Parallel(t)
  1102  
  1103  	testCases := []struct {
  1104  		name                string
  1105  		inputACLBindingRule *ACLBindingRule
  1106  	}{
  1107  		{
  1108  			name:                "new binding rule",
  1109  			inputACLBindingRule: &ACLBindingRule{},
  1110  		},
  1111  		{
  1112  			name: "existing binding rule",
  1113  			inputACLBindingRule: &ACLBindingRule{
  1114  				ID:         "some-random-uuid",
  1115  				CreateTime: time.Now().UTC(),
  1116  				ModifyTime: time.Now().UTC(),
  1117  			},
  1118  		},
  1119  	}
  1120  
  1121  	for _, tc := range testCases {
  1122  		t.Run(tc.name, func(t *testing.T) {
  1123  
  1124  			// Make a copy, so we can compare the modified object to it.
  1125  			copiedBindingRule := tc.inputACLBindingRule.Copy()
  1126  
  1127  			tc.inputACLBindingRule.Canonicalize()
  1128  
  1129  			if copiedBindingRule.ID == "" {
  1130  				must.NotEq(t, "", tc.inputACLBindingRule.ID)
  1131  				must.NotEq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime)
  1132  				must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime)
  1133  			} else {
  1134  				must.Eq(t, copiedBindingRule.ID, tc.inputACLBindingRule.ID)
  1135  				must.Eq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime)
  1136  				must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime)
  1137  			}
  1138  		})
  1139  	}
  1140  }
  1141  
  1142  func TestACLBindingRule_Validate(t *testing.T) {
  1143  	ci.Parallel(t)
  1144  
  1145  	// Quite possibly the most invalid binding rule to have ever existed.
  1146  	totallyInvalidACLBindingRule := ACLBindingRule{
  1147  		Description: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() +
  1148  			uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(),
  1149  		AuthMethod: "",
  1150  		BindType:   "",
  1151  		BindName:   "",
  1152  	}
  1153  	err := totallyInvalidACLBindingRule.Validate()
  1154  	must.StrContains(t, err.Error(), "auth method is missing")
  1155  	must.StrContains(t, err.Error(), "bind name is missing")
  1156  	must.StrContains(t, err.Error(), "description longer than 256")
  1157  	must.StrContains(t, err.Error(), "bind type is missing")
  1158  
  1159  	// Update the bind type, so we get the alternative error when this is not
  1160  	// empty, but incorrectly set.
  1161  	totallyInvalidACLBindingRule.BindType = "service"
  1162  	err = totallyInvalidACLBindingRule.Validate()
  1163  	must.StrContains(t, err.Error(), `unsupported bind type: "service"`)
  1164  }
  1165  
  1166  func TestACLBindingRule_SetHash(t *testing.T) {
  1167  	ci.Parallel(t)
  1168  
  1169  	bindingRule := &ACLBindingRule{
  1170  		ID:         uuid.Generate(),
  1171  		AuthMethod: "okta",
  1172  	}
  1173  	out1 := bindingRule.SetHash()
  1174  	must.NotNil(t, out1)
  1175  	must.NotNil(t, bindingRule.Hash)
  1176  	must.Eq(t, out1, bindingRule.Hash)
  1177  
  1178  	bindingRule.Description = "my lovely rule"
  1179  	out2 := bindingRule.SetHash()
  1180  	must.NotNil(t, out2)
  1181  	must.NotNil(t, bindingRule.Hash)
  1182  	must.Eq(t, out2, bindingRule.Hash)
  1183  	must.NotEq(t, out1, out2)
  1184  }
  1185  
  1186  func TestACLBindingRule_Equal(t *testing.T) {
  1187  	ci.Parallel(t)
  1188  
  1189  	aclBindingRule1 := &ACLBindingRule{
  1190  		ID:          uuid.Short(),
  1191  		Description: "mocked-acl-binding-rule",
  1192  		AuthMethod:  "auth0",
  1193  		Selector:    "engineering in list.roles",
  1194  		BindType:    "role-id",
  1195  		BindName:    "eng-ro",
  1196  		CreateTime:  time.Now().UTC(),
  1197  		ModifyTime:  time.Now().UTC(),
  1198  		CreateIndex: 10,
  1199  		ModifyIndex: 10,
  1200  	}
  1201  	aclBindingRule1.SetHash()
  1202  
  1203  	// Create a second binding rule, and modify this from the first.
  1204  	aclBindingRule2 := aclBindingRule1.Copy()
  1205  	aclBindingRule2.Description = ""
  1206  	aclBindingRule2.SetHash()
  1207  
  1208  	testCases := []struct {
  1209  		name    string
  1210  		method1 *ACLBindingRule
  1211  		method2 *ACLBindingRule
  1212  		want    bool
  1213  	}{
  1214  		{"one nil", aclBindingRule1, &ACLBindingRule{}, false},
  1215  		{"both nil", &ACLBindingRule{}, &ACLBindingRule{}, true},
  1216  		{"not equal", aclBindingRule1, aclBindingRule2, false},
  1217  		{"equal", aclBindingRule2, aclBindingRule2.Copy(), true},
  1218  	}
  1219  	for _, tc := range testCases {
  1220  		t.Run(tc.name, func(t *testing.T) {
  1221  			got := tc.method1.Equal(tc.method2)
  1222  			must.Eq(t, got, tc.want)
  1223  		})
  1224  	}
  1225  }
  1226  
  1227  func TestACLBindingRule_Copy(t *testing.T) {
  1228  	ci.Parallel(t)
  1229  
  1230  	aclBindingRule1 := &ACLBindingRule{
  1231  		ID:          uuid.Short(),
  1232  		Description: "mocked-acl-binding-rule",
  1233  		AuthMethod:  "auth0",
  1234  		Selector:    "engineering in list.roles",
  1235  		BindType:    "role-id",
  1236  		BindName:    "eng-ro",
  1237  		CreateTime:  time.Now().UTC(),
  1238  		ModifyTime:  time.Now().UTC(),
  1239  		CreateIndex: 10,
  1240  		ModifyIndex: 10,
  1241  	}
  1242  	aclBindingRule1.SetHash()
  1243  
  1244  	aclBindingRule2 := aclBindingRule1.Copy()
  1245  	must.Eq(t, aclBindingRule1, aclBindingRule2)
  1246  
  1247  	aclBindingRule3 := aclBindingRule1.Copy()
  1248  	aclBindingRule3.Description = ""
  1249  	aclBindingRule3.SetHash()
  1250  	must.NotEq(t, aclBindingRule1, aclBindingRule3)
  1251  }
  1252  
  1253  func TestACLBindingRule_Stub(t *testing.T) {
  1254  	ci.Parallel(t)
  1255  
  1256  	aclBindingRule := ACLBindingRule{
  1257  		ID:          "some-uuid",
  1258  		Description: "my-binding-rule",
  1259  		AuthMethod:  "auth0",
  1260  		Selector:    "some selector.pattern",
  1261  		BindType:    "role",
  1262  		BindName:    "some-role-id-or-name",
  1263  		CreateTime:  time.Now().UTC(),
  1264  		ModifyTime:  time.Now().UTC(),
  1265  		CreateIndex: 1309,
  1266  		ModifyIndex: 9031,
  1267  	}
  1268  	aclBindingRule.SetHash()
  1269  
  1270  	must.Eq(t, &ACLBindingRuleListStub{
  1271  		ID:          "some-uuid",
  1272  		Description: "my-binding-rule",
  1273  		AuthMethod:  "auth0",
  1274  		Hash:        aclBindingRule.Hash,
  1275  		CreateIndex: 1309,
  1276  		ModifyIndex: 9031,
  1277  	}, aclBindingRule.Stub())
  1278  }
  1279  
  1280  func Test_ACLBindingRulesUpsertRequest(t *testing.T) {
  1281  	ci.Parallel(t)
  1282  
  1283  	req := ACLBindingRulesUpsertRequest{}
  1284  	require.False(t, req.IsRead())
  1285  }
  1286  
  1287  func Test_ACLBindingRulesDeleteRequest(t *testing.T) {
  1288  	ci.Parallel(t)
  1289  
  1290  	req := ACLBindingRulesDeleteRequest{}
  1291  	require.False(t, req.IsRead())
  1292  }
  1293  
  1294  func Test_ACLBindingRulesListRequest(t *testing.T) {
  1295  	ci.Parallel(t)
  1296  
  1297  	req := ACLBindingRulesListRequest{}
  1298  	require.True(t, req.IsRead())
  1299  }
  1300  
  1301  func Test_ACLBindingRulesRequest(t *testing.T) {
  1302  	ci.Parallel(t)
  1303  
  1304  	req := ACLBindingRulesRequest{}
  1305  	require.True(t, req.IsRead())
  1306  }
  1307  
  1308  func Test_ACLBindingRuleRequest(t *testing.T) {
  1309  	ci.Parallel(t)
  1310  
  1311  	req := ACLBindingRuleRequest{}
  1312  	require.True(t, req.IsRead())
  1313  }