k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/validate/object_validator_test.go (about) 1 // Copyright 2017 go-swagger maintainers 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 validate 16 17 import ( 18 "testing" 19 20 "github.com/stretchr/testify/assert" 21 kubeopenapierrors "k8s.io/kube-openapi/pkg/validation/errors" 22 "k8s.io/kube-openapi/pkg/validation/spec" 23 "k8s.io/kube-openapi/pkg/validation/strfmt" 24 ) 25 26 func itemsFixture() map[string]interface{} { 27 return map[string]interface{}{ 28 "type": "array", 29 "items": "dummy", 30 } 31 } 32 33 func expectAllValid(t *testing.T, ov ValueValidator, dataValid, dataInvalid map[string]interface{}) { 34 res := ov.Validate(dataValid) 35 assert.Equal(t, 0, len(res.Errors)) 36 37 res = ov.Validate(dataInvalid) 38 assert.Equal(t, 0, len(res.Errors)) 39 } 40 41 func expectOnlyInvalid(t *testing.T, ov ValueValidator, dataValid, dataInvalid map[string]interface{}) { 42 res := ov.Validate(dataValid) 43 assert.Equal(t, 0, len(res.Errors)) 44 45 res = ov.Validate(dataInvalid) 46 assert.NotEqual(t, 0, len(res.Errors)) 47 } 48 49 func TestItemsMustBeTypeArray(t *testing.T) { 50 ov := new(objectValidator) 51 dataValid := itemsFixture() 52 dataInvalid := map[string]interface{}{ 53 "type": "object", 54 "items": "dummy", 55 } 56 expectAllValid(t, ov, dataValid, dataInvalid) 57 } 58 59 func TestItemsMustHaveType(t *testing.T) { 60 ov := new(objectValidator) 61 dataValid := itemsFixture() 62 dataInvalid := map[string]interface{}{ 63 "items": "dummy", 64 } 65 expectAllValid(t, ov, dataValid, dataInvalid) 66 } 67 68 func TestTypeArrayMustHaveItems(t *testing.T) { 69 ov := new(objectValidator) 70 dataValid := itemsFixture() 71 dataInvalid := map[string]interface{}{ 72 "type": "array", 73 "key": "dummy", 74 } 75 expectAllValid(t, ov, dataValid, dataInvalid) 76 } 77 78 // Test edge cases in object_validator which are difficult 79 // to simulate with specs 80 // (this one is a trivial, just to check all methods are filled) 81 func TestObjectValidator_EdgeCases(t *testing.T) { 82 s := objectValidator{} 83 s.SetPath("path") 84 assert.Equal(t, "path", s.Path) 85 } 86 87 func TestMinPropertiesMaxPropertiesDontShortCircuit(t *testing.T) { 88 s := objectValidator{ 89 In: "body", 90 Path: "some.path[5]", 91 KnownFormats: strfmt.Default, 92 MinProperties: ptr(int64(20)), 93 MaxProperties: ptr(int64(0)), 94 Properties: map[string]spec.Schema{ 95 "intField": { 96 SchemaProps: spec.SchemaProps{ 97 Type: spec.StringOrArray{"integer"}, 98 }, 99 }, 100 "requiredField": { 101 SchemaProps: spec.SchemaProps{ 102 Type: spec.StringOrArray{"string"}, 103 }, 104 }, 105 }, 106 Required: []string{"requiredField"}, 107 AdditionalProperties: &spec.SchemaOrBool{ 108 Allows: true, 109 Schema: &spec.Schema{ 110 SchemaProps: spec.SchemaProps{ 111 Type: spec.StringOrArray{"string"}, 112 Pattern: "^20[0-9][0-9]", 113 }, 114 }, 115 }, 116 Options: SchemaValidatorOptions{ 117 NewValidatorForIndex: func(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator { 118 return NewSchemaValidator(schema, rootSchema, root, formats, opts...) 119 }, 120 NewValidatorForField: func(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator { 121 return NewSchemaValidator(schema, rootSchema, root, formats, opts...) 122 }, 123 }, 124 } 125 126 obj := map[string]interface{}{ 127 "field": "hello, world", 128 } 129 res := s.Validate(obj) 130 131 assert.ElementsMatch(t, []*kubeopenapierrors.Validation{ 132 kubeopenapierrors.TooFewProperties(s.Path, s.In, *s.MinProperties, int64(len(obj))), 133 kubeopenapierrors.TooManyProperties(s.Path, s.In, *s.MaxProperties, int64(len(obj))), 134 kubeopenapierrors.FailedPattern(s.Path+"."+"field", s.In, s.AdditionalProperties.Schema.Pattern, "hello, world"), 135 kubeopenapierrors.Required(s.Path+"."+"requiredField", s.In), 136 }, res.Errors) 137 138 obj = map[string]interface{}{ 139 "field": "hello, world", 140 "field2": "hello, other world", 141 "field3": "hello, third world", 142 "intField": "a string", 143 } 144 res = s.Validate(obj) 145 146 assert.ElementsMatch(t, []*kubeopenapierrors.Validation{ 147 kubeopenapierrors.TooFewProperties(s.Path, s.In, *s.MinProperties, int64(len(obj))), 148 kubeopenapierrors.TooManyProperties(s.Path, s.In, *s.MaxProperties, int64(len(obj))), 149 kubeopenapierrors.FailedPattern(s.Path+"."+"field", s.In, s.AdditionalProperties.Schema.Pattern, "hello, world"), 150 kubeopenapierrors.FailedPattern(s.Path+"."+"field2", s.In, s.AdditionalProperties.Schema.Pattern, "hello, other world"), 151 kubeopenapierrors.FailedPattern(s.Path+"."+"field3", s.In, s.AdditionalProperties.Schema.Pattern, "hello, third world"), 152 kubeopenapierrors.InvalidType(s.Path+"."+"intField", s.In, "integer", "string"), 153 kubeopenapierrors.Required(s.Path+"."+"requiredField", s.In), 154 }, res.Errors) 155 } 156 157 func ptr[T any](v T) *T { 158 return &v 159 }