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  }