github.com/Datadog/cnab-go@v0.3.3-beta1.0.20191007143216-bba4b7e723d0/bundle/definition/schema_test.go (about) 1 package definition 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "testing" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 yaml "gopkg.in/yaml.v2" 12 ) 13 14 func TestSimpleUnMarshalDefinition(t *testing.T) { 15 def := `{ 16 "$comment": "schema comment", 17 "$id": "schema id", 18 "$ref": "schema ref", 19 "$schema": "http://json-schema.org/draft-07/schema#", 20 "type": "array", 21 "items": [ 22 { 23 "type": "object", 24 "required": ["description", "schema", "tests"], 25 "properties": { 26 "description": {"type": "string"}, 27 "schema": {}, 28 "tests": { 29 "type": "array", 30 "items": { 31 "type": "object", 32 "required": ["description", "data", "valid"], 33 "properties": { 34 "description": {"type": "string"}, 35 "data": {}, 36 "valid": {"type": "boolean"} 37 }, 38 "additionalProperties": false 39 }, 40 "minItems": 1 41 } 42 }, 43 "additionalProperties": false, 44 "minItems": 1 45 } 46 ], 47 "additionalItems": { 48 "type": "string" 49 } 50 }` 51 52 definition := new(Schema) 53 err := json.Unmarshal([]byte(def), definition) 54 require.NoError(t, err, "should have been able to json.Marshal definition") 55 assert.Equal(t, "array", definition.Type, "type should have been an array") 56 57 err = yaml.UnmarshalStrict([]byte(def), definition) 58 require.NoError(t, err, "should have been able to yaml.Marshal definition") 59 } 60 61 func TestSimpleSchema(t *testing.T) { 62 s := ` 63 { 64 "default": 80, 65 "maximum": 10240, 66 "minimum": 10, 67 "type": "integer" 68 } 69 ` 70 71 definition := new(Schema) 72 err := json.Unmarshal([]byte(s), definition) 73 require.NoError(t, err, "should have been able to marshall definition") 74 assert.Equal(t, "integer", definition.Type, "type should have been an integer") 75 76 maxVal := definition.Maximum 77 require.NotNil(t, maxVal, "maximum should have been loaded") 78 assert.Equal(t, 10240, int(*maxVal), "max should have been 10240") 79 80 minVal := definition.Minimum 81 require.NotNil(t, minVal, "minimum should have been loaded") 82 assert.Equal(t, 10, int(*minVal), "min should have been 10") 83 84 def, ok := definition.Default.(float64) 85 require.True(t, ok, "default should h ave been float64") 86 assert.Equal(t, 80, int(def), "default should have been 80") 87 88 } 89 90 func TestUnknownSchemaType(t *testing.T) { 91 s := ` 92 { 93 "type": "cnab" 94 } 95 ` 96 97 definition := new(Schema) 98 err := json.Unmarshal([]byte(s), definition) 99 assert.Error(t, err, "should not have been able to marshall definition") 100 assert.EqualError(t, err, "error unmarshaling type from json: \"cnab\" is not a valid type") 101 } 102 103 func TestSingleSchemaType(t *testing.T) { 104 s := ` 105 { 106 "type": "number" 107 } 108 ` 109 110 definition := new(Schema) 111 err := json.Unmarshal([]byte(s), definition) 112 assert.NoError(t, err, "should have been able to marshall definition") 113 typeString, ok, err := definition.GetType() 114 assert.NoError(t, err, "should have gotten back no error on fetch of types") 115 assert.True(t, ok, "types should have been a slice of strings") 116 assert.Equal(t, "number", typeString, "should have had a number type") 117 } 118 119 func TestMultipleSchemaTypes(t *testing.T) { 120 s := ` 121 { 122 "type": ["number", "string"] 123 } 124 ` 125 126 definition := new(Schema) 127 err := json.Unmarshal([]byte(s), definition) 128 assert.NoError(t, err, "should have been able to marshall definition") 129 types, ok, err := definition.GetTypes() 130 assert.NoError(t, err, "should have gotten back no error on fetch of types") 131 assert.True(t, ok, "types should have been a slice of strings") 132 assert.Equal(t, 2, len(types), "should have had two types") 133 } 134 135 func TestObjectDefinitionType(t *testing.T) { 136 s := `{ 137 "type": "object", 138 "properties" : { 139 "port" : { 140 "default": 80, 141 "maximum": 10240, 142 "minimum": 10, 143 "type": "integer" 144 } 145 }, 146 "required" : ["port"] 147 }` 148 definition := new(Schema) 149 err := json.Unmarshal([]byte(s), definition) 150 require.NoError(t, err, "should have been able to marshall definition") 151 assert.Equal(t, "object", definition.Type, "type should have been an object") 152 props := definition.Properties 153 assert.NotNil(t, props, "should have found properties") 154 assert.Equal(t, 1, len(props), "should have had a single property") 155 propSchema, ok := props["port"] 156 assert.True(t, ok, "should have found port property") 157 assert.Equal(t, "integer", propSchema.Type, "port type should have been an integer") 158 159 val := struct { 160 Port int `json:"port"` 161 }{ 162 Port: 80, 163 } 164 valErrors, err := definition.Validate(val) 165 assert.Empty(t, valErrors, "expected no validation errors") 166 assert.NoError(t, err, "expected not to encounter an error in the validation") 167 } 168 169 func TestBooleanTypeValidation(t *testing.T) { 170 boolValue := "true" 171 s := valueTestJSON("boolean", boolValue, boolValue) 172 definition := new(Schema) 173 err := json.Unmarshal([]byte(s), definition) 174 require.NoError(t, err, "schema should be valid for bool test") 175 thingToCheck := true 176 valErrors, err := definition.Validate(thingToCheck) 177 assert.Empty(t, valErrors, "check of true should have resulted in no validation errors") 178 assert.NoError(t, err, "should not have encountered an error in the validator") 179 valErrors, err = definition.Validate(!thingToCheck) 180 assert.NoError(t, err, "this check should not have resulted in an error") 181 assert.Len(t, valErrors, 1, "expected a validation error") 182 valErr := valErrors[0] 183 assert.Equal(t, "/", valErr.Path, "expected validation to fail at the root") 184 assert.Equal(t, "should be one of [true]", valErr.Error) 185 186 boolValue2 := "true, false" 187 s2 := valueTestJSON("boolean", boolValue, boolValue2) 188 definition2 := new(Schema) 189 err = json.Unmarshal([]byte(s2), definition2) 190 require.NoError(t, err, "test requires unmarshaled bundled") 191 valErrors, err = definition2.Validate(thingToCheck) 192 assert.Len(t, valErrors, 0, "check of true should have resulted in no validation errors with both allowed") 193 assert.NoError(t, err, "should not have encountered an error") 194 valErrors, err = definition2.Validate(!thingToCheck) 195 assert.Len(t, valErrors, 0, "check of false should have resulted in no validation errors with both allowed") 196 assert.NoError(t, err, "should not have encountered an error") 197 } 198 199 func TestStringTypeValidationEnum(t *testing.T) { 200 defaultVal := "\"dog\"" 201 s := valueTestJSON("string", defaultVal, defaultVal) 202 definition := new(Schema) 203 err := json.Unmarshal([]byte(s), definition) 204 require.NoError(t, err, "schema should be valid for string test") 205 valErrors, err := definition.Validate("dog") 206 assert.Len(t, valErrors, 0, "check of `dog` should have resulted in no validation errors") 207 assert.NoError(t, err, "should not have encountered an error") 208 valErrors, err = definition.Validate("cat") 209 assert.Len(t, valErrors, 1, "check of 'cat' should have resulted in a validation error") 210 assert.NoError(t, err) 211 valErr := valErrors[0] 212 assert.Equal(t, "/", valErr.Path, "expected validation to fail at the root") 213 assert.Equal(t, "should be one of [\"dog\"]", valErr.Error) 214 215 anotherSchema := `{ 216 "type" : "string", 217 "enum" : ["chicken", "duck"] 218 }` 219 220 definition2 := new(Schema) 221 err = json.Unmarshal([]byte(anotherSchema), definition2) 222 require.NoError(t, err, "should have been a valid schema") 223 224 valErrors, err = definition2.Validate("pig") 225 assert.NoError(t, err, "shouldn't have gotten an actual error") 226 assert.Len(t, valErrors, 1, "expected validation failure for pig") 227 assert.Equal(t, "should be one of [\"chicken\", \"duck\"]", valErrors[0].Error) 228 } 229 230 func TestStringMinLengthValidator(t *testing.T) { 231 aSchema := `{ 232 "type" : "string", 233 "minLength" : 10 234 }` 235 definition := new(Schema) 236 err := json.Unmarshal([]byte(aSchema), definition) 237 require.NoError(t, err, "should have been a valid schema") 238 239 valErrors, err := definition.Validate("four") 240 assert.Len(t, valErrors, 1, "expected the validation to fail with four characters") 241 assert.Equal(t, "min length of 10 characters required: four", valErrors[0].Error) 242 assert.NoError(t, err) 243 244 valErrors, err = definition.Validate("abcdefghijklmnopqrstuvwxyz") 245 assert.Len(t, valErrors, 0, "expected the validation to not fail with more than 10 characters") 246 assert.NoError(t, err) 247 248 valErrors, err = definition.Validate("qwertyuiop") 249 assert.Len(t, valErrors, 0, "expected the validation to not fail with exactly 10 characters") 250 assert.NoError(t, err) 251 } 252 253 func TestStringMaxLengthValidator(t *testing.T) { 254 aSchema := `{ 255 "$schema": "http://json-schema.org/draft-07/schema#", 256 "$id": "http://json-schema.org/draft-07/schema#", 257 "title": "a string validator", 258 "type" : "string", 259 "maxLength" : 10 260 }` 261 definition := new(Schema) 262 err := json.Unmarshal([]byte(aSchema), definition) 263 require.NoError(t, err, "should have been a valid schema") 264 265 valErrors, err := definition.Validate("four") 266 assert.Len(t, valErrors, 0, "expected the validation to not fail with four characters") 267 assert.NoError(t, err) 268 269 valErrors, err = definition.Validate("abcdefghijklmnopqrstuvwxyz") 270 assert.Len(t, valErrors, 1, "expected the validation to fail with more than 10 characters") 271 assert.NoError(t, err) 272 273 valErrors, err = definition.Validate("qwertyuiop") 274 assert.Len(t, valErrors, 0, "expected the validation to not fail with exactly 10 characters") 275 assert.NoError(t, err) 276 } 277 278 func valueTestJSON(kind, def, enum string) []byte { 279 return []byte(fmt.Sprintf(`{ 280 "type" : "%s", 281 "default": %s, 282 "enum": [ %s ] 283 }`, kind, def, enum)) 284 } 285 286 func TestConvertValue(t *testing.T) { 287 pd := Schema{ 288 Type: "boolean", 289 } 290 is := assert.New(t) 291 292 out, _ := pd.ConvertValue("true") 293 is.True(out.(bool)) 294 out, _ = pd.ConvertValue("false") 295 is.False(out.(bool)) 296 out, _ = pd.ConvertValue("barbeque") 297 is.False(out.(bool)) 298 299 pd.Type = "string" 300 out, err := pd.ConvertValue("hello") 301 is.NoError(err) 302 is.Equal("hello", out.(string)) 303 304 pd.Type = "integer" 305 out, err = pd.ConvertValue("123") 306 is.NoError(err) 307 is.Equal(123, out.(int)) 308 309 _, err = pd.ConvertValue("onetwothree") 310 is.Error(err) 311 312 pd.Type = "number" 313 _, err = pd.ConvertValue("123") 314 is.Error(err) 315 is.Contains(err.Error(), "invalid definition") 316 317 _, err = pd.ConvertValue("5.5") 318 is.Error(err) 319 is.Contains(err.Error(), "invalid definition") 320 321 _, err = pd.ConvertValue("nope") 322 is.Error(err) 323 324 pd.Type = "array" 325 _, err = pd.ConvertValue("nope") 326 is.Error(err) 327 328 _, err = pd.ConvertValue("123") 329 is.Error(err) 330 331 _, err = pd.ConvertValue("true") 332 is.Error(err) 333 334 _, err = pd.ConvertValue("123.5") 335 is.Error(err) 336 337 pd.Type = "object" 338 _, err = pd.ConvertValue("nope") 339 is.Error(err) 340 341 _, err = pd.ConvertValue("123") 342 is.Error(err) 343 344 _, err = pd.ConvertValue("true") 345 is.Error(err) 346 347 _, err = pd.ConvertValue("123.5") 348 is.Error(err) 349 350 }