...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_role_test.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package create
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	rbac "k8s.io/api/rbac/v1"
    25  	"k8s.io/apimachinery/pkg/api/equality"
    26  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/cli-runtime/pkg/genericclioptions"
    30  	"k8s.io/cli-runtime/pkg/genericiooptions"
    31  	"k8s.io/client-go/rest/fake"
    32  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    33  	"k8s.io/kubectl/pkg/scheme"
    34  )
    35  
    36  func TestCreateRole(t *testing.T) {
    37  	roleName := "my-role"
    38  	testNameSpace := "test"
    39  	tf := cmdtesting.NewTestFactory().WithNamespace(testNameSpace)
    40  	defer tf.Cleanup()
    41  
    42  	tf.Client = &fake.RESTClient{}
    43  	tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
    44  
    45  	tests := map[string]struct {
    46  		verbs         string
    47  		resources     string
    48  		resourceNames string
    49  		expectedRole  *rbac.Role
    50  	}{
    51  		"test-duplicate-resources": {
    52  			verbs:     "get,watch,list",
    53  			resources: "pods,pods",
    54  			expectedRole: &rbac.Role{
    55  				TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
    56  				ObjectMeta: v1.ObjectMeta{
    57  					Name:      roleName,
    58  					Namespace: testNameSpace,
    59  				},
    60  				Rules: []rbac.PolicyRule{
    61  					{
    62  						Verbs:         []string{"get", "watch", "list"},
    63  						Resources:     []string{"pods"},
    64  						APIGroups:     []string{""},
    65  						ResourceNames: []string{},
    66  					},
    67  				},
    68  			},
    69  		},
    70  		"test-subresources": {
    71  			verbs:     "get,watch,list",
    72  			resources: "replicasets/scale",
    73  			expectedRole: &rbac.Role{
    74  				TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
    75  				ObjectMeta: v1.ObjectMeta{
    76  					Name:      roleName,
    77  					Namespace: testNameSpace,
    78  				},
    79  				Rules: []rbac.PolicyRule{
    80  					{
    81  						Verbs:         []string{"get", "watch", "list"},
    82  						Resources:     []string{"replicasets/scale"},
    83  						APIGroups:     []string{"extensions"},
    84  						ResourceNames: []string{},
    85  					},
    86  				},
    87  			},
    88  		},
    89  		"test-subresources-with-apigroup": {
    90  			verbs:     "get,watch,list",
    91  			resources: "replicasets.extensions/scale",
    92  			expectedRole: &rbac.Role{
    93  				TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
    94  				ObjectMeta: v1.ObjectMeta{
    95  					Name:      roleName,
    96  					Namespace: testNameSpace,
    97  				},
    98  				Rules: []rbac.PolicyRule{
    99  					{
   100  						Verbs:         []string{"get", "watch", "list"},
   101  						Resources:     []string{"replicasets/scale"},
   102  						APIGroups:     []string{"extensions"},
   103  						ResourceNames: []string{},
   104  					},
   105  				},
   106  			},
   107  		},
   108  		"test-valid-case-with-multiple-apigroups": {
   109  			verbs:     "get,watch,list",
   110  			resources: "pods,deployments.extensions",
   111  			expectedRole: &rbac.Role{
   112  				TypeMeta: v1.TypeMeta{APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role"},
   113  				ObjectMeta: v1.ObjectMeta{
   114  					Name:      roleName,
   115  					Namespace: testNameSpace,
   116  				},
   117  				Rules: []rbac.PolicyRule{
   118  					{
   119  						Verbs:         []string{"get", "watch", "list"},
   120  						Resources:     []string{"pods"},
   121  						APIGroups:     []string{""},
   122  						ResourceNames: []string{},
   123  					},
   124  					{
   125  						Verbs:         []string{"get", "watch", "list"},
   126  						Resources:     []string{"deployments"},
   127  						APIGroups:     []string{"extensions"},
   128  						ResourceNames: []string{},
   129  					},
   130  				},
   131  			},
   132  		},
   133  	}
   134  
   135  	for name, test := range tests {
   136  		t.Run(name, func(t *testing.T) {
   137  			ioStreams, _, buf, _ := genericiooptions.NewTestIOStreams()
   138  			cmd := NewCmdCreateRole(tf, ioStreams)
   139  			cmd.Flags().Set("dry-run", "client")
   140  			cmd.Flags().Set("output", "yaml")
   141  			cmd.Flags().Set("verb", test.verbs)
   142  			cmd.Flags().Set("resource", test.resources)
   143  			if test.resourceNames != "" {
   144  				cmd.Flags().Set("resource-name", test.resourceNames)
   145  			}
   146  			cmd.Run(cmd, []string{roleName})
   147  			actual := &rbac.Role{}
   148  			if err := runtime.DecodeInto(scheme.Codecs.UniversalDecoder(), buf.Bytes(), actual); err != nil {
   149  				t.Log(buf.String())
   150  				t.Fatal(err)
   151  			}
   152  			if !equality.Semantic.DeepEqual(test.expectedRole, actual) {
   153  				t.Errorf("%s", cmp.Diff(test.expectedRole, actual))
   154  			}
   155  		})
   156  	}
   157  }
   158  
   159  func TestValidate(t *testing.T) {
   160  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
   161  	defer tf.Cleanup()
   162  
   163  	tests := map[string]struct {
   164  		roleOptions *CreateRoleOptions
   165  		expectErr   bool
   166  	}{
   167  		"test-missing-name": {
   168  			roleOptions: &CreateRoleOptions{},
   169  			expectErr:   true,
   170  		},
   171  		"test-missing-verb": {
   172  			roleOptions: &CreateRoleOptions{
   173  				Name: "my-role",
   174  			},
   175  			expectErr: true,
   176  		},
   177  		"test-missing-resource": {
   178  			roleOptions: &CreateRoleOptions{
   179  				Name:  "my-role",
   180  				Verbs: []string{"get"},
   181  			},
   182  			expectErr: true,
   183  		},
   184  		"test-missing-resource-existing-apigroup": {
   185  			roleOptions: &CreateRoleOptions{
   186  				Name:  "my-role",
   187  				Verbs: []string{"get"},
   188  				Resources: []ResourceOptions{
   189  					{
   190  						Group: "extensions",
   191  					},
   192  				},
   193  			},
   194  			expectErr: true,
   195  		},
   196  		"test-missing-resource-existing-subresource": {
   197  			roleOptions: &CreateRoleOptions{
   198  				Name:  "my-role",
   199  				Verbs: []string{"get"},
   200  				Resources: []ResourceOptions{
   201  					{
   202  						SubResource: "scale",
   203  					},
   204  				},
   205  			},
   206  			expectErr: true,
   207  		},
   208  		"test-invalid-verb": {
   209  			roleOptions: &CreateRoleOptions{
   210  				Name:  "my-role",
   211  				Verbs: []string{"invalid-verb"},
   212  				Resources: []ResourceOptions{
   213  					{
   214  						Resource: "pods",
   215  					},
   216  				},
   217  			},
   218  			expectErr: false,
   219  		},
   220  		"test-nonresource-verb": {
   221  			roleOptions: &CreateRoleOptions{
   222  				Name:  "my-role",
   223  				Verbs: []string{"post"},
   224  				Resources: []ResourceOptions{
   225  					{
   226  						Resource: "pods",
   227  					},
   228  				},
   229  			},
   230  			expectErr: false,
   231  		},
   232  		"test-special-verb": {
   233  			roleOptions: &CreateRoleOptions{
   234  				Name:  "my-role",
   235  				Verbs: []string{"use"},
   236  				Resources: []ResourceOptions{
   237  					{
   238  						Resource: "pods",
   239  					},
   240  				},
   241  			},
   242  			expectErr: true,
   243  		},
   244  		"test-mix-verbs": {
   245  			roleOptions: &CreateRoleOptions{
   246  				Name:  "my-role",
   247  				Verbs: []string{"impersonate", "use"},
   248  				Resources: []ResourceOptions{
   249  					{
   250  						Resource:    "userextras",
   251  						SubResource: "scopes",
   252  					},
   253  				},
   254  			},
   255  			expectErr: true,
   256  		},
   257  		"test-special-verb-with-wrong-apigroup": {
   258  			roleOptions: &CreateRoleOptions{
   259  				Name:  "my-role",
   260  				Verbs: []string{"impersonate"},
   261  				Resources: []ResourceOptions{
   262  					{
   263  						Resource:    "userextras",
   264  						SubResource: "scopes",
   265  						Group:       "extensions",
   266  					},
   267  				},
   268  			},
   269  			expectErr: true,
   270  		},
   271  		"test-invalid-resource": {
   272  			roleOptions: &CreateRoleOptions{
   273  				Name:  "my-role",
   274  				Verbs: []string{"get"},
   275  				Resources: []ResourceOptions{
   276  					{
   277  						Resource: "invalid-resource",
   278  					},
   279  				},
   280  			},
   281  			expectErr: true,
   282  		},
   283  		"test-resource-name-with-multiple-resources": {
   284  			roleOptions: &CreateRoleOptions{
   285  				Name:  "my-role",
   286  				Verbs: []string{"get"},
   287  				Resources: []ResourceOptions{
   288  					{
   289  						Resource: "pods",
   290  					},
   291  					{
   292  						Resource: "deployments",
   293  						Group:    "extensions",
   294  					},
   295  				},
   296  				ResourceNames: []string{"foo"},
   297  			},
   298  			expectErr: false,
   299  		},
   300  		"test-valid-case": {
   301  			roleOptions: &CreateRoleOptions{
   302  				Name:  "role-binder",
   303  				Verbs: []string{"get", "list", "bind"},
   304  				Resources: []ResourceOptions{
   305  					{
   306  						Resource: "roles",
   307  						Group:    "rbac.authorization.k8s.io",
   308  					},
   309  				},
   310  				ResourceNames: []string{"foo"},
   311  			},
   312  			expectErr: false,
   313  		},
   314  		"test-valid-case-with-subresource": {
   315  			roleOptions: &CreateRoleOptions{
   316  				Name:  "my-role",
   317  				Verbs: []string{"get", "list"},
   318  				Resources: []ResourceOptions{
   319  					{
   320  						Resource:    "replicasets",
   321  						SubResource: "scale",
   322  					},
   323  				},
   324  				ResourceNames: []string{"bar"},
   325  			},
   326  			expectErr: false,
   327  		},
   328  		"test-valid-case-with-additional-resource": {
   329  			roleOptions: &CreateRoleOptions{
   330  				Name:  "my-role",
   331  				Verbs: []string{"impersonate"},
   332  				Resources: []ResourceOptions{
   333  					{
   334  						Resource:    "userextras",
   335  						SubResource: "scopes",
   336  						Group:       "authentication.k8s.io",
   337  					},
   338  				},
   339  			},
   340  			expectErr: false,
   341  		},
   342  	}
   343  
   344  	for name, test := range tests {
   345  		test.roleOptions.IOStreams = genericiooptions.NewTestIOStreamsDiscard()
   346  
   347  		var err error
   348  		test.roleOptions.Mapper, err = tf.ToRESTMapper()
   349  		if err != nil {
   350  			t.Fatal(err)
   351  		}
   352  		err = test.roleOptions.Validate()
   353  		if test.expectErr && err == nil {
   354  			t.Errorf("%s: expect error happens but validate passes.", name)
   355  		}
   356  		if !test.expectErr && err != nil {
   357  			t.Errorf("%s: unexpected error: %v", name, err)
   358  		}
   359  	}
   360  }
   361  
   362  func TestComplete(t *testing.T) {
   363  	roleName := "my-role"
   364  
   365  	tf := cmdtesting.NewTestFactory().WithNamespace("test")
   366  	defer tf.Cleanup()
   367  
   368  	tf.Client = &fake.RESTClient{}
   369  	tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
   370  
   371  	defaultTestResources := "pods,deployments.extensions"
   372  
   373  	tests := map[string]struct {
   374  		params      []string
   375  		resources   string
   376  		roleOptions *CreateRoleOptions
   377  		expected    *CreateRoleOptions
   378  		expectErr   bool
   379  	}{
   380  		"test-missing-name": {
   381  			params:    []string{},
   382  			resources: defaultTestResources,
   383  			roleOptions: &CreateRoleOptions{
   384  				PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
   385  			},
   386  			expectErr: true,
   387  		},
   388  		"test-duplicate-verbs": {
   389  			params:    []string{roleName},
   390  			resources: defaultTestResources,
   391  			roleOptions: &CreateRoleOptions{
   392  				PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
   393  				Name:       roleName,
   394  				Verbs: []string{
   395  					"get",
   396  					"watch",
   397  					"list",
   398  					"get",
   399  				},
   400  			},
   401  			expected: &CreateRoleOptions{
   402  				Name: roleName,
   403  				Verbs: []string{
   404  					"get",
   405  					"watch",
   406  					"list",
   407  				},
   408  				Resources: []ResourceOptions{
   409  					{
   410  						Resource: "pods",
   411  						Group:    "",
   412  					},
   413  					{
   414  						Resource: "deployments",
   415  						Group:    "extensions",
   416  					},
   417  				},
   418  				ResourceNames: []string{},
   419  			},
   420  			expectErr: false,
   421  		},
   422  		"test-verball": {
   423  			params:    []string{roleName},
   424  			resources: defaultTestResources,
   425  			roleOptions: &CreateRoleOptions{
   426  				PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
   427  				Name:       roleName,
   428  				Verbs: []string{
   429  					"get",
   430  					"watch",
   431  					"list",
   432  					"*",
   433  				},
   434  			},
   435  			expected: &CreateRoleOptions{
   436  				Name:  roleName,
   437  				Verbs: []string{"*"},
   438  				Resources: []ResourceOptions{
   439  					{
   440  						Resource: "pods",
   441  						Group:    "",
   442  					},
   443  					{
   444  						Resource: "deployments",
   445  						Group:    "extensions",
   446  					},
   447  				},
   448  				ResourceNames: []string{},
   449  			},
   450  			expectErr: false,
   451  		},
   452  		"test-allresource": {
   453  			params:    []string{roleName},
   454  			resources: "*,pods",
   455  			roleOptions: &CreateRoleOptions{
   456  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   457  				Name:       roleName,
   458  				Verbs:      []string{"*"},
   459  			},
   460  			expected: &CreateRoleOptions{
   461  				Name:  roleName,
   462  				Verbs: []string{"*"},
   463  				Resources: []ResourceOptions{
   464  					{
   465  						Resource: "*",
   466  					},
   467  				},
   468  				ResourceNames: []string{},
   469  			},
   470  			expectErr: false,
   471  		},
   472  		"test-allresource-subresource": {
   473  			params:    []string{roleName},
   474  			resources: "*/scale,pods",
   475  			roleOptions: &CreateRoleOptions{
   476  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   477  				Name:       roleName,
   478  				Verbs:      []string{"*"},
   479  			},
   480  			expected: &CreateRoleOptions{
   481  				Name:  roleName,
   482  				Verbs: []string{"*"},
   483  				Resources: []ResourceOptions{
   484  					{
   485  						Resource:    "*",
   486  						SubResource: "scale",
   487  					},
   488  					{
   489  						Resource: "pods",
   490  					},
   491  				},
   492  				ResourceNames: []string{},
   493  			},
   494  			expectErr: false,
   495  		},
   496  		"test-allresrouce-allgroup": {
   497  			params:    []string{roleName},
   498  			resources: "*.*,pods",
   499  			roleOptions: &CreateRoleOptions{
   500  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   501  				Name:       roleName,
   502  				Verbs:      []string{"*"},
   503  			},
   504  			expected: &CreateRoleOptions{
   505  				Name:  roleName,
   506  				Verbs: []string{"*"},
   507  				Resources: []ResourceOptions{
   508  					{
   509  						Resource: "*",
   510  						Group:    "*",
   511  					},
   512  					{
   513  						Resource: "pods",
   514  					},
   515  				},
   516  				ResourceNames: []string{},
   517  			},
   518  			expectErr: false,
   519  		},
   520  		"test-allresource-allgroup-subresource": {
   521  			params:    []string{roleName},
   522  			resources: "*.*/scale,pods",
   523  			roleOptions: &CreateRoleOptions{
   524  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   525  				Name:       roleName,
   526  				Verbs:      []string{"*"},
   527  			},
   528  			expected: &CreateRoleOptions{
   529  				Name:  roleName,
   530  				Verbs: []string{"*"},
   531  				Resources: []ResourceOptions{
   532  					{
   533  						Resource:    "*",
   534  						Group:       "*",
   535  						SubResource: "scale",
   536  					},
   537  					{
   538  						Resource: "pods",
   539  					},
   540  				},
   541  				ResourceNames: []string{},
   542  			},
   543  			expectErr: false,
   544  		},
   545  		"test-allresource-specificgroup": {
   546  			params:    []string{roleName},
   547  			resources: "*.extensions,pods",
   548  			roleOptions: &CreateRoleOptions{
   549  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   550  				Name:       roleName,
   551  				Verbs:      []string{"*"},
   552  			},
   553  			expected: &CreateRoleOptions{
   554  				Name:  roleName,
   555  				Verbs: []string{"*"},
   556  				Resources: []ResourceOptions{
   557  					{
   558  						Resource: "*",
   559  						Group:    "extensions",
   560  					},
   561  					{
   562  						Resource: "pods",
   563  					},
   564  				},
   565  				ResourceNames: []string{},
   566  			},
   567  			expectErr: false,
   568  		},
   569  		"test-allresource-specificgroup-subresource": {
   570  			params:    []string{roleName},
   571  			resources: "*.extensions/scale,pods",
   572  			roleOptions: &CreateRoleOptions{
   573  				PrintFlags: genericclioptions.NewPrintFlags("created"),
   574  				Name:       roleName,
   575  				Verbs:      []string{"*"},
   576  			},
   577  			expected: &CreateRoleOptions{
   578  				Name:  roleName,
   579  				Verbs: []string{"*"},
   580  				Resources: []ResourceOptions{
   581  					{
   582  						Resource:    "*",
   583  						Group:       "extensions",
   584  						SubResource: "scale",
   585  					},
   586  					{
   587  						Resource: "pods",
   588  					},
   589  				},
   590  				ResourceNames: []string{},
   591  			},
   592  			expectErr: false,
   593  		},
   594  		"test-duplicate-resourcenames": {
   595  			params:    []string{roleName},
   596  			resources: defaultTestResources,
   597  			roleOptions: &CreateRoleOptions{
   598  				PrintFlags:    genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
   599  				Name:          roleName,
   600  				Verbs:         []string{"*"},
   601  				ResourceNames: []string{"foo", "foo"},
   602  			},
   603  			expected: &CreateRoleOptions{
   604  				Name:  roleName,
   605  				Verbs: []string{"*"},
   606  				Resources: []ResourceOptions{
   607  					{
   608  						Resource: "pods",
   609  						Group:    "",
   610  					},
   611  					{
   612  						Resource: "deployments",
   613  						Group:    "extensions",
   614  					},
   615  				},
   616  				ResourceNames: []string{"foo"},
   617  			},
   618  			expectErr: false,
   619  		},
   620  		"test-valid-complete-case": {
   621  			params:    []string{roleName},
   622  			resources: defaultTestResources,
   623  			roleOptions: &CreateRoleOptions{
   624  				PrintFlags:    genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
   625  				Name:          roleName,
   626  				Verbs:         []string{"*"},
   627  				ResourceNames: []string{"foo"},
   628  			},
   629  			expected: &CreateRoleOptions{
   630  				Name:  roleName,
   631  				Verbs: []string{"*"},
   632  				Resources: []ResourceOptions{
   633  					{
   634  						Resource: "pods",
   635  						Group:    "",
   636  					},
   637  					{
   638  						Resource: "deployments",
   639  						Group:    "extensions",
   640  					},
   641  				},
   642  				ResourceNames: []string{"foo"},
   643  			},
   644  			expectErr: false,
   645  		},
   646  	}
   647  
   648  	for name, test := range tests {
   649  		cmd := NewCmdCreateRole(tf, genericiooptions.NewTestIOStreamsDiscard())
   650  		cmd.Flags().Set("resource", test.resources)
   651  
   652  		err := test.roleOptions.Complete(tf, cmd, test.params)
   653  		if !test.expectErr && err != nil {
   654  			t.Errorf("%s: unexpected error: %v", name, err)
   655  		}
   656  
   657  		if test.expectErr {
   658  			if err != nil {
   659  				continue
   660  			} else {
   661  				t.Errorf("%s: expect error happens but test passes.", name)
   662  			}
   663  		}
   664  
   665  		if test.roleOptions.Name != test.expected.Name {
   666  			t.Errorf("%s:\nexpected name:\n%#v\nsaw name:\n%#v", name, test.expected.Name, test.roleOptions.Name)
   667  		}
   668  
   669  		if !reflect.DeepEqual(test.roleOptions.Verbs, test.expected.Verbs) {
   670  			t.Errorf("%s:\nexpected verbs:\n%#v\nsaw verbs:\n%#v", name, test.expected.Verbs, test.roleOptions.Verbs)
   671  		}
   672  
   673  		if !reflect.DeepEqual(test.roleOptions.Resources, test.expected.Resources) {
   674  			t.Errorf("%s:\nexpected resources:\n%#v\nsaw resources:\n%#v", name, test.expected.Resources, test.roleOptions.Resources)
   675  		}
   676  
   677  		if !reflect.DeepEqual(test.roleOptions.ResourceNames, test.expected.ResourceNames) {
   678  			t.Errorf("%s:\nexpected resource names:\n%#v\nsaw resource names:\n%#v", name, test.expected.ResourceNames, test.roleOptions.ResourceNames)
   679  		}
   680  	}
   681  }
   682  
   683  func TestAddSpecialVerb(t *testing.T) {
   684  	testCases := map[string]struct {
   685  		verb     string
   686  		resource schema.GroupResource
   687  	}{
   688  		"existing verb": {
   689  			verb:     "use",
   690  			resource: schema.GroupResource{Group: "my.custom.io", Resource: "one"},
   691  		},
   692  		"new verb": {
   693  			verb:     "new",
   694  			resource: schema.GroupResource{Group: "my.custom.io", Resource: "two"},
   695  		},
   696  	}
   697  
   698  	for name, tc := range testCases {
   699  		t.Run(name, func(t *testing.T) {
   700  			AddSpecialVerb(tc.verb, tc.resource)
   701  			resources, ok := specialVerbs[tc.verb]
   702  			if !ok {
   703  				t.Errorf("missing expected verb: %s", tc.verb)
   704  			}
   705  
   706  			for _, res := range resources {
   707  				if reflect.DeepEqual(tc.resource, res) {
   708  					return
   709  				}
   710  			}
   711  			t.Errorf("missing expected resource:%#v", tc.resource)
   712  		})
   713  	}
   714  }
   715  

View as plain text