...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/crdutil/objectorarray_test.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/crdutil

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package crdutil_test
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  
    21  	"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/util/crdutil"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    25  )
    26  
    27  func TestGetSchemaForFieldUnderObjectOrArray(t *testing.T) {
    28  	t.Parallel()
    29  	tests := []struct {
    30  		name           string
    31  		field          string
    32  		parent         *apiextensions.JSONSchemaProps
    33  		expectedSchema *apiextensions.JSONSchemaProps
    34  		hasError       bool
    35  	}{
    36  		{
    37  			name:  "get schema for field under object",
    38  			field: "test",
    39  			parent: &apiextensions.JSONSchemaProps{
    40  				Properties: map[string]apiextensions.JSONSchemaProps{
    41  					"test": {
    42  						Description: "field under object",
    43  						Type:        "string",
    44  					},
    45  				},
    46  				Type: "object",
    47  			},
    48  			expectedSchema: &apiextensions.JSONSchemaProps{
    49  				Description: "field under object",
    50  				Type:        "string",
    51  			},
    52  		},
    53  		{
    54  			name:  "get schema for field under map",
    55  			field: "test",
    56  			parent: &apiextensions.JSONSchemaProps{
    57  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
    58  					Schema: &apiextensions.JSONSchemaProps{
    59  						Properties: map[string]apiextensions.JSONSchemaProps{
    60  							"test": {
    61  								Description: "field under map",
    62  								Type:        "string",
    63  							},
    64  						},
    65  						Type: "object",
    66  					},
    67  				},
    68  				Type: "object",
    69  			},
    70  			expectedSchema: &apiextensions.JSONSchemaProps{
    71  				Description: "field under map",
    72  				Type:        "string",
    73  			},
    74  		},
    75  		{
    76  			name:  "get schema for field under array",
    77  			field: "test",
    78  			parent: &apiextensions.JSONSchemaProps{
    79  				Items: &apiextensions.JSONSchemaPropsOrArray{
    80  					Schema: &apiextensions.JSONSchemaProps{
    81  						Properties: map[string]apiextensions.JSONSchemaProps{
    82  							"test": {
    83  								Description: "field under array",
    84  								Type:        "string",
    85  							},
    86  						},
    87  						Type: "object",
    88  					},
    89  				},
    90  				Type: "array",
    91  			},
    92  			expectedSchema: &apiextensions.JSONSchemaProps{
    93  				Description: "field under array",
    94  				Type:        "string",
    95  			},
    96  		},
    97  		{
    98  			name:  "get empty schema when field doesn't exist",
    99  			field: "test",
   100  			parent: &apiextensions.JSONSchemaProps{
   101  				Properties: map[string]apiextensions.JSONSchemaProps{
   102  					"otherField": {
   103  						Type: "string",
   104  					},
   105  				},
   106  				Type: "object",
   107  			},
   108  			expectedSchema: nil,
   109  		},
   110  		{
   111  			name:  "can't get schema for field under string field",
   112  			field: "test",
   113  			parent: &apiextensions.JSONSchemaProps{
   114  				Type: "string",
   115  			},
   116  			hasError: true,
   117  		},
   118  		{
   119  			name:  "can't get schema for incorrect object schema",
   120  			field: "test",
   121  			parent: &apiextensions.JSONSchemaProps{
   122  				Description: "an object field without properties or additionalProperties",
   123  				Type:        "object",
   124  			},
   125  			hasError: true,
   126  		},
   127  	}
   128  
   129  	for _, tc := range tests {
   130  		t.Run(tc.name, func(t *testing.T) {
   131  			schema, _, err := crdutil.GetSchemaForFieldUnderObjectOrArray(tc.field, tc.parent)
   132  			if err != nil {
   133  				if !tc.hasError {
   134  					t.Fatalf("got an error, but want no error: %v", err)
   135  				}
   136  				return
   137  			}
   138  			if got, want := schema, tc.expectedSchema; !reflect.DeepEqual(got, want) {
   139  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   140  			}
   141  		})
   142  	}
   143  }
   144  
   145  func TestSetSchemaForFieldUnderObjectOrArray(t *testing.T) {
   146  	t.Parallel()
   147  	tests := []struct {
   148  		name           string
   149  		field          string
   150  		parent         *apiextensions.JSONSchemaProps
   151  		fieldSchema    *apiextensions.JSONSchemaProps
   152  		expectedSchema *apiextensions.JSONSchemaProps
   153  		hasError       bool
   154  	}{
   155  		{
   156  			name:  "set schema for non-existent field under object",
   157  			field: "test",
   158  			parent: &apiextensions.JSONSchemaProps{
   159  				Properties: map[string]apiextensions.JSONSchemaProps{
   160  					"otherField": {
   161  						Type: "bool",
   162  					},
   163  				},
   164  				Type: "object",
   165  			},
   166  			fieldSchema: &apiextensions.JSONSchemaProps{
   167  				Description: "field under object",
   168  				Type:        "string",
   169  			},
   170  			expectedSchema: &apiextensions.JSONSchemaProps{
   171  				Properties: map[string]apiextensions.JSONSchemaProps{
   172  					"otherField": {
   173  						Type: "bool",
   174  					},
   175  					"test": {
   176  						Description: "field under object",
   177  						Type:        "string",
   178  					},
   179  				},
   180  				Type: "object",
   181  			},
   182  		},
   183  		{
   184  			name:  "set schema for non-existent field under map",
   185  			field: "test",
   186  			parent: &apiextensions.JSONSchemaProps{
   187  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   188  					Schema: &apiextensions.JSONSchemaProps{
   189  						Properties: map[string]apiextensions.JSONSchemaProps{
   190  							"otherField": {
   191  								Type: "bool",
   192  							},
   193  						},
   194  						Type: "object",
   195  					},
   196  				},
   197  				Type: "object",
   198  			},
   199  			fieldSchema: &apiextensions.JSONSchemaProps{
   200  				Description: "field under map",
   201  				Type:        "string",
   202  			},
   203  			expectedSchema: &apiextensions.JSONSchemaProps{
   204  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   205  					Schema: &apiextensions.JSONSchemaProps{
   206  						Properties: map[string]apiextensions.JSONSchemaProps{
   207  							"test": {
   208  								Description: "field under map",
   209  								Type:        "string",
   210  							},
   211  							"otherField": {
   212  								Type: "bool",
   213  							},
   214  						},
   215  						Type: "object",
   216  					},
   217  				},
   218  				Type: "object",
   219  			},
   220  		},
   221  		{
   222  			name:  "set schema for non-existent field under array",
   223  			field: "test",
   224  			parent: &apiextensions.JSONSchemaProps{
   225  				Items: &apiextensions.JSONSchemaPropsOrArray{
   226  					Schema: &apiextensions.JSONSchemaProps{
   227  						Properties: map[string]apiextensions.JSONSchemaProps{
   228  							"otherField": {
   229  								Type: "bool",
   230  							},
   231  						},
   232  						Type: "object",
   233  					},
   234  				},
   235  				Type: "array",
   236  			},
   237  			fieldSchema: &apiextensions.JSONSchemaProps{
   238  				Description: "field under array",
   239  				Type:        "string",
   240  			},
   241  			expectedSchema: &apiextensions.JSONSchemaProps{
   242  				Items: &apiextensions.JSONSchemaPropsOrArray{
   243  					Schema: &apiextensions.JSONSchemaProps{
   244  						Properties: map[string]apiextensions.JSONSchemaProps{
   245  							"test": {
   246  								Description: "field under array",
   247  								Type:        "string",
   248  							},
   249  							"otherField": {
   250  								Type: "bool",
   251  							},
   252  						},
   253  						Type: "object",
   254  					},
   255  				},
   256  				Type: "array",
   257  			},
   258  		},
   259  		{
   260  			name:  "set schema for existing field under object",
   261  			field: "test",
   262  			parent: &apiextensions.JSONSchemaProps{
   263  				Properties: map[string]apiextensions.JSONSchemaProps{
   264  					"otherField": {
   265  						Type: "bool",
   266  					},
   267  					"test": {
   268  						Description: "old description",
   269  						Type:        "bool",
   270  					},
   271  				},
   272  				Type: "object",
   273  			},
   274  			fieldSchema: &apiextensions.JSONSchemaProps{
   275  				Description: "field under object",
   276  				Type:        "string",
   277  			},
   278  			expectedSchema: &apiextensions.JSONSchemaProps{
   279  				Properties: map[string]apiextensions.JSONSchemaProps{
   280  					"otherField": {
   281  						Type: "bool",
   282  					},
   283  					"test": {
   284  						Description: "field under object",
   285  						Type:        "string",
   286  					},
   287  				},
   288  				Type: "object",
   289  			},
   290  		},
   291  		{
   292  			name:  "can't set schema under a string field",
   293  			field: "test",
   294  			parent: &apiextensions.JSONSchemaProps{
   295  				Type: "string",
   296  			},
   297  			fieldSchema: &apiextensions.JSONSchemaProps{
   298  				Description: "test schema",
   299  				Type:        "string",
   300  			},
   301  			hasError: true,
   302  		},
   303  		{
   304  			name:  "can't set schema for incorrect object schema",
   305  			field: "test",
   306  			parent: &apiextensions.JSONSchemaProps{
   307  				Type: "object",
   308  			},
   309  			fieldSchema: &apiextensions.JSONSchemaProps{
   310  				Description: "test schema",
   311  				Type:        "string",
   312  			},
   313  			hasError: true,
   314  		},
   315  	}
   316  
   317  	for _, tc := range tests {
   318  		t.Run(tc.name, func(t *testing.T) {
   319  			if err := crdutil.SetSchemaForFieldUnderObjectOrArray(tc.field, tc.parent, tc.fieldSchema); err != nil {
   320  				if !tc.hasError {
   321  					t.Fatalf("got an error, but want no error: %v", err)
   322  				}
   323  				return
   324  			}
   325  			if got, want := tc.parent, tc.expectedSchema; !reflect.DeepEqual(got, want) {
   326  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   327  			}
   328  		})
   329  	}
   330  }
   331  
   332  func TestGetRequiredRuleForObjectOrArray(t *testing.T) {
   333  	t.Parallel()
   334  	tests := []struct {
   335  		name         string
   336  		schema       *apiextensions.JSONSchemaProps
   337  		expectedRule []string
   338  		hasError     bool
   339  	}{
   340  		{
   341  			name: "get required rule under object",
   342  			schema: &apiextensions.JSONSchemaProps{
   343  				Properties: map[string]apiextensions.JSONSchemaProps{
   344  					"test": {
   345  						Description: "field under object",
   346  						Type:        "string",
   347  					},
   348  				},
   349  				Type:     "object",
   350  				Required: []string{"requiredField"},
   351  			},
   352  			expectedRule: []string{"requiredField"},
   353  		},
   354  		{
   355  			name: "get required rule under map",
   356  			schema: &apiextensions.JSONSchemaProps{
   357  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   358  					Schema: &apiextensions.JSONSchemaProps{
   359  						Properties: map[string]apiextensions.JSONSchemaProps{
   360  							"test": {
   361  								Description: "field under map",
   362  								Type:        "string",
   363  							},
   364  						},
   365  						Type:     "object",
   366  						Required: []string{"requiredField"},
   367  					},
   368  				},
   369  				Type: "object",
   370  			},
   371  			expectedRule: []string{"requiredField"},
   372  		},
   373  		{
   374  			name: "get required rule under array",
   375  			schema: &apiextensions.JSONSchemaProps{
   376  				Items: &apiextensions.JSONSchemaPropsOrArray{
   377  					Schema: &apiextensions.JSONSchemaProps{
   378  						Properties: map[string]apiextensions.JSONSchemaProps{
   379  							"test": {
   380  								Description: "field under array",
   381  								Type:        "string",
   382  							},
   383  						},
   384  						Type:     "object",
   385  						Required: []string{"requiredField"},
   386  					},
   387  				},
   388  				Type: "array",
   389  			},
   390  			expectedRule: []string{"requiredField"},
   391  		},
   392  		{
   393  			name: "can't get rule for field under string field",
   394  			schema: &apiextensions.JSONSchemaProps{
   395  				Type:     "string",
   396  				Required: []string{"nonretrievable"},
   397  			},
   398  			hasError: true,
   399  		},
   400  		{
   401  			name: "can't get rule for incorrect object schema",
   402  			schema: &apiextensions.JSONSchemaProps{
   403  				Description: "an object field without properties or additionalProperties",
   404  				Type:        "object",
   405  				Required:    []string{"nonretrievable"},
   406  			},
   407  			hasError: true,
   408  		},
   409  	}
   410  
   411  	for _, tc := range tests {
   412  		t.Run(tc.name, func(t *testing.T) {
   413  			rule, err := crdutil.GetRequiredRuleForObjectOrArray(tc.schema)
   414  			if err != nil {
   415  				if !tc.hasError {
   416  					t.Fatalf("got an error, but want no error: %v", err)
   417  				}
   418  				return
   419  			}
   420  			if got, want := rule, tc.expectedRule; !reflect.DeepEqual(got, want) {
   421  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   422  			}
   423  		})
   424  	}
   425  }
   426  
   427  func TestSetRequiredRuleForObjectOrArray(t *testing.T) {
   428  	t.Parallel()
   429  	tests := []struct {
   430  		name           string
   431  		schema         *apiextensions.JSONSchemaProps
   432  		rule           []string
   433  		expectedSchema *apiextensions.JSONSchemaProps
   434  		hasError       bool
   435  	}{
   436  		{
   437  			name: "set required rule under object",
   438  			schema: &apiextensions.JSONSchemaProps{
   439  				Properties: map[string]apiextensions.JSONSchemaProps{
   440  					"otherField": {
   441  						Type: "bool",
   442  					},
   443  				},
   444  				Type: "object",
   445  			},
   446  			rule: []string{"requiredField"},
   447  			expectedSchema: &apiextensions.JSONSchemaProps{
   448  				Properties: map[string]apiextensions.JSONSchemaProps{
   449  					"otherField": {
   450  						Type: "bool",
   451  					},
   452  				},
   453  				Type:     "object",
   454  				Required: []string{"requiredField"},
   455  			},
   456  		},
   457  		{
   458  			name: "set required rule under map",
   459  			schema: &apiextensions.JSONSchemaProps{
   460  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   461  					Schema: &apiextensions.JSONSchemaProps{
   462  						Properties: map[string]apiextensions.JSONSchemaProps{
   463  							"otherField": {
   464  								Type: "bool",
   465  							},
   466  						},
   467  						Type: "object",
   468  					},
   469  				},
   470  				Type: "object",
   471  			},
   472  			rule: []string{"requiredField"},
   473  			expectedSchema: &apiextensions.JSONSchemaProps{
   474  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   475  					Schema: &apiextensions.JSONSchemaProps{
   476  						Properties: map[string]apiextensions.JSONSchemaProps{
   477  							"otherField": {
   478  								Type: "bool",
   479  							},
   480  						},
   481  						Type:     "object",
   482  						Required: []string{"requiredField"},
   483  					},
   484  				},
   485  				Type: "object",
   486  			},
   487  		},
   488  		{
   489  			name: "set required rule under array",
   490  			schema: &apiextensions.JSONSchemaProps{
   491  				Items: &apiextensions.JSONSchemaPropsOrArray{
   492  					Schema: &apiextensions.JSONSchemaProps{
   493  						Properties: map[string]apiextensions.JSONSchemaProps{
   494  							"otherField": {
   495  								Type: "bool",
   496  							},
   497  						},
   498  						Type: "object",
   499  					},
   500  				},
   501  				Type: "array",
   502  			},
   503  			rule: []string{"requiredField"},
   504  			expectedSchema: &apiextensions.JSONSchemaProps{
   505  				Items: &apiextensions.JSONSchemaPropsOrArray{
   506  					Schema: &apiextensions.JSONSchemaProps{
   507  						Properties: map[string]apiextensions.JSONSchemaProps{
   508  							"otherField": {
   509  								Type: "bool",
   510  							},
   511  						},
   512  						Type:     "object",
   513  						Required: []string{"requiredField"},
   514  					},
   515  				},
   516  				Type: "array",
   517  			},
   518  		},
   519  		{
   520  			name: "can't set rulee under a string field",
   521  			schema: &apiextensions.JSONSchemaProps{
   522  				Type: "string",
   523  			},
   524  			rule:     []string{"unsettable"},
   525  			hasError: true,
   526  		},
   527  		{
   528  			name: "can't set rule for incorrect object schema",
   529  			schema: &apiextensions.JSONSchemaProps{
   530  				Type: "object",
   531  			},
   532  			rule:     []string{"unsettable"},
   533  			hasError: true,
   534  		},
   535  	}
   536  
   537  	for _, tc := range tests {
   538  		t.Run(tc.name, func(t *testing.T) {
   539  			if err := crdutil.SetRequiredRuleForObjectOrArray(tc.schema, tc.rule); err != nil {
   540  				if !tc.hasError {
   541  					t.Fatalf("got an error, but want no error: %v", err)
   542  				}
   543  				return
   544  			}
   545  			if got, want := tc.schema, tc.expectedSchema; !reflect.DeepEqual(got, want) {
   546  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   547  			}
   548  		})
   549  	}
   550  }
   551  
   552  func TestGetNotRuleForObjectOrArray(t *testing.T) {
   553  	t.Parallel()
   554  	tests := []struct {
   555  		name         string
   556  		schema       *apiextensions.JSONSchemaProps
   557  		expectedRule *apiextensions.JSONSchemaProps
   558  		hasError     bool
   559  	}{
   560  		{
   561  			name: "get not rule under object",
   562  			schema: &apiextensions.JSONSchemaProps{
   563  				Properties: map[string]apiextensions.JSONSchemaProps{
   564  					"test": {
   565  						Description: "field under object",
   566  						Type:        "string",
   567  					},
   568  				},
   569  				Type: "object",
   570  				Not: &apiextensions.JSONSchemaProps{
   571  					Required: []string{"requiredField"},
   572  				},
   573  			},
   574  			expectedRule: &apiextensions.JSONSchemaProps{
   575  				Required: []string{"requiredField"},
   576  			},
   577  		},
   578  		{
   579  			name: "get not rule under map",
   580  			schema: &apiextensions.JSONSchemaProps{
   581  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   582  					Schema: &apiextensions.JSONSchemaProps{
   583  						Properties: map[string]apiextensions.JSONSchemaProps{
   584  							"test": {
   585  								Description: "field under map",
   586  								Type:        "string",
   587  							},
   588  						},
   589  						Type: "object",
   590  						Not: &apiextensions.JSONSchemaProps{
   591  							Required: []string{"requiredField"},
   592  						},
   593  					},
   594  				},
   595  				Type: "object",
   596  			},
   597  			expectedRule: &apiextensions.JSONSchemaProps{
   598  				Required: []string{"requiredField"},
   599  			},
   600  		},
   601  		{
   602  			name: "get not rule under array",
   603  			schema: &apiextensions.JSONSchemaProps{
   604  				Items: &apiextensions.JSONSchemaPropsOrArray{
   605  					Schema: &apiextensions.JSONSchemaProps{
   606  						Properties: map[string]apiextensions.JSONSchemaProps{
   607  							"test": {
   608  								Description: "field under array",
   609  								Type:        "string",
   610  							},
   611  						},
   612  						Type: "object",
   613  						Not: &apiextensions.JSONSchemaProps{
   614  							Required: []string{"requiredField"},
   615  						},
   616  					},
   617  				},
   618  				Type: "array",
   619  			},
   620  			expectedRule: &apiextensions.JSONSchemaProps{
   621  				Required: []string{"requiredField"},
   622  			},
   623  		},
   624  		{
   625  			name: "can't get rule for field under string field",
   626  			schema: &apiextensions.JSONSchemaProps{
   627  				Type: "string",
   628  				Not: &apiextensions.JSONSchemaProps{
   629  					Required: []string{"nonretrievable"},
   630  				},
   631  			},
   632  			hasError: true,
   633  		},
   634  		{
   635  			name: "can't get rule for incorrect object schema",
   636  			schema: &apiextensions.JSONSchemaProps{
   637  				Description: "an object field without properties or additionalProperties",
   638  				Type:        "object",
   639  				Not: &apiextensions.JSONSchemaProps{
   640  					Required: []string{"nonretrievable"},
   641  				},
   642  			},
   643  			hasError: true,
   644  		},
   645  	}
   646  
   647  	for _, tc := range tests {
   648  		t.Run(tc.name, func(t *testing.T) {
   649  			rule, err := crdutil.GetNotRuleForObjectOrArray(tc.schema)
   650  			if err != nil {
   651  				if !tc.hasError {
   652  					t.Fatalf("got an error, but want no error: %v", err)
   653  				}
   654  				return
   655  			}
   656  			if got, want := rule, tc.expectedRule; !reflect.DeepEqual(got, want) {
   657  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   658  			}
   659  		})
   660  	}
   661  }
   662  
   663  func TestSetNotRuleForObjectOrArray(t *testing.T) {
   664  	t.Parallel()
   665  	tests := []struct {
   666  		name           string
   667  		schema         *apiextensions.JSONSchemaProps
   668  		rule           *apiextensions.JSONSchemaProps
   669  		expectedSchema *apiextensions.JSONSchemaProps
   670  		hasError       bool
   671  	}{
   672  		{
   673  			name: "set required rule under object",
   674  			schema: &apiextensions.JSONSchemaProps{
   675  				Properties: map[string]apiextensions.JSONSchemaProps{
   676  					"otherField": {
   677  						Type: "bool",
   678  					},
   679  				},
   680  				Type: "object",
   681  			},
   682  			rule: &apiextensions.JSONSchemaProps{
   683  				Required: []string{"requiredField"},
   684  			},
   685  			expectedSchema: &apiextensions.JSONSchemaProps{
   686  				Properties: map[string]apiextensions.JSONSchemaProps{
   687  					"otherField": {
   688  						Type: "bool",
   689  					},
   690  				},
   691  				Type: "object",
   692  				Not: &apiextensions.JSONSchemaProps{
   693  					Required: []string{"requiredField"},
   694  				},
   695  			},
   696  		},
   697  		{
   698  			name: "set required rule under map",
   699  			schema: &apiextensions.JSONSchemaProps{
   700  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   701  					Schema: &apiextensions.JSONSchemaProps{
   702  						Properties: map[string]apiextensions.JSONSchemaProps{
   703  							"otherField": {
   704  								Type: "bool",
   705  							},
   706  						},
   707  						Type: "object",
   708  					},
   709  				},
   710  				Type: "object",
   711  			},
   712  			rule: &apiextensions.JSONSchemaProps{
   713  				Required: []string{"requiredField"},
   714  			},
   715  			expectedSchema: &apiextensions.JSONSchemaProps{
   716  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   717  					Schema: &apiextensions.JSONSchemaProps{
   718  						Properties: map[string]apiextensions.JSONSchemaProps{
   719  							"otherField": {
   720  								Type: "bool",
   721  							},
   722  						},
   723  						Type: "object",
   724  						Not: &apiextensions.JSONSchemaProps{
   725  							Required: []string{"requiredField"},
   726  						},
   727  					},
   728  				},
   729  				Type: "object",
   730  			},
   731  		},
   732  		{
   733  			name: "set required rule under array",
   734  			schema: &apiextensions.JSONSchemaProps{
   735  				Items: &apiextensions.JSONSchemaPropsOrArray{
   736  					Schema: &apiextensions.JSONSchemaProps{
   737  						Properties: map[string]apiextensions.JSONSchemaProps{
   738  							"otherField": {
   739  								Type: "bool",
   740  							},
   741  						},
   742  						Type: "object",
   743  					},
   744  				},
   745  				Type: "array",
   746  			},
   747  			rule: &apiextensions.JSONSchemaProps{
   748  				Required: []string{"requiredField"},
   749  			},
   750  			expectedSchema: &apiextensions.JSONSchemaProps{
   751  				Items: &apiextensions.JSONSchemaPropsOrArray{
   752  					Schema: &apiextensions.JSONSchemaProps{
   753  						Properties: map[string]apiextensions.JSONSchemaProps{
   754  							"otherField": {
   755  								Type: "bool",
   756  							},
   757  						},
   758  						Type: "object",
   759  						Not: &apiextensions.JSONSchemaProps{
   760  							Required: []string{"requiredField"},
   761  						},
   762  					},
   763  				},
   764  				Type: "array",
   765  			},
   766  		},
   767  		{
   768  			name: "can't set rulee under a string field",
   769  			schema: &apiextensions.JSONSchemaProps{
   770  				Type: "string",
   771  			},
   772  			rule: &apiextensions.JSONSchemaProps{
   773  				Required: []string{"unsettable"},
   774  			},
   775  			hasError: true,
   776  		},
   777  		{
   778  			name: "can't set rule for incorrect object schema",
   779  			schema: &apiextensions.JSONSchemaProps{
   780  				Type: "object",
   781  			},
   782  			rule: &apiextensions.JSONSchemaProps{
   783  				Required: []string{"unsettable"},
   784  			},
   785  			hasError: true,
   786  		},
   787  	}
   788  
   789  	for _, tc := range tests {
   790  		t.Run(tc.name, func(t *testing.T) {
   791  			if err := crdutil.SetNotRuleForObjectOrArray(tc.schema, tc.rule); err != nil {
   792  				if !tc.hasError {
   793  					t.Fatalf("got an error, but want no error: %v", err)
   794  				}
   795  				return
   796  			}
   797  			if got, want := tc.schema, tc.expectedSchema; !reflect.DeepEqual(got, want) {
   798  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   799  			}
   800  		})
   801  	}
   802  }
   803  
   804  func TestGetOneOfRuleForObjectOrArray(t *testing.T) {
   805  	t.Parallel()
   806  	tests := []struct {
   807  		name         string
   808  		schema       *apiextensions.JSONSchemaProps
   809  		expectedRule []*apiextensions.JSONSchemaProps
   810  		hasError     bool
   811  	}{
   812  		{
   813  			name: "get oneOf rule under object",
   814  			schema: &apiextensions.JSONSchemaProps{
   815  				Properties: map[string]apiextensions.JSONSchemaProps{
   816  					"test": {
   817  						Description: "field under object",
   818  						Type:        "string",
   819  					},
   820  				},
   821  				Type: "object",
   822  				OneOf: []apiextensions.JSONSchemaProps{
   823  					{Required: []string{"field1"}},
   824  					{Required: []string{"field2"}},
   825  				},
   826  			},
   827  			expectedRule: []*apiextensions.JSONSchemaProps{
   828  				{Required: []string{"field1"}},
   829  				{Required: []string{"field2"}},
   830  			},
   831  		},
   832  		{
   833  			name: "get oneOf rule under map",
   834  			schema: &apiextensions.JSONSchemaProps{
   835  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   836  					Schema: &apiextensions.JSONSchemaProps{
   837  						Properties: map[string]apiextensions.JSONSchemaProps{
   838  							"test": {
   839  								Description: "field under map",
   840  								Type:        "string",
   841  							},
   842  						},
   843  						Type: "object",
   844  						OneOf: []apiextensions.JSONSchemaProps{
   845  							{Required: []string{"field1"}},
   846  							{Required: []string{"field2"}},
   847  						},
   848  					},
   849  				},
   850  				Type: "object",
   851  			},
   852  			expectedRule: []*apiextensions.JSONSchemaProps{
   853  				{Required: []string{"field1"}},
   854  				{Required: []string{"field2"}},
   855  			},
   856  		},
   857  		{
   858  			name: "get oneOf rule under array",
   859  			schema: &apiextensions.JSONSchemaProps{
   860  				Items: &apiextensions.JSONSchemaPropsOrArray{
   861  					Schema: &apiextensions.JSONSchemaProps{
   862  						Properties: map[string]apiextensions.JSONSchemaProps{
   863  							"test": {
   864  								Description: "field under array",
   865  								Type:        "string",
   866  							},
   867  						},
   868  						Type: "object",
   869  						OneOf: []apiextensions.JSONSchemaProps{
   870  							{Required: []string{"field1"}},
   871  							{Required: []string{"field2"}},
   872  						},
   873  					},
   874  				},
   875  				Type: "array",
   876  			},
   877  			expectedRule: []*apiextensions.JSONSchemaProps{
   878  				{Required: []string{"field1"}},
   879  				{Required: []string{"field2"}},
   880  			},
   881  		},
   882  		{
   883  			name: "can't get rule for field under string field",
   884  			schema: &apiextensions.JSONSchemaProps{
   885  				Type: "string",
   886  				OneOf: []apiextensions.JSONSchemaProps{
   887  					{Required: []string{"field1"}},
   888  					{Required: []string{"field2"}},
   889  				},
   890  			},
   891  			hasError: true,
   892  		},
   893  		{
   894  			name: "can't get rule for incorrect object schema",
   895  			schema: &apiextensions.JSONSchemaProps{
   896  				Description: "an object field without properties or additionalProperties",
   897  				Type:        "object",
   898  				OneOf: []apiextensions.JSONSchemaProps{
   899  					{Required: []string{"field1"}},
   900  					{Required: []string{"field2"}},
   901  				},
   902  			},
   903  			hasError: true,
   904  		},
   905  	}
   906  
   907  	for _, tc := range tests {
   908  		t.Run(tc.name, func(t *testing.T) {
   909  			rule, err := crdutil.GetOneOfRuleForObjectOrArray(tc.schema)
   910  			if err != nil {
   911  				if !tc.hasError {
   912  					t.Fatalf("got an error, but want no error: %v", err)
   913  				}
   914  				return
   915  			}
   916  			if got, want := rule, tc.expectedRule; !reflect.DeepEqual(got, want) {
   917  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
   918  			}
   919  		})
   920  	}
   921  }
   922  
   923  func TestSetOneOfRuleForObjectOrArray(t *testing.T) {
   924  	t.Parallel()
   925  	tests := []struct {
   926  		name           string
   927  		schema         *apiextensions.JSONSchemaProps
   928  		rule           []*apiextensions.JSONSchemaProps
   929  		expectedSchema *apiextensions.JSONSchemaProps
   930  		hasError       bool
   931  	}{
   932  		{
   933  			name: "set required rule under object",
   934  			schema: &apiextensions.JSONSchemaProps{
   935  				Properties: map[string]apiextensions.JSONSchemaProps{
   936  					"otherField": {
   937  						Type: "bool",
   938  					},
   939  				},
   940  				Type: "object",
   941  			},
   942  			rule: []*apiextensions.JSONSchemaProps{
   943  				{Required: []string{"field1"}},
   944  				{Required: []string{"field2"}},
   945  			},
   946  			expectedSchema: &apiextensions.JSONSchemaProps{
   947  				Properties: map[string]apiextensions.JSONSchemaProps{
   948  					"otherField": {
   949  						Type: "bool",
   950  					},
   951  				},
   952  				Type: "object",
   953  				OneOf: []apiextensions.JSONSchemaProps{
   954  					{Required: []string{"field1"}},
   955  					{Required: []string{"field2"}},
   956  				},
   957  			},
   958  		},
   959  		{
   960  			name: "set required rule under map",
   961  			schema: &apiextensions.JSONSchemaProps{
   962  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   963  					Schema: &apiextensions.JSONSchemaProps{
   964  						Properties: map[string]apiextensions.JSONSchemaProps{
   965  							"otherField": {
   966  								Type: "bool",
   967  							},
   968  						},
   969  						Type: "object",
   970  					},
   971  				},
   972  				Type: "object",
   973  			},
   974  			rule: []*apiextensions.JSONSchemaProps{
   975  				{Required: []string{"field1"}},
   976  				{Required: []string{"field2"}},
   977  			},
   978  			expectedSchema: &apiextensions.JSONSchemaProps{
   979  				AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{
   980  					Schema: &apiextensions.JSONSchemaProps{
   981  						Properties: map[string]apiextensions.JSONSchemaProps{
   982  							"otherField": {
   983  								Type: "bool",
   984  							},
   985  						},
   986  						Type: "object",
   987  						OneOf: []apiextensions.JSONSchemaProps{
   988  							{Required: []string{"field1"}},
   989  							{Required: []string{"field2"}},
   990  						},
   991  					},
   992  				},
   993  				Type: "object",
   994  			},
   995  		},
   996  		{
   997  			name: "set required rule under array",
   998  			schema: &apiextensions.JSONSchemaProps{
   999  				Items: &apiextensions.JSONSchemaPropsOrArray{
  1000  					Schema: &apiextensions.JSONSchemaProps{
  1001  						Properties: map[string]apiextensions.JSONSchemaProps{
  1002  							"otherField": {
  1003  								Type: "bool",
  1004  							},
  1005  						},
  1006  						Type: "object",
  1007  					},
  1008  				},
  1009  				Type: "array",
  1010  			},
  1011  			rule: []*apiextensions.JSONSchemaProps{
  1012  				{Required: []string{"field1"}},
  1013  				{Required: []string{"field2"}},
  1014  			},
  1015  			expectedSchema: &apiextensions.JSONSchemaProps{
  1016  				Items: &apiextensions.JSONSchemaPropsOrArray{
  1017  					Schema: &apiextensions.JSONSchemaProps{
  1018  						Properties: map[string]apiextensions.JSONSchemaProps{
  1019  							"otherField": {
  1020  								Type: "bool",
  1021  							},
  1022  						},
  1023  						Type: "object",
  1024  						OneOf: []apiextensions.JSONSchemaProps{
  1025  							{Required: []string{"field1"}},
  1026  							{Required: []string{"field2"}},
  1027  						},
  1028  					},
  1029  				},
  1030  				Type: "array",
  1031  			},
  1032  		},
  1033  		{
  1034  			name: "can't set rulee under a string field",
  1035  			schema: &apiextensions.JSONSchemaProps{
  1036  				Type: "string",
  1037  			},
  1038  			rule: []*apiextensions.JSONSchemaProps{
  1039  				{Required: []string{"field1"}},
  1040  				{Required: []string{"field2"}},
  1041  			},
  1042  			hasError: true,
  1043  		},
  1044  		{
  1045  			name: "can't set rule for incorrect object schema",
  1046  			schema: &apiextensions.JSONSchemaProps{
  1047  				Type: "object",
  1048  			},
  1049  			rule: []*apiextensions.JSONSchemaProps{
  1050  				{Required: []string{"field1"}},
  1051  				{Required: []string{"field2"}},
  1052  			},
  1053  			hasError: true,
  1054  		},
  1055  	}
  1056  
  1057  	for _, tc := range tests {
  1058  		t.Run(tc.name, func(t *testing.T) {
  1059  			if err := crdutil.SetOneOfRuleForObjectOrArray(tc.schema, tc.rule); err != nil {
  1060  				if !tc.hasError {
  1061  					t.Fatalf("got an error, but want no error: %v", err)
  1062  				}
  1063  				return
  1064  			}
  1065  			if got, want := tc.schema, tc.expectedSchema; !reflect.DeepEqual(got, want) {
  1066  				t.Fatalf("unexpected diff (-want +got): \n%v", cmp.Diff(want, got))
  1067  			}
  1068  		})
  1069  	}
  1070  }
  1071  

View as plain text