github.com/rajeev159/opa@v0.45.0/ast/schema_test.go (about)

     1  package ast
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/open-policy-agent/opa/types"
    13  	"github.com/open-policy-agent/opa/util"
    14  )
    15  
    16  func testParseSchema(t *testing.T, schema string, expectedType types.Type, expectedError error) {
    17  	t.Helper()
    18  
    19  	var sch interface{}
    20  	err := util.Unmarshal([]byte(schema), &sch)
    21  	if err != nil {
    22  		t.Fatalf("unexpected error: %s", err)
    23  	}
    24  	newtype, err := loadSchema(sch, nil)
    25  	if err != nil && errors.Is(err, expectedError) {
    26  		t.Fatalf("unexpected error: %v", err)
    27  	}
    28  	if newtype == nil && expectedType != nil {
    29  		t.Fatalf("parseSchema returned nil type")
    30  	}
    31  	if newtype != nil && expectedType == nil {
    32  		t.Fatalf("expected nil but parseSchema returned a not nil type")
    33  	}
    34  	if types.Compare(newtype, expectedType) != 0 {
    35  		t.Fatalf("parseSchema returned an incorrect type: %s, expected: %s", newtype.String(), expectedType.String())
    36  	}
    37  }
    38  
    39  func TestParseSchemaObject(t *testing.T) {
    40  	innerObjectStaticProps := []*types.StaticProperty{}
    41  	innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "a", Value: types.N})
    42  	innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, types.N)})
    43  	innerObjectStaticProps = append(innerObjectStaticProps, &types.StaticProperty{Key: "c", Value: types.A})
    44  	innerObjectType := types.NewObject(innerObjectStaticProps, nil)
    45  
    46  	staticProps := []*types.StaticProperty{}
    47  	staticProps = append(staticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, innerObjectType)})
    48  	staticProps = append(staticProps, &types.StaticProperty{Key: "foo", Value: types.S})
    49  
    50  	expectedType := types.NewObject(staticProps, nil)
    51  	testParseSchema(t, objectSchema, expectedType, nil)
    52  }
    53  
    54  func TestSetTypesWithSchemaRef(t *testing.T) {
    55  	var sch interface{}
    56  
    57  	ts := kubeSchemaServer(t)
    58  	t.Cleanup(ts.Close)
    59  	refSchemaReplaced := strings.ReplaceAll(refSchema, "https://kubernetesjsonschema.dev/v1.14.0/", ts.URL+"/")
    60  	err := util.Unmarshal([]byte(refSchemaReplaced), &sch)
    61  	if err != nil {
    62  		t.Fatalf("unexpected error: %s", err)
    63  	}
    64  
    65  	t.Run("remote refs disabled", func(t *testing.T) {
    66  		_, err := loadSchema(sch, []string{})
    67  		if err == nil {
    68  			t.Fatal("expected error, got nil")
    69  		}
    70  		expErr := fmt.Sprintf("unable to compile the schema: remote reference loading disabled: %s/_definitions.json", ts.URL)
    71  		if exp, act := expErr, err.Error(); act != exp {
    72  			t.Errorf("expected message %q, got %q", exp, act)
    73  		}
    74  	})
    75  
    76  	t.Run("all remote refs enabled", func(t *testing.T) {
    77  		newtype, err := loadSchema(sch, nil)
    78  		if err != nil {
    79  			t.Fatalf("unexpected error: %v", err)
    80  		}
    81  		if newtype == nil {
    82  			t.Fatalf("parseSchema returned nil type")
    83  		}
    84  		if newtype.String() != "object<apiVersion: string, kind: string, metadata: object<annotations: object[any: any], clusterName: string, creationTimestamp: string, deletionGracePeriodSeconds: number, deletionTimestamp: string, finalizers: array[string], generateName: string, generation: number, initializers: object<pending: array[object<name: string>], result: object<apiVersion: string, code: number, details: object<causes: array[object<field: string, message: string, reason: string>], group: string, kind: string, name: string, retryAfterSeconds: number, uid: string>, kind: string, message: string, metadata: object<continue: string, resourceVersion: string, selfLink: string>, reason: string, status: string>>, labels: object[any: any], managedFields: array[object<apiVersion: string, fields: object[any: any], manager: string, operation: string, time: string>], name: string, namespace: string, ownerReferences: array[object<apiVersion: string, blockOwnerDeletion: boolean, controller: boolean, kind: string, name: string, uid: string>], resourceVersion: string, selfLink: string, uid: string>>" {
    85  			t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String())
    86  		}
    87  	})
    88  
    89  	t.Run("desired remote ref selectively enabled", func(t *testing.T) {
    90  		newtype, err := loadSchema(sch, []string{"127.0.0.1"})
    91  		if err != nil {
    92  			t.Fatalf("unexpected error: %v", err)
    93  		}
    94  		if newtype == nil {
    95  			t.Fatalf("parseSchema returned nil type")
    96  		}
    97  		if newtype.String() != "object<apiVersion: string, kind: string, metadata: object<annotations: object[any: any], clusterName: string, creationTimestamp: string, deletionGracePeriodSeconds: number, deletionTimestamp: string, finalizers: array[string], generateName: string, generation: number, initializers: object<pending: array[object<name: string>], result: object<apiVersion: string, code: number, details: object<causes: array[object<field: string, message: string, reason: string>], group: string, kind: string, name: string, retryAfterSeconds: number, uid: string>, kind: string, message: string, metadata: object<continue: string, resourceVersion: string, selfLink: string>, reason: string, status: string>>, labels: object[any: any], managedFields: array[object<apiVersion: string, fields: object[any: any], manager: string, operation: string, time: string>], name: string, namespace: string, ownerReferences: array[object<apiVersion: string, blockOwnerDeletion: boolean, controller: boolean, kind: string, name: string, uid: string>], resourceVersion: string, selfLink: string, uid: string>>" {
    98  			t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String())
    99  		}
   100  	})
   101  
   102  	t.Run("different remote ref selectively enabled", func(t *testing.T) {
   103  		_, err := loadSchema(sch, []string{"foo"})
   104  		if err == nil {
   105  			t.Fatal("expected error, got nil")
   106  		}
   107  		expErr := fmt.Sprintf("unable to compile the schema: remote reference loading disabled: %s/_definitions.json", ts.URL)
   108  		if exp, act := expErr, err.Error(); act != exp {
   109  			t.Errorf("expected message %q, got %q", exp, act)
   110  		}
   111  	})
   112  }
   113  
   114  func TestSetTypesWithPodSchema(t *testing.T) {
   115  	var sch interface{}
   116  
   117  	ts := kubeSchemaServer(t)
   118  	t.Cleanup(ts.Close)
   119  
   120  	podSchemaReplaced := strings.ReplaceAll(podSchema, "https://kubernetesjsonschema.dev/v1.14.0/", ts.URL+"/")
   121  	err := util.Unmarshal([]byte(podSchemaReplaced), &sch)
   122  	if err != nil {
   123  		t.Fatalf("unexpected error: %s", err)
   124  	}
   125  	newtype, err := loadSchema(sch, nil)
   126  	if err != nil {
   127  		t.Fatalf("unexpected error: %s", err)
   128  	}
   129  	if newtype == nil {
   130  		t.Fatalf("parseSchema returned nil type")
   131  	}
   132  	if newtype.String() == "object<apiVersion: string, kind: string, metadata: any, spec: any, status: any>" {
   133  		t.Fatalf("parseSchema returned an incorrect type: %s", newtype.String())
   134  	}
   135  
   136  }
   137  
   138  func TestAllOfSchemas(t *testing.T) {
   139  	//Test 1: object schema
   140  	objectSchemaStaticProps := []*types.StaticProperty{}
   141  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "AddressLine1", Value: types.S})
   142  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "AddressLine2", Value: types.S})
   143  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "City", Value: types.S})
   144  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "State", Value: types.S})
   145  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S})
   146  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "County", Value: types.S})
   147  	objectSchemaStaticProps = append(objectSchemaStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S})
   148  	objectSchemaExpectedType := types.NewObject(objectSchemaStaticProps, nil)
   149  
   150  	//Test 2: array schema
   151  	arrayExpectedType := types.NewArray(nil, types.N)
   152  
   153  	//Test 3: parent variation
   154  	parentVariationStaticProps := []*types.StaticProperty{}
   155  	parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "State", Value: types.S})
   156  	parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S})
   157  	parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "County", Value: types.S})
   158  	parentVariationStaticProps = append(parentVariationStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S})
   159  	parentVariationExpectedType := types.NewObject(parentVariationStaticProps, nil)
   160  
   161  	//Test 4: empty schema with allOf
   162  	emptyExpectedType := types.A
   163  
   164  	//Tests 5 & 6: schema with array of arrays,  object and array as siblings
   165  	expectedError := fmt.Errorf("unable to merge these schemas")
   166  
   167  	//Test 7: array of objects
   168  	arrayOfObjectsStaticProps := []*types.StaticProperty{}
   169  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "State", Value: types.S})
   170  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S})
   171  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "County", Value: types.S})
   172  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.S})
   173  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "Street", Value: types.S})
   174  	arrayOfObjectsStaticProps = append(arrayOfObjectsStaticProps, &types.StaticProperty{Key: "House", Value: types.S})
   175  	innerType := types.NewObject(arrayOfObjectsStaticProps, nil)
   176  	arrayOfObjectsExpectedType := types.NewArray(nil, innerType)
   177  
   178  	//Tests 8 & 9: allOf schema with type not specified
   179  	objectMissingStaticProps := []*types.StaticProperty{}
   180  	objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "AddressLine", Value: types.S})
   181  	objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "State", Value: types.S})
   182  	objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "ZipCode", Value: types.S})
   183  	objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "County", Value: types.S})
   184  	objectMissingStaticProps = append(objectMissingStaticProps, &types.StaticProperty{Key: "PostCode", Value: types.N})
   185  	objectMissingExpectedType := types.NewObject(objectMissingStaticProps, nil)
   186  	arrayMissingExpectedType := types.NewArray([]types.Type{types.N, types.N}, nil)
   187  
   188  	//Tests 10 & 11: allOf schema with array that contains different types (with and without error)
   189  	arrayDifTypesExpectedType := types.NewArray([]types.Type{types.S, types.N}, nil)
   190  
   191  	//Test 12: array inside of object
   192  	arrayInObjectstaticProps := []*types.StaticProperty{}
   193  	arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "age", Value: types.N})
   194  	arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "name", Value: types.S})
   195  	arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "personality", Value: types.S})
   196  	arrayInObjectstaticProps = append(arrayInObjectstaticProps, &types.StaticProperty{Key: "nickname", Value: types.S})
   197  	innerObjectsType := types.NewObject(arrayInObjectstaticProps, nil)
   198  	arrayInObjectInnerType := types.NewArray(nil, innerObjectsType)
   199  	arrayInObjectExpectedType := types.NewObject([]*types.StaticProperty{
   200  		types.NewStaticProperty("familyMembers", arrayInObjectInnerType)}, nil)
   201  
   202  	//Test 13: allOf inside core schema
   203  	coreStaticProps := []*types.StaticProperty{}
   204  	coreStaticProps = append(coreStaticProps, &types.StaticProperty{Key: "accessMe", Value: types.S})
   205  	coreStaticProps = append(coreStaticProps, &types.StaticProperty{Key: "accessYou", Value: types.S})
   206  	insideType := types.NewObject(coreStaticProps, nil)
   207  	outerType := []*types.StaticProperty{}
   208  	outerType = append(outerType, &types.StaticProperty{Key: "RandomInfo", Value: insideType})
   209  	outerType = append(outerType, &types.StaticProperty{Key: "AddressLine", Value: types.S})
   210  	coreSchemaExpectedType := types.NewObject(outerType, nil)
   211  
   212  	//Tests 14-17: other types besides array and object
   213  	expectedStringType := types.NewString()
   214  	expectedIntegerType := types.NewNumber()
   215  	expectedBooleanType := types.NewBoolean()
   216  
   217  	//Test 18: array with uneven numbers of items children to merge
   218  	expectedUnevenArrayType := types.NewArray([]types.Type{types.N, types.N, types.S}, nil)
   219  
   220  	tests := []struct {
   221  		note          string
   222  		schema        string
   223  		expectedType  types.Type
   224  		expectedError error
   225  	}{
   226  		{
   227  			note:          "allOf with mergeable Object types in schema",
   228  			schema:        allOfObjectSchema,
   229  			expectedType:  objectSchemaExpectedType,
   230  			expectedError: nil,
   231  		},
   232  		{
   233  			note:          "allOf with mergeable Array types in schema",
   234  			schema:        allOfArraySchema,
   235  			expectedType:  arrayExpectedType,
   236  			expectedError: nil,
   237  		},
   238  		{
   239  			note:          "allOf without a parent schema",
   240  			schema:        allOfSchemaParentVariation,
   241  			expectedType:  parentVariationExpectedType,
   242  			expectedError: nil,
   243  		},
   244  		{
   245  			note:          "allOf with empty schema",
   246  			schema:        emptySchema,
   247  			expectedType:  emptyExpectedType,
   248  			expectedError: nil,
   249  		},
   250  		{
   251  			note:          "allOf schema with unmergeable Array of Arrays",
   252  			schema:        allOfArrayOfArrays,
   253  			expectedType:  nil,
   254  			expectedError: expectedError,
   255  		},
   256  		{
   257  			note:          "allOf with mergeable Array of Object types in schema",
   258  			schema:        allOfArrayOfObjects,
   259  			expectedType:  arrayOfObjectsExpectedType,
   260  			expectedError: nil,
   261  		},
   262  		{
   263  			note:          "allOf schema with Array and Object types as siblings",
   264  			schema:        allOfObjectAndArray,
   265  			expectedType:  nil,
   266  			expectedError: expectedError,
   267  		},
   268  		{
   269  			note:          "allOf with mergeable Object types in schema with type declaration missing",
   270  			schema:        allOfObjectMissing,
   271  			expectedType:  objectMissingExpectedType,
   272  			expectedError: nil,
   273  		},
   274  		{
   275  			note:          "allOf with mergeable Array types in schema with type declaration missing",
   276  			schema:        allOfArrayMissing,
   277  			expectedType:  arrayMissingExpectedType,
   278  			expectedError: nil,
   279  		},
   280  		{
   281  			note:          "allOf schema with an Array that contains different mergeable types",
   282  			schema:        allOfArrayDifTypes,
   283  			expectedType:  arrayDifTypesExpectedType,
   284  			expectedError: nil,
   285  		},
   286  		{
   287  			note:          "allOf schema with Array type that contains different unmergeable types",
   288  			schema:        allOfArrayDifTypesWithError,
   289  			expectedType:  nil,
   290  			expectedError: expectedError,
   291  		},
   292  		{
   293  			note:          "allOf with mergeable Object containing Array types in schema",
   294  			schema:        allOfArrayInsideObject,
   295  			expectedType:  arrayInObjectExpectedType,
   296  			expectedError: nil,
   297  		},
   298  		{
   299  			note:          "allOf with mergeable types inside of core schema",
   300  			schema:        allOfInsideCoreSchema,
   301  			expectedType:  coreSchemaExpectedType,
   302  			expectedError: nil,
   303  		},
   304  		{
   305  			note:          "allOf with mergeable String types in schema",
   306  			schema:        allOfStringSchema,
   307  			expectedType:  expectedStringType,
   308  			expectedError: nil,
   309  		},
   310  		{
   311  			note:          "allOf with mergeable Integer types in schema",
   312  			schema:        allOfIntegerSchema,
   313  			expectedType:  expectedIntegerType,
   314  			expectedError: nil,
   315  		},
   316  		{
   317  			note:          "allOf with mergeable Boolean types in schema",
   318  			schema:        allOfBooleanSchema,
   319  			expectedType:  expectedBooleanType,
   320  			expectedError: nil,
   321  		},
   322  		{
   323  			note:          "allOf schema with different unmergeable types",
   324  			schema:        allOfTypeErrorSchema,
   325  			expectedType:  nil,
   326  			expectedError: expectedError,
   327  		},
   328  		{
   329  			note:          "allOf schema with unmergeable types String and Boolean",
   330  			schema:        allOfStringSchemaWithError,
   331  			expectedType:  nil,
   332  			expectedError: expectedError,
   333  		},
   334  		{
   335  			note:          "allOf unmergeable schema with different parent and items types",
   336  			schema:        allOfSchemaWithParentError,
   337  			expectedType:  nil,
   338  			expectedError: expectedError,
   339  		},
   340  		{
   341  			note:          "allOf schema of Array type with uneven numbers of items to merge",
   342  			schema:        allOfSchemaWithUnevenArray,
   343  			expectedType:  expectedUnevenArrayType,
   344  			expectedError: nil,
   345  		},
   346  	}
   347  
   348  	for _, tc := range tests {
   349  		t.Run(tc.note, func(t *testing.T) {
   350  			testParseSchema(t, tc.schema, tc.expectedType, tc.expectedError)
   351  		})
   352  	}
   353  }
   354  
   355  func TestParseSchemaUntypedField(t *testing.T) {
   356  	//Expected type is: object<foo: any>
   357  	staticProps := []*types.StaticProperty{}
   358  	staticProps = append(staticProps, &types.StaticProperty{Key: "foo", Value: types.A})
   359  	expectedType := types.NewObject(staticProps, nil)
   360  	testParseSchema(t, untypedFieldObjectSchema, expectedType, nil)
   361  }
   362  
   363  func TestParseSchemaNoChildren(t *testing.T) {
   364  	//Expected type is: object[any: any]
   365  	expectedType := types.NewObject(nil, &types.DynamicProperty{Key: types.A, Value: types.A})
   366  	testParseSchema(t, noChildrenObjectSchema, expectedType, nil)
   367  }
   368  
   369  func TestParseSchemaArrayNoItems(t *testing.T) {
   370  	//Expected type is: object<b: array[any]>
   371  	staticProps := []*types.StaticProperty{}
   372  	staticProps = append(staticProps, &types.StaticProperty{Key: "b", Value: types.NewArray(nil, types.A)})
   373  	expectedType := types.NewObject(staticProps, nil)
   374  	testParseSchema(t, arrayNoItemsSchema, expectedType, nil)
   375  }
   376  
   377  func TestParseSchemaBooleanField(t *testing.T) {
   378  	//Expected type is: object<a: boolean>
   379  	staticProps := []*types.StaticProperty{}
   380  	staticProps = append(staticProps, &types.StaticProperty{Key: "a", Value: types.B})
   381  	expectedType := types.NewObject(staticProps, nil)
   382  	testParseSchema(t, booleanSchema, expectedType, nil)
   383  }
   384  
   385  func TestParseSchemaBasics(t *testing.T) {
   386  	tests := []struct {
   387  		note   string
   388  		schema string
   389  		exp    types.Type
   390  	}{
   391  		{
   392  			note:   "number",
   393  			schema: `{"type": "number"}`,
   394  			exp:    types.N,
   395  		},
   396  		{
   397  			note: "array of objects",
   398  			schema: `{
   399  				"type": "array",
   400  				"items": {
   401  					"type": "object",
   402  					"properties": {
   403  						"id": {"type": "string"},
   404  						"value": {"type": "number"}
   405  					}
   406  				}
   407  			}`,
   408  			exp: types.NewArray(nil, types.NewObject([]*types.StaticProperty{
   409  				types.NewStaticProperty("id", types.S),
   410  				types.NewStaticProperty("value", types.N),
   411  			}, nil)),
   412  		},
   413  		{
   414  			note: "static array items",
   415  			schema: `{
   416  				"type": "array",
   417  				"items": [
   418  					{"type": "string"},
   419  					{"type": "number"}
   420  				]
   421  			}`,
   422  			exp: types.NewArray([]types.Type{
   423  				types.S,
   424  				types.N,
   425  			}, nil),
   426  		},
   427  	}
   428  
   429  	for _, tc := range tests {
   430  		t.Run(tc.note, func(t *testing.T) {
   431  			testParseSchema(t, tc.schema, tc.exp, nil)
   432  		})
   433  	}
   434  }
   435  
   436  func TestCompileSchemaEmptySchema(t *testing.T) {
   437  	schema := ""
   438  	var sch interface{}
   439  	err := util.Unmarshal([]byte(schema), &sch)
   440  	if err != nil {
   441  		t.Fatalf("unexpected error: %s", err)
   442  	}
   443  	jsonSchema, _ := compileSchema(sch, []string{})
   444  	if jsonSchema != nil {
   445  		t.Fatalf("Incorrect return from parseSchema with an empty schema")
   446  	}
   447  }
   448  
   449  func TestParseSchemaWithSchemaBadSchema(t *testing.T) {
   450  	var sch interface{}
   451  	err := util.Unmarshal([]byte(objectSchema), &sch)
   452  	if err != nil {
   453  		t.Fatalf("unexpected error: %s", err)
   454  	}
   455  	jsonSchema, err := compileSchema(sch, []string{})
   456  	if err != nil {
   457  		t.Fatalf("Unable to compile schema: %v", err)
   458  	}
   459  	newtype, err := parseSchema(jsonSchema) // Did not pass the subschema
   460  	if err == nil {
   461  		t.Fatalf("Expected parseSchema() = error, got nil")
   462  	}
   463  	if newtype != nil {
   464  		t.Fatalf("Incorrect return from parseSchema with a bad schema")
   465  	}
   466  }
   467  
   468  func TestAnyOfSchema(t *testing.T) {
   469  
   470  	// Test 1 & 3: anyOf extends the core schema
   471  	extendCoreStaticPropsExp1 := []*types.StaticProperty{types.NewStaticProperty("AddressLine", types.S)}
   472  	extendCoreStaticPropsExp2 := []*types.StaticProperty{
   473  		types.NewStaticProperty("State", types.S),
   474  		types.NewStaticProperty("ZipCode", types.S)}
   475  	extendCoreStaticPropsExp3 := []*types.StaticProperty{
   476  		types.NewStaticProperty("County", types.S),
   477  		types.NewStaticProperty("PostCode", types.N)}
   478  	extendCoreSchemaTypeExp := types.Or(types.NewObject(extendCoreStaticPropsExp3, nil),
   479  		types.Or(
   480  			types.NewObject(extendCoreStaticPropsExp1, nil),
   481  			types.NewObject(extendCoreStaticPropsExp2, nil)))
   482  
   483  	// Test 2: anyOf is inside the core schema
   484  	insideCoreObjType := types.NewObject([]*types.StaticProperty{
   485  		types.NewStaticProperty("accessMe", types.S)}, nil)
   486  	insideCoreAnyType := types.Or(insideCoreObjType, types.N)
   487  	insideCoreStaticProps := []*types.StaticProperty{
   488  		types.NewStaticProperty("AddressLine", types.S),
   489  		types.NewStaticProperty("RandomInfo", insideCoreAnyType)}
   490  	insideCoreTypeExp := types.NewObject(insideCoreStaticProps, nil)
   491  
   492  	// Test 4: anyOf in an array
   493  	arrayObjType1 := types.NewObject([]*types.StaticProperty{
   494  		types.NewStaticProperty("age", types.N),
   495  		types.NewStaticProperty("name", types.S)}, nil)
   496  	arrayObjType2 := types.NewObject([]*types.StaticProperty{
   497  		types.NewStaticProperty("personality", types.S),
   498  		types.NewStaticProperty("nickname", types.S)}, nil)
   499  	arrayOrType := types.Or(arrayObjType1, arrayObjType2)
   500  	arrayArrayType := types.NewArray(nil, arrayOrType)
   501  	arrayObjtype := types.NewObject([]*types.StaticProperty{
   502  		types.NewStaticProperty("familyMembers", arrayArrayType)}, nil)
   503  
   504  	// Test 5: anyOf array has items but not specified
   505  	arrayMissing1 := types.NewArray([]types.Type{
   506  		types.N, types.S}, nil)
   507  	arrayMissing2 := types.NewArray([]types.Type{types.N}, nil)
   508  	arrayMissingType := types.Or(arrayMissing1, arrayMissing2)
   509  
   510  	// Test 6: anyOf Parent variation schema
   511  	anyOfParentVar1 := types.NewObject([]*types.StaticProperty{
   512  		types.NewStaticProperty("State", types.S),
   513  		types.NewStaticProperty("ZipCode", types.S)}, nil)
   514  	anyOfParentVar2 := types.NewObject([]*types.StaticProperty{
   515  		types.NewStaticProperty("County", types.S),
   516  		types.NewStaticProperty("PostCode", types.S)}, nil)
   517  	anyOfParentVarType := types.Or(anyOfParentVar1, anyOfParentVar2)
   518  
   519  	tests := []struct {
   520  		note     string
   521  		schema   string
   522  		expected types.Type
   523  	}{
   524  		{
   525  			note:     "anyOf extend core schema",
   526  			schema:   anyOfExtendCoreSchema,
   527  			expected: extendCoreSchemaTypeExp,
   528  		},
   529  		{
   530  			note:     "anyOf inside core schema",
   531  			schema:   anyOfInsideCoreSchema,
   532  			expected: insideCoreTypeExp,
   533  		},
   534  		{
   535  			note:     "anyOf object missing type",
   536  			schema:   anyOfObjectMissing,
   537  			expected: extendCoreSchemaTypeExp,
   538  		},
   539  		{
   540  			note:     "anyOf of an array",
   541  			schema:   anyOfArraySchema,
   542  			expected: arrayObjtype,
   543  		},
   544  		{
   545  			note:     "anyOf array missing type",
   546  			schema:   anyOfArrayMissing,
   547  			expected: arrayMissingType,
   548  		},
   549  		{
   550  			note:     "anyOf as parent",
   551  			schema:   anyOfSchemaParentVariation,
   552  			expected: anyOfParentVarType,
   553  		},
   554  	}
   555  
   556  	for _, tc := range tests {
   557  		t.Run(tc.note, func(t *testing.T) {
   558  			testParseSchema(t, tc.schema, tc.expected, nil)
   559  		})
   560  	}
   561  }
   562  
   563  func kubeSchemaServer(t *testing.T) *httptest.Server {
   564  	t.Helper()
   565  	bs, err := ioutil.ReadFile("testdata/_definitions.json")
   566  	if err != nil {
   567  		t.Fatal(err)
   568  	}
   569  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
   570  		_, err := w.Write(bs)
   571  		if err != nil {
   572  			panic(err)
   573  		}
   574  	}))
   575  	return ts
   576  }