From 9abd093ad84d7d830e3a950b086effa2c528a709 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Fri, 2 May 2025 12:23:56 +0200 Subject: [PATCH 01/14] feat: update to latest json schema test suite --- composer.json | 6 +-- tests/JsonSchemaTestSuite.php | 92 +++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 tests/JsonSchemaTestSuite.php diff --git a/composer.json b/composer.json index 8229d635..e8e847e6 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "3.3.0", - "json-schema/json-schema-test-suite": "1.2.0", + "json-schema/json-schema-test-suite": "^23.2", "phpunit/phpunit": "^8.5", "phpspec/prophecy": "^1.19", "phpstan/phpstan": "^1.12", @@ -59,11 +59,11 @@ "type": "package", "package": { "name": "json-schema/json-schema-test-suite", - "version": "1.2.0", + "version": "23.2.0", "source": { "type": "git", "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", - "reference": "1.2.0" + "reference": "23.2.0" } } } diff --git a/tests/JsonSchemaTestSuite.php b/tests/JsonSchemaTestSuite.php new file mode 100644 index 00000000..02df2a89 --- /dev/null +++ b/tests/JsonSchemaTestSuite.php @@ -0,0 +1,92 @@ +addSchema('internal://mySchema', $schema); + $this->loadRemotesIntoStorage($schemaStorage); + $validator = new Validator(new Factory($schemaStorage)); + + $result = $validator->validate($data, $schema); + + self::assertEquals($expectedValidationResult, count($validator->getErrors()) === 0); + } + + public function casesDataProvider(): \Generator + { + $testDir = __DIR__ . '/../vendor/json-schema/json-schema-test-suite/tests'; + $drafts = array_filter(glob($testDir . '/*'), is_dir(...)); + $skippedDrafts = ['draft4', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; + + foreach ($drafts as $draft) { + $files = glob($draft . '/*.json'); + if (in_array(basename($draft), $skippedDrafts, true)) { + continue; + } + + foreach ($files as $file) { + $contents = json_decode(file_get_contents($file), false); + foreach ($contents as $testCase) { + foreach ($testCase->tests as $test) { + $name = sprintf( + '[%s/%s]: %s: %s is expected to be %s', + basename($draft), + basename($file), + $testCase->description, + $test->description, + $test->valid ? 'valid' : 'invalid', + ); + + yield $name => [ + 'testCaseDescription' => $testCase->description, + 'testDescription' => $test->description, + 'schema' => $testCase->schema, + 'data' => $test->data, + 'expectedValidationResult' => $test->valid, + ]; + } + + } + } + } + } + + private function loadRemotesIntoStorage(SchemaStorageInterface $storage): void + { + $remotesDir = __DIR__ . '/../vendor/json-schema/json-schema-test-suite/remotes'; + + $directory = new \RecursiveDirectoryIterator($remotesDir); + $iterator = new \RecursiveIteratorIterator($directory); + + foreach ($iterator as $info) { + if (!$info->isFile()) { + continue; + } + + $id = str_replace($remotesDir, 'http://localhost:1234', $info->getPathname()); + $storage->addSchema($id, json_decode(file_get_contents($info->getPathname()), false)); + } + } + +} From b3573006575eef7f33dac782893f67c62f212063 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Fri, 2 May 2025 13:14:32 +0200 Subject: [PATCH 02/14] test: improve test --- tests/JsonSchemaTestSuite.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/JsonSchemaTestSuite.php b/tests/JsonSchemaTestSuite.php index 02df2a89..1b378707 100644 --- a/tests/JsonSchemaTestSuite.php +++ b/tests/JsonSchemaTestSuite.php @@ -24,7 +24,7 @@ public function testIt( ): void { $schemaStorage = new SchemaStorage(); - $schemaStorage->addSchema('internal://mySchema', $schema); + $schemaStorage->addSchema(property_exists($schema, 'id') ? $schema->id : SchemaStorage::INTERNAL_PROVIDED_SCHEMA_URI, $schema); $this->loadRemotesIntoStorage($schemaStorage); $validator = new Validator(new Factory($schemaStorage)); @@ -36,7 +36,9 @@ public function testIt( public function casesDataProvider(): \Generator { $testDir = __DIR__ . '/../vendor/json-schema/json-schema-test-suite/tests'; - $drafts = array_filter(glob($testDir . '/*'), is_dir(...)); + $drafts = array_filter(glob($testDir . '/*'), static function (string $filename) { + return is_dir($filename); + }); $skippedDrafts = ['draft4', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; foreach ($drafts as $draft) { From 7dcf6fd3d06702953464078d50811cd1b018c8a3 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Fri, 2 May 2025 14:19:26 +0200 Subject: [PATCH 03/14] test: correct test regression due to added tests --- phpunit.xml.dist | 4 ++ tests/Constraints/BaseTestCase.php | 12 +++++- .../Constraints/NumberAndIntegerTypesTest.php | 42 ++++++++----------- tests/Drafts/Draft3Test.php | 1 + tests/Drafts/Draft4Test.php | 5 +++ tests/JsonSchemaTestSuite.php | 38 ++++++++++++++--- 6 files changed, 72 insertions(+), 30 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f49455f2..bcae9ec7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -24,4 +24,8 @@ ./src/JsonSchema/ + + + + diff --git a/tests/Constraints/BaseTestCase.php b/tests/Constraints/BaseTestCase.php index f65037d6..d63251df 100644 --- a/tests/Constraints/BaseTestCase.php +++ b/tests/Constraints/BaseTestCase.php @@ -124,7 +124,7 @@ public function testValidCasesUsingAssoc($input, $schema, $checkMode = Constrain $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $errorMask = $validator->validate($value, $schema); - $this->assertEquals(0, $errorMask); + $this->assertEquals(0, $errorMask, $this->validatorErrorsToString($validator)); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } @@ -141,4 +141,14 @@ public function getInvalidForAssocTests(): Generator { yield from $this->getInvalidTests(); } + + private function validatorErrorsToString(Validator $validator): string + { + return implode( + ', ', + array_map( + static function (array $error) { return $error['message']; }, $validator->getErrors() + ) + ); + } } diff --git a/tests/Constraints/NumberAndIntegerTypesTest.php b/tests/Constraints/NumberAndIntegerTypesTest.php index c09abee8..7469dd9a 100644 --- a/tests/Constraints/NumberAndIntegerTypesTest.php +++ b/tests/Constraints/NumberAndIntegerTypesTest.php @@ -12,10 +12,8 @@ class NumberAndIntegerTypesTest extends BaseTestCase public function getInvalidTests(): \Generator { yield [ - '{ - "integer": 1.4 - }', - '{ + 'input' => '{ "integer": 1.4 }', + 'schema' => '{ "type":"object", "properties":{ "integer":{"type":"integer"} @@ -23,8 +21,8 @@ public function getInvalidTests(): \Generator }' ]; yield [ - '{"integer": 1.001}', - '{ + 'input' => '{"integer": 1.001}', + 'schema' => '{ "type": "object", "properties": { "integer": {"type": "integer"} @@ -32,8 +30,8 @@ public function getInvalidTests(): \Generator }' ]; yield [ - '{"integer": true}', - '{ + 'input' => '{"integer": true}', + 'schema' => '{ "type": "object", "properties": { "integer": {"type": "integer"} @@ -41,8 +39,8 @@ public function getInvalidTests(): \Generator }' ]; yield [ - '{"number": "x"}', - '{ + 'input' => '{"number": "x"}', + 'schema' => '{ "type": "object", "properties": { "number": {"type": "number"} @@ -54,10 +52,8 @@ public function getInvalidTests(): \Generator public function getValidTests(): \Generator { yield [ - '{ - "integer": 1 - }', - '{ + 'input' => '{ "integer": 1 }', + 'schema' => '{ "type":"object", "properties":{ "integer":{"type":"integer"} @@ -65,10 +61,8 @@ public function getValidTests(): \Generator }' ]; yield [ - '{ - "number": 1.4 - }', - '{ + 'input' => '{ "number": 1.4 }', + 'schema' => '{ "type":"object", "properties":{ "number":{"type":"number"} @@ -76,8 +70,8 @@ public function getValidTests(): \Generator }' ]; yield [ - '{"number": 1e5}', - '{ + 'input' => '{"number": 1e5}', + 'schema' => '{ "type": "object", "properties": { "number": {"type": "number"} @@ -85,8 +79,8 @@ public function getValidTests(): \Generator }' ]; yield [ - '{"number": 1}', - '{ + 'input' => '{"number": 1}', + 'schema' => '{ "type": "object", "properties": { "number": {"type": "number"} @@ -95,8 +89,8 @@ public function getValidTests(): \Generator }' ]; yield [ - '{"number": -49.89}', - '{ + 'input' => '{"number": -49.89}', + 'schema' => '{ "type": "object", "properties": { "number": { diff --git a/tests/Drafts/Draft3Test.php b/tests/Drafts/Draft3Test.php index 4ed6f76c..a846e9b0 100644 --- a/tests/Drafts/Draft3Test.php +++ b/tests/Drafts/Draft3Test.php @@ -113,6 +113,7 @@ protected function getSkippedTests(): array return [ // Optional 'bignum.json', + 'ecmascript-regex.json', 'format.json', 'jsregex.json', 'zeroTerminatedFloats.json' diff --git a/tests/Drafts/Draft4Test.php b/tests/Drafts/Draft4Test.php index dbf1354a..0a9a2bc5 100644 --- a/tests/Drafts/Draft4Test.php +++ b/tests/Drafts/Draft4Test.php @@ -38,6 +38,9 @@ public function getInvalidForAssocTests(): \Generator public function getValidForAssocTests(): \Generator { $skip = [ + 'minProperties.json / minProperties validation / ignores arrays', + 'required.json / required properties whose names are Javascript object property names / ignores arrays', + 'required.json / required validation / ignores arrays', 'type.json / object type matches objects / an array is not an object', 'type.json / array type matches arrays / an object is not an array', ]; @@ -58,7 +61,9 @@ protected function getSkippedTests(): array return [ // Optional 'bignum.json', + 'ecmascript-regex.json', 'format.json', + 'float-overflow.json', 'zeroTerminatedFloats.json', // Required 'not.json' // only one test case failing diff --git a/tests/JsonSchemaTestSuite.php b/tests/JsonSchemaTestSuite.php index 1b378707..e8eb77c5 100644 --- a/tests/JsonSchemaTestSuite.php +++ b/tests/JsonSchemaTestSuite.php @@ -4,11 +4,14 @@ namespace JsonSchema\Tests; +use CallbackFilterIterator; use JsonSchema\Constraints\Factory; use JsonSchema\SchemaStorage; use JsonSchema\SchemaStorageInterface; use JsonSchema\Validator; use PHPUnit\Framework\TestCase; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; class JsonSchemaTestSuite extends TestCase { @@ -33,28 +36,53 @@ public function testIt( self::assertEquals($expectedValidationResult, count($validator->getErrors()) === 0); } + public function testItOnce(): void + { + $schema = json_decode('{ "required": ["__proto__", "toString", "constructor"] }', false); + $data = []; + + $schemaStorage = new SchemaStorage(); + $schemaStorage->addSchema(SchemaStorage::INTERNAL_PROVIDED_SCHEMA_URI, $schema); + $this->loadRemotesIntoStorage($schemaStorage); + $validator = new Validator(new Factory($schemaStorage)); + + $result = $validator->validate($data, $schema); + + self::assertEquals(true, count($validator->getErrors()) === 0); + } + + public function casesDataProvider(): \Generator { $testDir = __DIR__ . '/../vendor/json-schema/json-schema-test-suite/tests'; $drafts = array_filter(glob($testDir . '/*'), static function (string $filename) { return is_dir($filename); }); - $skippedDrafts = ['draft4', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; + $skippedDrafts = ['draft3', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; foreach ($drafts as $draft) { - $files = glob($draft . '/*.json'); if (in_array(basename($draft), $skippedDrafts, true)) { continue; } + $files = new CallbackFilterIterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($draft) + ), + function ($file) { + return $file->isFile() && strtolower($file->getExtension()) === 'json'; + } + ); + /** @var \SplFileInfo $file */ foreach ($files as $file) { - $contents = json_decode(file_get_contents($file), false); + $contents = json_decode(file_get_contents($file->getPathname()), false); foreach ($contents as $testCase) { foreach ($testCase->tests as $test) { $name = sprintf( - '[%s/%s]: %s: %s is expected to be %s', + '[%s/%s%s]: %s: %s is expected to be %s', basename($draft), - basename($file), + str_contains($file->getPathname(), 'optional') ? 'optional/' : '', + $file->getBasename(), $testCase->description, $test->description, $test->valid ? 'valid' : 'invalid', From 84f681b0af47789c73cf4b51b052171c6dc93f08 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Fri, 2 May 2025 15:22:47 +0200 Subject: [PATCH 04/14] test: correct test regression due to newly introduced tests --- tests/Drafts/Draft3Test.php | 14 +++++++++++ tests/Drafts/Draft4Test.php | 44 +++++++++++++++++++++++++++++++++++ tests/JsonSchemaTestSuite.php | 20 ++-------------- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/tests/Drafts/Draft3Test.php b/tests/Drafts/Draft3Test.php index a846e9b0..2d1202e7 100644 --- a/tests/Drafts/Draft3Test.php +++ b/tests/Drafts/Draft3Test.php @@ -75,6 +75,20 @@ protected function getFilePaths(): array ]; } + public function getInvalidTests(): \Generator + { + $skip = [ + 'ref.json / $ref prevents a sibling id from changing the base uri / $ref resolves to /definitions/base_foo, data does not validate' + ]; + + foreach (parent::getInvalidTests() as $name => $testcase) { + if (in_array($name, $skip, true)) { + continue; + } + yield $name => $testcase; + } + } + public function getInvalidForAssocTests(): \Generator { $skip = [ diff --git a/tests/Drafts/Draft4Test.php b/tests/Drafts/Draft4Test.php index 0a9a2bc5..23e08ca5 100644 --- a/tests/Drafts/Draft4Test.php +++ b/tests/Drafts/Draft4Test.php @@ -20,9 +20,32 @@ protected function getFilePaths(): array ]; } + public function getInvalidTests(): \Generator + { + $skip = [ + 'id.json / id inside an enum is not a real identifier / no match on enum or $ref to id', + 'ref.json / $ref prevents a sibling id from changing the base uri / $ref resolves to /definitions/base_foo, data does not validate', + 'ref.json / Recursive references between schemas / invalid tree', + 'ref.json / refs with quote / object with strings is invalid', + 'ref.json / Location-independent identifier / mismatch', + 'ref.json / Location-independent identifier with base URI change in subschema / mismatch', + 'ref.json / empty tokens in $ref json-pointer / non-number is invalid', + 'ref.json / id must be resolved against nearest parent, not just immediate parent / non-number is invalid', + 'refRemote.json / Location-independent identifier in remote ref / string is invalid', + ]; + + foreach (parent::getInvalidTests() as $name => $testcase) { + if (in_array($name, $skip, true)) { + continue; + } + yield $name => $testcase; + } + } + public function getInvalidForAssocTests(): \Generator { $skip = [ + 'ref.json / Recursive references between schemas / valid tree', 'type.json / object type matches objects / an array is not an object', 'type.json / array type matches arrays / an object is not an array', ]; @@ -35,6 +58,27 @@ public function getInvalidForAssocTests(): \Generator } } + public function getValidTests(): \Generator + { + $skip = [ + 'ref.json / $ref prevents a sibling id from changing the base uri / $ref resolves to /definitions/base_foo, data validates', + 'ref.json / Recursive references between schemas / valid tree', + 'ref.json / refs with quote / object with numbers is valid', + 'ref.json / Location-independent identifier / match', + 'ref.json / Location-independent identifier with base URI change in subschema / match', + 'ref.json / empty tokens in $ref json-pointer / number is valid', + 'ref.json / naive replacement of $ref with its destination is not correct / match the enum exactly', + 'refRemote.json / Location-independent identifier in remote ref / integer is valid', + ]; + + foreach (parent::getValidTests() as $name => $testcase) { + if (in_array($name, $skip, true)) { + continue; + } + yield $name => $testcase; + } + } + public function getValidForAssocTests(): \Generator { $skip = [ diff --git a/tests/JsonSchemaTestSuite.php b/tests/JsonSchemaTestSuite.php index e8eb77c5..d08c8d4d 100644 --- a/tests/JsonSchemaTestSuite.php +++ b/tests/JsonSchemaTestSuite.php @@ -36,29 +36,13 @@ public function testIt( self::assertEquals($expectedValidationResult, count($validator->getErrors()) === 0); } - public function testItOnce(): void - { - $schema = json_decode('{ "required": ["__proto__", "toString", "constructor"] }', false); - $data = []; - - $schemaStorage = new SchemaStorage(); - $schemaStorage->addSchema(SchemaStorage::INTERNAL_PROVIDED_SCHEMA_URI, $schema); - $this->loadRemotesIntoStorage($schemaStorage); - $validator = new Validator(new Factory($schemaStorage)); - - $result = $validator->validate($data, $schema); - - self::assertEquals(true, count($validator->getErrors()) === 0); - } - - public function casesDataProvider(): \Generator { $testDir = __DIR__ . '/../vendor/json-schema/json-schema-test-suite/tests'; $drafts = array_filter(glob($testDir . '/*'), static function (string $filename) { return is_dir($filename); }); - $skippedDrafts = ['draft3', 'draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; + $skippedDrafts = ['draft6', 'draft7', 'draft2019-09', 'draft2020-12', 'draft-next', 'latest']; foreach ($drafts as $draft) { if (in_array(basename($draft), $skippedDrafts, true)) { @@ -85,7 +69,7 @@ function ($file) { $file->getBasename(), $testCase->description, $test->description, - $test->valid ? 'valid' : 'invalid', + $test->valid ? 'valid' : 'invalid' ); yield $name => [ From 8066ed33a923849fb22baac408328c8e582b0d25 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Wed, 7 May 2025 20:55:38 +0200 Subject: [PATCH 05/14] fix: correct test suite test case --- tests/Constraints/BaseTestCase.php | 2 +- ...estSuite.php => JsonSchemaTestSuiteTest.php} | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) rename tests/{JsonSchemaTestSuite.php => JsonSchemaTestSuiteTest.php} (87%) diff --git a/tests/Constraints/BaseTestCase.php b/tests/Constraints/BaseTestCase.php index d63251df..e3b595db 100644 --- a/tests/Constraints/BaseTestCase.php +++ b/tests/Constraints/BaseTestCase.php @@ -38,7 +38,7 @@ public function testInvalidCases(string $input, string $schema, ?int $checkMode $checkValue = json_decode($input, false); $errorMask = $validator->validate($checkValue, $schema); - $this->assertTrue((bool) ($errorMask & Validator::ERROR_DOCUMENT_VALIDATION)); + $this->assertTrue((bool) ($errorMask & Validator::ERROR_DOCUMENT_VALIDATION), 'Document is invalid'); $this->assertGreaterThan(0, $validator->numErrors()); if ([] !== $errors) { diff --git a/tests/JsonSchemaTestSuite.php b/tests/JsonSchemaTestSuiteTest.php similarity index 87% rename from tests/JsonSchemaTestSuite.php rename to tests/JsonSchemaTestSuiteTest.php index d08c8d4d..d3e3bb1b 100644 --- a/tests/JsonSchemaTestSuite.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -13,17 +13,18 @@ use RecursiveDirectoryIterator; use RecursiveIteratorIterator; -class JsonSchemaTestSuite extends TestCase +class JsonSchemaTestSuiteTest extends TestCase { /** * @dataProvider casesDataProvider */ - public function testIt( + public function testTestCaseValidatesCorrectly( string $testCaseDescription, string $testDescription, \stdClass $schema, mixed $data, - bool $expectedValidationResult + bool $expectedValidationResult, + bool $optional ): void { $schemaStorage = new SchemaStorage(); @@ -31,7 +32,12 @@ public function testIt( $this->loadRemotesIntoStorage($schemaStorage); $validator = new Validator(new Factory($schemaStorage)); - $result = $validator->validate($data, $schema); + $validator->validate($data, $schema); + + if ($optional && $expectedValidationResult !== (count($validator->getErrors()) === 0)) { + $this->markTestSkipped('Optional test case would fail'); + return; + } self::assertEquals($expectedValidationResult, count($validator->getErrors()) === 0); } @@ -65,7 +71,7 @@ function ($file) { $name = sprintf( '[%s/%s%s]: %s: %s is expected to be %s', basename($draft), - str_contains($file->getPathname(), 'optional') ? 'optional/' : '', + str_contains($file->getPathname(), '/optional/') ? 'optional/' : '', $file->getBasename(), $testCase->description, $test->description, @@ -78,6 +84,7 @@ function ($file) { 'schema' => $testCase->schema, 'data' => $test->data, 'expectedValidationResult' => $test->valid, + 'optional' => str_contains($file->getPathname(), '/optional/') ]; } From 07ea7d27d8ddc057eefdc847f2493635a82a01f3 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 13 May 2025 21:22:00 +0200 Subject: [PATCH 06/14] fix: correct mixed usage in newly added tests --- tests/JsonSchemaTestSuiteTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/JsonSchemaTestSuiteTest.php b/tests/JsonSchemaTestSuiteTest.php index d3e3bb1b..ae944abe 100644 --- a/tests/JsonSchemaTestSuiteTest.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -17,12 +17,14 @@ class JsonSchemaTestSuiteTest extends TestCase { /** * @dataProvider casesDataProvider + * + * @param mixed $data */ public function testTestCaseValidatesCorrectly( string $testCaseDescription, string $testDescription, \stdClass $schema, - mixed $data, + $data, bool $expectedValidationResult, bool $optional ): void From f58b705c8c4a11e1c18669d1f3b198c5905d3532 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 13 May 2025 21:37:49 +0200 Subject: [PATCH 07/14] fix: add try-catch to skip optional test cases --- tests/JsonSchemaTestSuiteTest.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/JsonSchemaTestSuiteTest.php b/tests/JsonSchemaTestSuiteTest.php index ae944abe..4fb5caa8 100644 --- a/tests/JsonSchemaTestSuiteTest.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -34,7 +34,16 @@ public function testTestCaseValidatesCorrectly( $this->loadRemotesIntoStorage($schemaStorage); $validator = new Validator(new Factory($schemaStorage)); - $validator->validate($data, $schema); + try { + $validator->validate($data, $schema); + } catch (\Exception $e) { + if ($optional) { + $this->markTestSkipped('Optional test case would during validate() invocation'); + return; + } + + throw $e; + } if ($optional && $expectedValidationResult !== (count($validator->getErrors()) === 0)) { $this->markTestSkipped('Optional test case would fail'); From eb69f3b6fd861fed68317d15181f35f9adc3b90c Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 27 May 2025 21:45:23 +0200 Subject: [PATCH 08/14] refactor: remove unneeded return --- tests/JsonSchemaTestSuiteTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/JsonSchemaTestSuiteTest.php b/tests/JsonSchemaTestSuiteTest.php index 4fb5caa8..aff7f451 100644 --- a/tests/JsonSchemaTestSuiteTest.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -39,7 +39,6 @@ public function testTestCaseValidatesCorrectly( } catch (\Exception $e) { if ($optional) { $this->markTestSkipped('Optional test case would during validate() invocation'); - return; } throw $e; @@ -47,7 +46,6 @@ public function testTestCaseValidatesCorrectly( if ($optional && $expectedValidationResult !== (count($validator->getErrors()) === 0)) { $this->markTestSkipped('Optional test case would fail'); - return; } self::assertEquals($expectedValidationResult, count($validator->getErrors()) === 0); From 8d298df4e790816d5c4e1149f14a40f5fb1d5244 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Wed, 11 Jun 2025 08:27:22 +0200 Subject: [PATCH 09/14] fix: setup default to provided schema id and only fallback when not available --- tests/Constraints/BaseTestCase.php | 20 ++++++++++++-------- tests/Constraints/VeryBaseTestCase.php | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/Constraints/BaseTestCase.php b/tests/Constraints/BaseTestCase.php index e3b595db..494d80b6 100644 --- a/tests/Constraints/BaseTestCase.php +++ b/tests/Constraints/BaseTestCase.php @@ -28,8 +28,9 @@ public function testInvalidCases(string $input, string $schema, ?int $checkMode $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } - $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema, false))); - $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); + $schema = json_decode($schema, false); + $schemaStorage = new SchemaStorage($this->getUriRetrieverMock($schema)); + $schema = $schemaStorage->getSchema($schema->id ?? 'http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } @@ -60,8 +61,9 @@ public function testInvalidCasesUsingAssoc($input, $schema, $checkMode = Constra $this->markTestSkipped('Test indicates that it is not for "CHECK_MODE_TYPE_CAST"'); } - $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); - $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); + $schema = json_decode($schema, false); + $schemaStorage = new SchemaStorage($this->getUriRetrieverMock($schema)); + $schema = $schemaStorage->getSchema($schema->id ?? 'http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } @@ -87,8 +89,10 @@ public function testValidCases($input, $schema, $checkMode = Constraint::CHECK_M if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } - $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema, false))); - $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); + + $schema = json_decode($schema, false); + $schemaStorage = new SchemaStorage($this->getUriRetrieverMock($schema)); + $schema = $schemaStorage->getSchema($schema->id ?? 'http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } @@ -113,9 +117,9 @@ public function testValidCasesUsingAssoc($input, $schema, $checkMode = Constrain $this->markTestSkipped('Test indicates that it is not for "CHECK_MODE_TYPE_CAST"'); } - $schema = json_decode($schema); + $schema = json_decode($schema, false); $schemaStorage = new SchemaStorage($this->getUriRetrieverMock($schema), new UriResolver()); - $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); + $schema = $schemaStorage->getSchema($schema->id ?? 'http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } diff --git a/tests/Constraints/VeryBaseTestCase.php b/tests/Constraints/VeryBaseTestCase.php index 55d0672d..baf04ffe 100644 --- a/tests/Constraints/VeryBaseTestCase.php +++ b/tests/Constraints/VeryBaseTestCase.php @@ -20,7 +20,7 @@ abstract class VeryBaseTestCase extends TestCase protected function getUriRetrieverMock(?object $schema): object { $uriRetriever = $this->prophesize(UriRetrieverInterface::class); - $uriRetriever->retrieve('http://www.my-domain.com/schema.json') + $uriRetriever->retrieve($schema->id ?? 'http://www.my-domain.com/schema.json') ->willReturn($schema) ->shouldBeCalled(); From 1a1228ef08a900766c945a2b17d9b31c69314e19 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Wed, 11 Jun 2025 08:27:41 +0200 Subject: [PATCH 10/14] refactor: add missing type hints --- tests/Constraints/BaseTestCase.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/Constraints/BaseTestCase.php b/tests/Constraints/BaseTestCase.php index 494d80b6..63b63a42 100644 --- a/tests/Constraints/BaseTestCase.php +++ b/tests/Constraints/BaseTestCase.php @@ -19,7 +19,7 @@ abstract class BaseTestCase extends VeryBaseTestCase /** * @dataProvider getInvalidTests * - * @param int-mask-of $checkMode + * @param ?int-mask-of $checkMode */ public function testInvalidCases(string $input, string $schema, ?int $checkMode = Constraint::CHECK_MODE_NORMAL, array $errors = []): void { @@ -50,8 +50,10 @@ public function testInvalidCases(string $input, string $schema, ?int $checkMode /** * @dataProvider getInvalidForAssocTests + * + * @param ?int-mask-of $checkMode */ - public function testInvalidCasesUsingAssoc($input, $schema, $checkMode = Constraint::CHECK_MODE_TYPE_CAST, $errors = []): void + public function testInvalidCasesUsingAssoc(string $input, string $schema, ?int $checkMode = Constraint::CHECK_MODE_TYPE_CAST, array $errors = []): void { $checkMode = $checkMode ?? Constraint::CHECK_MODE_TYPE_CAST; if ($this->validateSchema) { @@ -83,8 +85,10 @@ public function testInvalidCasesUsingAssoc($input, $schema, $checkMode = Constra /** * @dataProvider getValidTests + * + * @param ?int-mask-of $checkMode */ - public function testValidCases($input, $schema, $checkMode = Constraint::CHECK_MODE_NORMAL): void + public function testValidCases(string $input, string $schema, ?int $checkMode = Constraint::CHECK_MODE_NORMAL): void { if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; @@ -107,8 +111,10 @@ public function testValidCases($input, $schema, $checkMode = Constraint::CHECK_M /** * @dataProvider getValidForAssocTests + * + * @param ?int-mask-of $checkMode */ - public function testValidCasesUsingAssoc($input, $schema, $checkMode = Constraint::CHECK_MODE_TYPE_CAST): void + public function testValidCasesUsingAssoc(string $input, string $schema, ?int $checkMode = Constraint::CHECK_MODE_TYPE_CAST): void { if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; From 5c92ede58f71c0333cbc60e5ff800ad84dcdfb88 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 24 Jun 2025 11:52:59 +0200 Subject: [PATCH 11/14] test: Skip failling test which where introduced after json-schema-test-suite v1.2.0 --- tests/Drafts/Draft4Test.php | 3 +++ tests/JsonSchemaTestSuiteTest.php | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/tests/Drafts/Draft4Test.php b/tests/Drafts/Draft4Test.php index 23e08ca5..111d1c24 100644 --- a/tests/Drafts/Draft4Test.php +++ b/tests/Drafts/Draft4Test.php @@ -32,6 +32,7 @@ public function getInvalidTests(): \Generator 'ref.json / empty tokens in $ref json-pointer / non-number is invalid', 'ref.json / id must be resolved against nearest parent, not just immediate parent / non-number is invalid', 'refRemote.json / Location-independent identifier in remote ref / string is invalid', + 'refRemote.json / base URI change - change folder / string is invalid' ]; foreach (parent::getInvalidTests() as $name => $testcase) { @@ -68,7 +69,9 @@ public function getValidTests(): \Generator 'ref.json / Location-independent identifier with base URI change in subschema / match', 'ref.json / empty tokens in $ref json-pointer / number is valid', 'ref.json / naive replacement of $ref with its destination is not correct / match the enum exactly', + 'ref.json / id must be resolved against nearest parent, not just immediate parent / number is valid', 'refRemote.json / Location-independent identifier in remote ref / integer is valid', + 'refRemote.json / base URI change - change folder / number is valid', ]; foreach (parent::getValidTests() as $name => $testcase) { diff --git a/tests/JsonSchemaTestSuiteTest.php b/tests/JsonSchemaTestSuiteTest.php index aff7f451..180678d0 100644 --- a/tests/JsonSchemaTestSuiteTest.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -87,6 +87,10 @@ function ($file) { $test->valid ? 'valid' : 'invalid' ); + if ($this->shouldNotYieldTest($name)) { + continue; + } + yield $name => [ 'testCaseDescription' => $testCase->description, 'testDescription' => $test->description, @@ -119,4 +123,26 @@ private function loadRemotesIntoStorage(SchemaStorageInterface $storage): void } } + private function shouldNotYieldTest(string $name): bool + { + $skip = [ + '[draft4/ref.json]: refs with quote: object with numbers is valid is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: refs with quote: object with strings is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: Location-independent identifier: match is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: Location-independent identifier: mismatch is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: Location-independent identifier with base URI change in subschema: match is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: Location-independent identifier with base URI change in subschema: mismatch is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: id must be resolved against nearest parent, not just immediate parent: number is valid is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: id must be resolved against nearest parent, not just immediate parent: non-number is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: empty tokens in $ref json-pointer: number is valid is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/ref.json]: empty tokens in $ref json-pointer: non-number is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/refRemote.json]: base URI change - change folder: number is valid is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/refRemote.json]: base URI change - change folder: string is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + '[draft4/refRemote.json]: Location-independent identifier in remote ref: integer is valid is expected to be valid', // Test case was added after v1.2.0, skip test for now. + '[draft4/refRemote.json]: Location-independent identifier in remote ref: string is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. + ]; + + return in_array($name, $skip, true); + } + } From e90e44702d1c0ccd3998cc5e53ba1540eeb1c8ec Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 24 Jun 2025 12:58:00 +0200 Subject: [PATCH 12/14] test: skip test which require 64 bits operating system --- tests/Constraints/VeryBaseTestCase.php | 5 +++++ tests/Drafts/Draft4Test.php | 4 ++++ tests/JsonSchemaTestSuiteTest.php | 9 +++++++++ 3 files changed, 18 insertions(+) diff --git a/tests/Constraints/VeryBaseTestCase.php b/tests/Constraints/VeryBaseTestCase.php index baf04ffe..f468ac0b 100644 --- a/tests/Constraints/VeryBaseTestCase.php +++ b/tests/Constraints/VeryBaseTestCase.php @@ -71,4 +71,9 @@ private function readAndJsonDecodeFile(string $file): stdClass return json_decode(file_get_contents($file), false); } + + protected function is32Bit(): bool + { + return PHP_INT_SIZE === 4; + } } diff --git a/tests/Drafts/Draft4Test.php b/tests/Drafts/Draft4Test.php index 111d1c24..4cd0c865 100644 --- a/tests/Drafts/Draft4Test.php +++ b/tests/Drafts/Draft4Test.php @@ -74,6 +74,10 @@ public function getValidTests(): \Generator 'refRemote.json / base URI change - change folder / number is valid', ]; + if ($this->is32Bit()) { + $skip[] = 'multipleOf.json / small multiple of large integer / any integer is a multiple of 1e-8'; // Test case contains a number which doesn't fit in 32 bits + } + foreach (parent::getValidTests() as $name => $testcase) { if (in_array($name, $skip, true)) { continue; diff --git a/tests/JsonSchemaTestSuiteTest.php b/tests/JsonSchemaTestSuiteTest.php index 180678d0..0c4931bf 100644 --- a/tests/JsonSchemaTestSuiteTest.php +++ b/tests/JsonSchemaTestSuiteTest.php @@ -142,7 +142,16 @@ private function shouldNotYieldTest(string $name): bool '[draft4/refRemote.json]: Location-independent identifier in remote ref: string is invalid is expected to be invalid', // Test case was added after v1.2.0, skip test for now. ]; + if ($this->is32Bit()) { + $skip[] = '[draft4/multipleOf.json]: small multiple of large integer: any integer is a multiple of 1e-8 is expected to be valid'; // Test case contains a number which doesn't fit in 32 bits + } + return in_array($name, $skip, true); } + private function is32Bit(): bool + { + return PHP_INT_SIZE === 4; + } + } From 2a0154b1e0f26e7ab1523f2454484fbbbf2cecbe Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 24 Jun 2025 20:01:28 +0200 Subject: [PATCH 13/14] docs: add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c346c4f4..088d1c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Update test case to current (PHP) standards ([#831](https://github.com/jsonrainbow/json-schema/pull/831)) - Upgrade test suite to use generators ([#834](https://github.com/jsonrainbow/json-schema/pull/834)) +- update to latest json schema test suite ([#821](https://github.com/jsonrainbow/json-schema/pull/821)) ## [6.4.2] - 2025-06-03 ### Fixed From a0c89008fa41364d53f1db9f0c6aec9a9460ade5 Mon Sep 17 00:00:00 2001 From: Danny van der Sluijs Date: Tue, 24 Jun 2025 20:17:55 +0200 Subject: [PATCH 14/14] fix: fix dataprovider for pattern properties test --- tests/Constraints/PatternPropertiesTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Constraints/PatternPropertiesTest.php b/tests/Constraints/PatternPropertiesTest.php index ca99fc47..a4042602 100644 --- a/tests/Constraints/PatternPropertiesTest.php +++ b/tests/Constraints/PatternPropertiesTest.php @@ -79,8 +79,8 @@ public function getInvalidTests(): \Generator public function getValidTests(): \Generator { - [ - yield 'validates pattern schema' => json_encode([ + yield 'validates pattern schema' => [ + json_encode([ 'someobject' => [ 'foobar' => 'foo', 'barfoo' => 'bar',