diff --git a/src/JsonSchema/Constraints/Collection.php b/src/JsonSchema/Constraints/Collection.php index 311869d5..4ca8e1b3 100644 --- a/src/JsonSchema/Constraints/Collection.php +++ b/src/JsonSchema/Constraints/Collection.php @@ -33,9 +33,14 @@ public function check($value, $schema = null, $path = null, $i = null) } // Verify uniqueItems - // @TODO array_unique doesnt work with objects - if (isset($schema->uniqueItems) && array_unique($value) != $value) { - $this->addError($path, "There are no duplicates allowed in the array"); + if (isset($schema->uniqueItems)) { + $unique = $value; + if (is_array($value) && count($value)) { + $unique = array_map(function($e) { return var_export($e, true); }, $value); + } + if (count(array_unique($unique)) != count($value)) { + $this->addError($path, "There are no duplicates allowed in the array"); + } } // Verify items @@ -54,15 +59,13 @@ public function check($value, $schema = null, $path = null, $i = null) */ protected function validateItems($value, $schema = null, $path = null, $i = null) { - if (!is_array($schema->items)) { + if (is_object($schema->items)) { // just one type definition for the whole array foreach ($value as $k => $v) { $initErrors = $this->getErrors(); // First check if its defined in "items" - if (!isset($schema->additionalItems) || $schema->additionalItems === false) { - $this->checkUndefined($v, $schema->items, $path, $k); - } + $this->checkUndefined($v, $schema->items, $path, $k); // Recheck with "additionalItems" if the first test fails if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) { @@ -71,9 +74,9 @@ protected function validateItems($value, $schema = null, $path = null, $i = null } // Reset errors if needed - if (isset($secondErrors) && count($secondErrors) < $this->getErrors()) { + if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) { $this->errors = $secondErrors; - } elseif (isset($secondErrors) && count($secondErrors) == count($this->getErrors())) { + } else if (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { $this->errors = $initErrors; } } @@ -84,13 +87,16 @@ protected function validateItems($value, $schema = null, $path = null, $i = null $this->checkUndefined($v, $schema->items[$k], $path, $k); } else { // Additional items - if (array_key_exists('additionalItems', $schema) && $schema->additionalItems !== false) { - $this->checkUndefined($v, $schema->additionalItems, $path, $k); + if (property_exists($schema, 'additionalItems')) { + if ($schema->additionalItems !== false) { + $this->checkUndefined($v, $schema->additionalItems, $path, $k); + } else { + $this->addError( + $path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items'); + } } else { - $this->addError( - $path, - 'The item ' . $i . '[' . $k . '] is not defined in the objTypeDef and the objTypeDef does not allow additional properties' - ); + // Should be valid against an empty schema + $this->checkUndefined($v, new \stdClass(), $path, $k); } } } diff --git a/src/JsonSchema/Constraints/ConstraintInterface.php b/src/JsonSchema/Constraints/ConstraintInterface.php index f30164fe..8b1eadb2 100644 --- a/src/JsonSchema/Constraints/ConstraintInterface.php +++ b/src/JsonSchema/Constraints/ConstraintInterface.php @@ -50,9 +50,9 @@ function isValid(); * * @abstract * @param mixed $value - * @param null $schema - * @param null $path - * @param null $i + * @param mixed $schema + * @param mixed $path + * @param mixed $i */ function check($value, $schema = null, $path = null, $i = null); } \ No newline at end of file diff --git a/src/JsonSchema/Constraints/Enum.php b/src/JsonSchema/Constraints/Enum.php index a4ec8944..c80a2d55 100644 --- a/src/JsonSchema/Constraints/Enum.php +++ b/src/JsonSchema/Constraints/Enum.php @@ -27,8 +27,12 @@ public function check($element, $schema = null, $path = null, $i = null) return; } - if (!in_array($element, $schema->enum)) { - $this->addError($path, "does not have a value in the enumeration " . implode(', ', $schema->enum)); + foreach ($schema->enum as $enum) { + if ((gettype($element) === gettype($enum)) && ($element == $enum)) { + return; + } } + + $this->addError($path, "does not have a value in the enumeration " . print_r($schema->enum, true)); } } \ No newline at end of file diff --git a/src/JsonSchema/Constraints/Format.php b/src/JsonSchema/Constraints/Format.php index acc555f2..9262d6bd 100644 --- a/src/JsonSchema/Constraints/Format.php +++ b/src/JsonSchema/Constraints/Format.php @@ -35,16 +35,17 @@ public function check($element, $schema = null, $path = null, $i = null) case 'time': if (!$this->validateDateTime($element, 'H:i:s')) { - $this->addError($path, sprintf('Invalid time %s, expected format HH:MM:SS', json_encode($element))); + $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element))); } break; case 'date-time': if (!$this->validateDateTime($element, 'Y-m-d\TH:i:s\Z') && + !$this->validateDateTime($element, 'Y-m-d\TH:i:s.u\Z') && !$this->validateDateTime($element, 'Y-m-d\TH:i:sP') && !$this->validateDateTime($element, 'Y-m-d\TH:i:sO') ) { - $this->addError($path, sprintf('Invalid date time %s, expected format YYYY-MM-DDTHH:MM:SSZ or YYYY-MM-DDTHH:MM:SS+HH:MM', json_encode($element))); + $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element))); } break; @@ -55,8 +56,8 @@ public function check($element, $schema = null, $path = null, $i = null) break; case 'regex': - if (!$this->validateRegex($element, $schema->pattern)) { - $this->addError($path, "Invalid regex format"); + if (!$this->validateRegex($element)) { + $this->addError($path, 'Invalid regex format ' . $element); } break; @@ -125,9 +126,9 @@ protected function validateDateTime($datetime, $format) return $datetime === $dt->format($format); } - protected function validateRegex($string, $regex) + protected function validateRegex($regex) { - return preg_match('/' . $regex . '/', $string); + return false !== @preg_match('/' . $regex . '/', ''); } protected function validateColor($color) diff --git a/src/JsonSchema/Constraints/Number.php b/src/JsonSchema/Constraints/Number.php index 74a3111c..8bfd594c 100644 --- a/src/JsonSchema/Constraints/Number.php +++ b/src/JsonSchema/Constraints/Number.php @@ -23,20 +23,56 @@ class Number extends Constraint public function check($element, $schema = null, $path = null, $i = null) { // Verify minimum - if (isset($schema->minimum) && $element < $schema->minimum) { + if (isset($schema->exclusiveMinimum)) { + if (isset($schema->minimum)) { + if ($schema->exclusiveMinimum && $element === $schema->minimum) { + $this->addError($path, "must have a minimum value greater than boundary value of " . $schema->minimum); + } else if ($element < $schema->minimum) { + $this->addError($path, "must have a minimum value of " . $schema->minimum); + } + } else { + $this->addError($path, "use of exclusiveMinimum requires presence of minimum"); + } + } else if (isset($schema->minimum) && $element < $schema->minimum) { $this->addError($path, "must have a minimum value of " . $schema->minimum); } // Verify maximum - if (isset($schema->maximum) && $element > $schema->maximum) { + if (isset($schema->exclusiveMaximum)) { + if (isset($schema->maximum)) { + if ($schema->exclusiveMaximum && $element === $schema->maximum) { + $this->addError($path, "must have a maximum value less than boundary value of " . $schema->maximum); + } else if ($element > $schema->maximum) { + $this->addError($path, "must have a maximum value of " . $schema->maximum); + } + } else { + $this->addError($path, "use of exclusiveMaximum requires presence of maximum"); + } + } else if (isset($schema->maximum) && $element > $schema->maximum) { $this->addError($path, "must have a maximum value of " . $schema->maximum); } // Verify divisibleBy - if (isset($schema->divisibleBy) && $element % $schema->divisibleBy != 0) { + if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) { $this->addError($path, "is not divisible by " . $schema->divisibleBy); } $this->checkFormat($element, $schema, $path, $i); } -} \ No newline at end of file + + private function fmod($number1, $number2) + { + $modulus = fmod($number1, $number2); + $precision = abs(0.0000000001); + $diff = (float)($modulus - $number2); + + if (-$precision < $diff && $diff < $precision) { + return 0.0; + } + + $decimals1 = mb_strpos($number1, ".") ? mb_strlen($number1) - mb_strpos($number1, ".") - 1 : 0; + $decimals2 = mb_strpos($number2, ".") ? mb_strlen($number2) - mb_strpos($number2, ".") - 1 : 0; + + return (float)round($modulus, max($decimals1, $decimals2)); + } +} diff --git a/src/JsonSchema/Constraints/Type.php b/src/JsonSchema/Constraints/Type.php index 452f3249..fe7c0ec4 100644 --- a/src/JsonSchema/Constraints/Type.php +++ b/src/JsonSchema/Constraints/Type.php @@ -78,11 +78,11 @@ protected function validateType($value, $type) } if ('integer' === $type) { - return is_int($value) || ctype_digit($value); + return is_int($value); } if ('number' === $type) { - return is_numeric($value); + return is_numeric($value) && !is_string($value); } if ('boolean' === $type) { diff --git a/src/JsonSchema/Constraints/Undefined.php b/src/JsonSchema/Constraints/Undefined.php index 8e243aea..5316000d 100644 --- a/src/JsonSchema/Constraints/Undefined.php +++ b/src/JsonSchema/Constraints/Undefined.php @@ -9,8 +9,6 @@ namespace JsonSchema\Constraints; -use JsonSchema\Validator; - /** * The Undefined Constraints * @@ -40,10 +38,10 @@ public function check($value, $schema = null, $path = null, $i = null) /** * Validates the value against the types * - * @param $value - * @param null $schema - * @param null $path - * @param null $i + * @param mixed $value + * @param mixed $schema + * @param string $path + * @param string $i */ public function validateTypes($value, $schema = null, $path = null, $i = null) { @@ -82,25 +80,44 @@ public function validateTypes($value, $schema = null, $path = null, $i = null) /** * Validates common properties * - * @param $value - * @param null $schema - * @param null $path - * @param null $i + * @param mixed $value + * @param mixed $schema + * @param string $path + * @param string $i */ protected function validateCommonProperties($value, $schema = null, $path = null, $i = null) { // if it extends another schema, it must pass that schema as well if (isset($schema->extends)) { if (is_string($schema->extends)) { - $schema->extends = $this->validateUri($schema->extends, $schema, $path, $i); + $schema->extends = $this->validateUri($schema, $schema->extends); + } + $increment = is_null($i) ? "" : $i; + if (is_array($schema->extends)) { + foreach ($schema->extends as $extends) { + $this->checkUndefined($value, $extends, $path, $increment); + } + } else { + $this->checkUndefined($value, $schema->extends, $path, $increment); } - $this->checkUndefined($value, $schema->extends, $path, $i); } // Verify required values - if (is_object($value) && $value instanceof Undefined) { - if (isset($schema->required) && $schema->required) { - $this->addError($path, "is missing and it is required"); + if (is_object($value)) { + if ($value instanceof Undefined) { + // Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true} + if (isset($schema->required) && $schema->required) { + $this->addError($path, "is missing and it is required"); + } + } else if (isset($schema->required)) { + // Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...] + foreach ($schema->required as $required) { + if (!property_exists($value, $required)) { + $this->addError($path, "the property " . $required . " is required"); + } + } + } else { + $this->checkType($value, $schema, $path); } } else { $this->checkType($value, $schema, $path); @@ -110,7 +127,9 @@ protected function validateCommonProperties($value, $schema = null, $path = null if (isset($schema->disallow)) { $initErrors = $this->getErrors(); - $this->checkUndefined($value, $schema->disallow, $path); + $typeSchema = new \stdClass(); + $typeSchema->type = $schema->disallow; + $this->checkType($value, $typeSchema, $path); // if no new errors were raised it must be a disallowed value if (count($this->getErrors()) == count($initErrors)) { @@ -119,9 +138,45 @@ protected function validateCommonProperties($value, $schema = null, $path = null $this->errors = $initErrors; } } + + // Verify that dependencies are met + if (is_object($value) && isset($schema->dependencies)) { + $this->validateDependencies($value, $schema->dependencies, $path); + } + } + + /** + * Validate dependencies + * + * @param mixed $value + * @param mixed $dependencies + * @param string $path + */ + protected function validateDependencies($value, $dependencies, $path) + { + foreach ($dependencies as $key => $dependency) { + if (property_exists($value, $key)) { + if (is_string($dependency)) { + // Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"} + if (!property_exists($value, $dependency)) { + $this->addError($path, "$key depends on $dependency and $dependency is missing"); + } + } else if (is_array($dependency)) { + // Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]} + foreach ($dependency as $d) { + if (!property_exists($value, $d)) { + $this->addError($path, "$key depends on $d and $d is missing"); + } + } + } else if (is_object($dependency)) { + // Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}} + $this->checkUndefined($value, $dependency, $path, ""); + } + } + } } - protected function validateUri($schemaUri = null, $schema, $path = null, $i = null) + protected function validateUri($schema, $schemaUri = null) { $resolver = new \JsonSchema\Uri\UriResolver(); $retriever = $this->getUriRetriever(); diff --git a/tests/JsonSchema/Tests/Constraints/ArraysTest.php b/tests/JsonSchema/Tests/Constraints/ArraysTest.php index 18fc4134..1fef244c 100644 --- a/tests/JsonSchema/Tests/Constraints/ArraysTest.php +++ b/tests/JsonSchema/Tests/Constraints/ArraysTest.php @@ -28,6 +28,21 @@ public function getInvalidTests() } }' ), + array( + '{ + "array":[1,2,"a"] + }', + '{ + "type":"object", + "properties":{ + "array":{ + "type":"array", + "items":{"type":"number"}, + "additionalItems":{"type":"boolean"} + } + } + }' + ), array( '{ "array":[1,2,null] @@ -41,6 +56,19 @@ public function getInvalidTests() } } }' + ), + array( + '{"data": [1, 2, 3, "foo"]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": [], + "additionalItems": {"type": "integer"} + } + } + }' ) ); } @@ -74,6 +102,55 @@ public function getValidTests() } }' ), + array( + '{"data": [1, 2, 3, 4]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": [], + "additionalItems": {"type": "integer"} + } + } + }' + ), + array( + '{"data": [1, "foo", false]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": [] + } + } + }' + ), + array( + '{"data": [1, "foo", false]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": {} + } + } + }' + ), + array( + '{"data": [1, 2, 3, 4, 5]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "additionalItems": false + } + } + }' + ) ); } } diff --git a/tests/JsonSchema/Tests/Constraints/DependenciesTest.php b/tests/JsonSchema/Tests/Constraints/DependenciesTest.php new file mode 100644 index 00000000..daf9177b --- /dev/null +++ b/tests/JsonSchema/Tests/Constraints/DependenciesTest.php @@ -0,0 +1,148 @@ +format = 'regex'; - $schema->pattern = '\d+'; - $validator->check('10', $schema); + $validator->check('\d+', $schema); $this->assertEmpty($validator->getErrors()); - $validator->check('ten', $schema); + $validator->check('^(abc]', $schema); $this->assertCount(1, $validator->getErrors()); } @@ -73,8 +72,9 @@ public function getValidFormats() array('23:59:59', 'time'), array('2000-05-01T12:12:12Z', 'date-time'), - array('2000-05-01T12:12:12+0100', 'date-time'), + array('2000-05-01T12:12:12+0100', 'date-time'), array('2000-05-01T12:12:12+01:00', 'date-time'), + array('2000-05-01T12:12:12.123456Z', 'date-time'), array('0', 'utc-millisec'), diff --git a/tests/JsonSchema/Tests/Constraints/MinimumMaximumTest.php b/tests/JsonSchema/Tests/Constraints/MinimumMaximumTest.php index 107ab69f..7cc3f20e 100644 --- a/tests/JsonSchema/Tests/Constraints/MinimumMaximumTest.php +++ b/tests/JsonSchema/Tests/Constraints/MinimumMaximumTest.php @@ -25,6 +25,15 @@ public function getInvalidTests() } }' ), + array( + '{"value": 3}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "minimum": 3, "exclusiveMinimum": true} + } + }' + ), array( '{ "value":16 @@ -35,6 +44,50 @@ public function getInvalidTests() "value":{"type":"integer","maximum":8} } }' + ), + array( + '{"value": 8}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "maximum": 8, "exclusiveMaximum": true} + } + }' + ), + array( + '{"value": 4}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "exclusiveMinimum": true} + } + }' + ), + array( + '{"value": 4}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "exclusiveMaximum": true} + } + }' + ), + array( + '{"value": 4}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "minimum": 5, "exclusiveMinimum": false} + } + }' + ), + array( + '{"value": 4}', + '{ + "properties": { + "value": {"type": "integer", "maximum": 3, "exclusiveMaximum": false} + } + }' ) ); } @@ -63,6 +116,42 @@ public function getValidTests() "value":{"type":"integer","maximum":8} } }' + ), + array( + '{"value": 6}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "minimum": 6, "exclusiveMinimum": false} + } + }' + ), + array( + '{"value": 6}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "maximum": 6, "exclusiveMaximum": false} + } + }' + ), + array( + '{"value": 6}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "minimum": 6} + } + }' + ), + array( + '{"value": 6}', + '{ + "type": "object", + "properties": { + "value": {"type": "integer", "maximum": 6} + } + }' ) ); } diff --git a/tests/JsonSchema/Tests/Constraints/NumberAndIntegerTypesTest.php b/tests/JsonSchema/Tests/Constraints/NumberAndIntegerTypesTest.php index 6f66117e..932c2f56 100644 --- a/tests/JsonSchema/Tests/Constraints/NumberAndIntegerTypesTest.php +++ b/tests/JsonSchema/Tests/Constraints/NumberAndIntegerTypesTest.php @@ -16,14 +16,59 @@ public function getInvalidTests() return array( array( '{ - "number": 1.4 + "integer": 1.4 }', '{ "type":"object", "properties":{ - "number":{"type":"integer"} + "integer":{"type":"integer"} } }' + ), + array( + '{"number": "1.5"}', + '{ + "type": "object", + "properties": { + "number": {"type": "number"} + } + }' + ), + array( + '{"integer": "1"}', + '{ + "type": "object", + "properties": { + "integer": {"type": "integer"} + } + }' + ), + array( + '{"integer": 1.001}', + '{ + "type": "object", + "properties": { + "integer": {"type": "integer"} + } + }' + ), + array( + '{"integer": true}', + '{ + "type": "object", + "properties": { + "integer": {"type": "integer"} + } + }' + ), + array( + '{"number": "x"}', + '{ + "type": "object", + "properties": { + "number": {"type": "number"} + } + }' ) ); } @@ -33,12 +78,12 @@ public function getValidTests() return array( array( '{ - "number": 1 + "integer": 1 }', '{ "type":"object", "properties":{ - "number":{"type":"number"} + "integer":{"type":"integer"} } }' ), @@ -54,14 +99,21 @@ public function getValidTests() }' ), array( + '{"number": 1e5}', '{ - "number": "1.4" - }', + "type": "object", + "properties": { + "number": {"type": "number"} + } + }' + ), + array( + '{"number": 1}', '{ - "type":"object", - "properties":{ - "number":{"type":"number"} - } + "type": "object", + "properties": { + "number": {"type": "number"} + } }' ) ); diff --git a/tests/JsonSchema/Tests/Constraints/PatternTest.php b/tests/JsonSchema/Tests/Constraints/PatternTest.php index 2a18fc93..f28a5790 100644 --- a/tests/JsonSchema/Tests/Constraints/PatternTest.php +++ b/tests/JsonSchema/Tests/Constraints/PatternTest.php @@ -25,6 +25,16 @@ public function getInvalidTests() }, "additionalProperties":false }' + ), + array( + '{"value": "abc"}', + '{ + "type": "object", + "properties": { + "value": {"type": "string", "pattern": "^a*$"} + }, + "additionalProperties": false + }' ) ); } @@ -55,6 +65,16 @@ public function getValidTests() }, "additionalProperties":false }' + ), + array( + '{"value": "aaa"}', + '{ + "type": "object", + "properties": { + "value": {"type": "string", "pattern": "^a*$"} + }, + "additionalProperties": false + }' ) ); } diff --git a/tests/JsonSchema/Tests/Constraints/PhpTypeCastModeTest.php b/tests/JsonSchema/Tests/Constraints/PhpTypeCastModeTest.php deleted file mode 100644 index de49dd89..00000000 --- a/tests/JsonSchema/Tests/Constraints/PhpTypeCastModeTest.php +++ /dev/null @@ -1,104 +0,0 @@ - 'a', - 'message' => 'string value found, but a number is required' - ) - ) - ), - array( - '{ - "a":"9" - }', - '{ - "type":"object", - "properties":{ - "a":{"type":"integer","maximum":"8"} - } - }' - ) - ); - } - - public function getValidTests() - { - return array( - array( - '{ - "a":"7" - }', - '{ - "type":"object", - "properties":{ - "a":{"type":"integer","maximum":8} - } - }', - Validator::CHECK_MODE_TYPE_CAST - ), - array( - '{ - "a":1.337 - }', - '{ - "type":"object", - "properties":{ - "a":{"type":"number","maximum":8.0} - } - }', - Validator::CHECK_MODE_TYPE_CAST - ), - array( - '{ - "a":"1.337" - }', - '{ - "type":"object", - "properties":{ - "a":{"type":"number","maximum":8.0} - } - }', - Validator::CHECK_MODE_TYPE_CAST - ), - array( - '{ - "a":"9e42" - }', - '{ - "type":"object", - "properties":{ - "a":{"type":"number"} - } - }', - Validator::CHECK_MODE_TYPE_CAST - ), - ); - } -} diff --git a/tests/JsonSchema/Tests/Constraints/RequiredPropertyTest.php b/tests/JsonSchema/Tests/Constraints/RequiredPropertyTest.php index 6e46444b..8880ef31 100644 --- a/tests/JsonSchema/Tests/Constraints/RequiredPropertyTest.php +++ b/tests/JsonSchema/Tests/Constraints/RequiredPropertyTest.php @@ -19,9 +19,55 @@ public function getInvalidTests() '{ "type":"object", "properties":{ - "number":{"type":"string","required":true} + "number":{"type":"number","required":true} } }' + ), + array( + '{}', + '{ + "type": "object", + "properties": { + "number": {"type": "number"} + }, + "required": ["number"] + }' + ), + array( + '{ + "foo": {} + }', + '{ + "type": "object", + "properties": { + "foo": { + "type": "object", + "properties": { + "bar": {"type": "number"} + }, + "required": ["bar"] + } + } + }' + ), + array( + '{ + "bar": 1.4 + }', + '{ + "type": "object", + "properties": { + "foo": {"type": "string", "required": true}, + "bar": {"type": "number"} + }, + "required": ["bar"] + }' + ), + array( + '{}', + '{ + "required": ["foo"] + }' ) ); } @@ -31,12 +77,21 @@ public function getValidTests() return array( array( '{ - "number": "1.4" + "number": 1.4 }', '{ "type":"object", "properties":{ - "number":{"type":"string","required":true} + "number":{"type":"number","required":true} + } + }' + ), + array( + '{}', + '{ + "type":"object", + "properties":{ + "number":{"type":"number"} } }' ), @@ -45,14 +100,14 @@ public function getValidTests() '{ "type":"object", "properties":{ - "number":{"type":"string"} + "number":{"type":"number","required":false} } }' ), - array( + array( '{ - "number": 0 - }', + "number": 0 + }', '{ "type":"object", "properties":{ @@ -60,10 +115,10 @@ public function getValidTests() } }' ), - array( + array( '{ - "is_active": false - }', + "is_active": false + }', '{ "type":"object", "properties":{ @@ -71,10 +126,10 @@ public function getValidTests() } }' ), - array( + array( '{ - "status": null - }', + "status": null + }', '{ "type":"object", "properties":{ @@ -82,16 +137,48 @@ public function getValidTests() } }' ), - array( + array( '{ - "users": [] - }', + "users": [] + }', '{ "type":"object", "properties":{ "users":{"type":"array","required":true} } }' + ), + array( + '{ + "foo": "foo", + "bar": 1.4 + }', + '{ + "type": "object", + "properties": { + "foo": {"type": "string", "required": true}, + "bar": {"type": "number"} + }, + "required": ["bar"] + }' + ), + array( + '{ + "foo": {"bar": 1.5} + }', + '{ + "type": "object", + "properties": { + "foo": { + "type": "object", + "properties": { + "bar": {"type": "number"} + }, + "required": ["bar"] + } + }, + "required": ["foo"] + }' ) ); } diff --git a/tests/JsonSchema/Tests/Constraints/TupleTypingTest.php b/tests/JsonSchema/Tests/Constraints/TupleTypingTest.php index 5488d247..ceab8ec6 100644 --- a/tests/JsonSchema/Tests/Constraints/TupleTypingTest.php +++ b/tests/JsonSchema/Tests/Constraints/TupleTypingTest.php @@ -33,7 +33,7 @@ public function getInvalidTests() ), array( '{ - "tupleTyping":["2",2,3] + "tupleTyping":["2",2,true] }', '{ "type":"object", @@ -44,7 +44,7 @@ public function getInvalidTests() {"type":"string"}, {"type":"number"} ] , - "additionalProperties":false + "additionalItems":false } } }' @@ -62,14 +62,33 @@ public function getInvalidTests() {"type":"string"}, {"type":"number"} ] , - "additionalProperties":{"type":"string"} + "additionalItems":{"type":"string"} } } }' ), + array( + '{"data": [1, "foo", true, 1.5]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": [{}, {}, {}], + "additionalItems": false + } + } + }' + ) + ); + } + + public function getValidTests() + { + return array( array( '{ - "tupleTyping":["2"] + "tupleTyping":["2", 1] }', '{ "type":"object", @@ -78,22 +97,15 @@ public function getInvalidTests() "type":"array", "items":[ {"type":"string"}, - {"type":"number"}, - {"required":true} + {"type":"number"} ] } } }' - ) - ); - } - - public function getValidTests() - { - return array( + ), array( '{ - "tupleTyping":["2"] + "tupleTyping":["2",2,3] }', '{ "type":"object", @@ -102,12 +114,24 @@ public function getValidTests() "type":"array", "items":[ {"type":"string"}, - {"type":"number","required":false}, - {"type":"number","required":false} + {"type":"number"} ] } } }' + ), + array( + '{"data": [1, "foo", true]}', + '{ + "type": "object", + "properties": { + "data": { + "type": "array", + "items": [{}, {}, {}], + "additionalItems": false + } + } + }' ) ); } diff --git a/tests/JsonSchema/Tests/Constraints/UniqueItemsTest.php b/tests/JsonSchema/Tests/Constraints/UniqueItemsTest.php index 24e6e22b..3006bb43 100644 --- a/tests/JsonSchema/Tests/Constraints/UniqueItemsTest.php +++ b/tests/JsonSchema/Tests/Constraints/UniqueItemsTest.php @@ -21,13 +21,41 @@ public function getInvalidTests() "uniqueItems": true }' ), -/* array( + array( '[{"a":"b"},{"a":"c"},{"a":"b"}]', '{ "type":"array", "uniqueItems": true }' - )*/ + ), + array( + '[{"foo": {"bar" : {"baz" : true}}}, {"foo": {"bar" : {"baz" : true}}}]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[1.0, 1.00, 1]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[["foo"], ["foo"]]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[{}, [1], true, null, {}, 1]', + '{ + "type": "array", + "uniqueItems": true + }' + ) ); } @@ -40,6 +68,48 @@ public function getValidTests() "type":"array", "uniqueItems": true }' + ), + array( + '[{"foo": 12}, {"bar": false}]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[1, true]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[0, false]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[{"foo": {"bar" : {"baz" : true}}}, {"foo": {"bar" : {"baz" : false}}}]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[["foo"], ["bar"]]', + '{ + "type": "array", + "uniqueItems": true + }' + ), + array( + '[{}, [1], true, null, 1]', + '{ + "type": "array", + "uniqueItems": true + }' ) ); }