From 1310fcb87162913da4681c6eebba9584713a76bf Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 18:28:29 -0700 Subject: [PATCH 01/10] Add support for draft 6 - updates json library dependency version for draft 6 support - adds assertions for some draft 6 scenarios --- README.md | 2 +- composer.json | 2 +- src/Assert.php | 2 +- tests/AssertTraitTest.php | 166 +++++++++++++++++- ...assertJsonMatchesSchema_simple_draft6.json | 11 ++ ...assertJsonMatchesSchema_draft6.schema.json | 81 +++++++++ 6 files changed, 256 insertions(+), 8 deletions(-) create mode 100644 tests/json/assertJsonMatchesSchema_simple_draft6.json create mode 100644 tests/schemas/assertJsonMatchesSchema_draft6.schema.json diff --git a/README.md b/README.md index 3bdf443..a8e14c9 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ class MyTestCase extends \PHPUnit_Framework_TestCase ### Class -In case you don't want to use the `trait` you can use the provided class wich extends from `\PHPUnit_Framework_TestCase`. +In case you don't want to use the `trait` you can use the provided class which extends from `\PHPUnit_Framework_TestCase`. You can either extend your test case or use the static methods like below. ```php diff --git a/composer.json b/composer.json index 9bd4764..b5d851f 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "minimum-stability": "stable", "require": { "php": "^7.4|^8.0", - "justinrainbow/json-schema": "^5.0", + "justinrainbow/json-schema": "^6.0", "mtdowling/jmespath.php": "^2.3", "ext-json": "*" }, diff --git a/src/Assert.php b/src/Assert.php index 49bc458..513bb49 100644 --- a/src/Assert.php +++ b/src/Assert.php @@ -63,7 +63,7 @@ public static function assertJsonMatchesSchema($content, ?string $schema = null) $message = '- Property: %s, Constraint: %s, Message: %s'; $messages = array_map(function ($exception) use ($message) { - return sprintf($message, $exception['property'], $exception['constraint'], $exception['message']); + return sprintf($message, $exception['property'], $exception['constraint']['name'], $exception['message']); }, $validator->getErrors()); $messages[] = '- Response: '.json_encode($content); diff --git a/tests/AssertTraitTest.php b/tests/AssertTraitTest.php index 8885cd2..46b6098 100644 --- a/tests/AssertTraitTest.php +++ b/tests/AssertTraitTest.php @@ -22,11 +22,164 @@ class AssertTraitTest extends TestCase * * @see https://github.com/estahn/phpunit-json-assertions/wiki/assertJsonMatchesSchema */ + public function testAssertJsonMatchesSchemaSimpleDraft6() + { + $content = json_decode(file_get_contents(Utils::getJsonPath('assertJsonMatchesSchema_simple_draft6.json'))); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"created_at\": \"2016-01-01T12:00:00Z\"}", true] + * ["{\"created_at\": \"2016/01/01\"}", false] + */ + public function testAssertJsonMatchesSchemaDraft6DateTime($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"status\": \"active\", \"created_at\": \"2016-01-01T12:00:00Z\"}", true] + * ["{\"status\": \"completed\", \"created_at\": \"2016-01-01T12:00:00Z\"}", true] + * ["{\"status\": \"deleted\", \"created_at\": \"2016-01-01T12:00:00Z\"}", false] + */ + public function testAssertJsonMatchesSchemaDraft6EnumAndNot($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"status\": \"active\", \"created_at\": \"2016-01-01T12:00:00Z\"}", true] + * ["{\"status\": \"active\"}", false] + */ + public function testAssertJsonMatchesSchemaDraft6Dependency($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"id\": 2}", true] + * ["{\"id\": 1}", false] + * ["{\"id\": 0}", false] + */ + public function testAssertJsonMatchesSchemaDraft6ExclusiveMinimum($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"title\": \"A brief description\"}", true] + * ["{\"title\": \"A description that is too long\"}", false] + * ["{\"title\": \"A\"}", false] + */ + public function testAssertJsonMatchesSchemaDraft6MaxMinLength($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"invalid_name\": \"value\"}", false] + */ + public function testAssertJsonMatchesSchemaDraft6AdditionalProperties($json, $pass) + { + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + + /** + * @testWith + * ["{\"status\": \"completed\", \"completed_at\": \"2020-01-01T12:00:00Z\", \"created_at\": \"2020-01-01T12:00:00Z\"}", true] + * ["{\"status\": \"completed\", \"created_at\": \"2020-01-01T12:00:00Z\"}", false] + * ["{\"status\": \"pending\", \"expected_completion\": \"2020-01-01T12:00:00Z\", \"created_at\": \"2020-01-01T12:00:00Z\"}", true] + * ["{\"status\": \"pending\", \"created_at\": \"2020-01-01T12:00:00Z\"}", false] + * ["{\"status\": \"active\", \"created_at\": \"2020-01-01T12:00:00Z\"}", true] + */ + public function testAssertJsonMatchesSchemaDraft6Conditional($json, $pass) + { + $this->markTestSkipped('Conditional validation is not supported by the current implementation.'); + + if (!$pass) { + $this->expectException(ExpectationFailedException::class); + } + + $content = json_decode($json); + + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_draft6.schema.json') + ); + } + public function testAssertJsonMatchesSchemaSimple() { $content = json_decode(file_get_contents(Utils::getJsonPath('assertJsonMatchesSchema_simple.json'))); - AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('assertJsonMatchesSchema_simple.schema.json')); + AssertTraitImpl::assertJsonMatchesSchema( + $content, + Utils::getSchemaPath('assertJsonMatchesSchema_simple.schema.json') + ); } public function testAssertJsonMatchesSchema() @@ -61,7 +214,10 @@ public function testAssertJsonMatchesSchemaFailMessage() try { AssertTraitImpl::assertJsonMatchesSchema($content, Utils::getSchemaPath('test.schema.json')); } catch (ExpectationFailedException $exception) { - self::assertStringContainsString('- Property: foo, Constraint: type, Message: String value found, but an integer is required', $exception->getMessage()); + self::assertStringContainsString( + '- Property: foo, Constraint: type, Message: String value found, but an integer is required', + $exception->getMessage() + ); self::assertStringContainsString('- Response: {"foo":"123"}', $exception->getMessage()); } @@ -100,7 +256,7 @@ public function testAssertJsonMatchesSchemaString() * @dataProvider assertJsonValueEqualsProvider * * @param string $expression - * @param mixed $value + * @param mixed $value */ public function testAssertJsonValueEquals(string $expression, $value) { @@ -114,10 +270,10 @@ public function testAssertWithSchemaStore() $obj = new AssertTraitImpl(); $obj->setUp(); - $schemaStore = $obj->testWithSchemaStore('foobar', (object) ['type' => 'string']); + $schemaStore = $obj->testWithSchemaStore('foobar', (object)['type' => 'string']); self::assertInstanceOf('JsonSchema\SchemaStorage', $schemaStore); - self::assertEquals($schemaStore->getSchema('foobar'), (object) ['type' => 'string']); + self::assertEquals($schemaStore->getSchema('foobar'), (object)['type' => 'string']); } public function assertJsonValueEqualsProvider(): array diff --git a/tests/json/assertJsonMatchesSchema_simple_draft6.json b/tests/json/assertJsonMatchesSchema_simple_draft6.json new file mode 100644 index 0000000..70f3e1c --- /dev/null +++ b/tests/json/assertJsonMatchesSchema_simple_draft6.json @@ -0,0 +1,11 @@ +{ + "id": 33, + "title": "Sample Title", + "status": "active", + "created_at": "2009-03-24T16:24:32Z", + "tags": [ + "foo", + "bar" + ], + "meta_description": "A brief description." +} diff --git a/tests/schemas/assertJsonMatchesSchema_draft6.schema.json b/tests/schemas/assertJsonMatchesSchema_draft6.schema.json new file mode 100644 index 0000000..f829b00 --- /dev/null +++ b/tests/schemas/assertJsonMatchesSchema_draft6.schema.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "type": "object", + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 7, + "properties": { + "id": { + "type": "integer", + "minimum": 1, + "exclusiveMinimum": 1, + "multipleOf": 1 + }, + "title": { + "type": "string", + "examples": ["Sample Title", "Another Title"], + "minLength": 2, + "maxLength": 20 + }, + "status": { + "type": "string", + "enum": ["active", "pending", "completed"], + "not": { + "enum": ["inactive", "deleted"] + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "completed_at": { + "type": "string", + "format": "date-time" + }, + "expected_completion": { + "type": "string", + "format": "date-time" + }, + "tags": { + "type": "array", + "minItems": 1, + "maxItems": 10, + "items": { + "type": "string" + }, + "uniqueItems": true, + "contains": { "const": "foo" } + }, + "meta_description": { + "type": "string" + } + }, + "dependencies": { + "status": ["created_at"] + }, + "patternProperties": { + "^meta_": { + "type": "string", + "minLength": 5 + } + }, + "propertyNames": { + "pattern": "^[a-z][a-z0-9_]*$", + "maxLength": 10 + }, + + "if": { + "properties": { "status": { "const": "completed" } } + }, + "then": { + "required": ["completed_at"] + }, + "else": { + "if": { + "properties": { "status": { "const": "pending" } } + }, + "then": { + "required": ["expected_completion"] + } + } +} From c071e884d39960af0ddebfd41e683908c96ce524 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 18:38:26 -0700 Subject: [PATCH 02/10] Debugging github action failure --- .github/workflows/build.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 528e26c..ba24ed5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,16 +51,19 @@ jobs: strategy: matrix: php: - - 7.4 - - 8.0 + - 8.1 + - 8.2 + - 8.3 include: - - php: 7.4 + - php: 8.1 phpunit: 9.5.0 - - php: 8.0 + - php: 8.2 + phpunit: 9.5.0 + - php: 8.3 phpunit: 9.5.0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache Composer dependencies uses: actions/cache@v2 @@ -72,7 +75,7 @@ jobs: with: php_version: ${{ matrix.php }} - - uses: php-actions/phpunit@v9 + - uses: php-actions/phpunit@v3 with: php_version: ${{ matrix.php }} version: ${{ matrix.phpunit }} From 36bc236905e17c9a30465d311fd63b7282184c75 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 18:48:07 -0700 Subject: [PATCH 03/10] Debugging github action failure --- .github/workflows/build.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba24ed5..83fddd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ jobs: psalm: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Psalm uses: docker://vimeo/psalm-github-actions @@ -56,11 +56,8 @@ jobs: - 8.3 include: - php: 8.1 - phpunit: 9.5.0 - php: 8.2 - phpunit: 9.5.0 - php: 8.3 - phpunit: 9.5.0 steps: - uses: actions/checkout@v3 @@ -78,4 +75,3 @@ jobs: - uses: php-actions/phpunit@v3 with: php_version: ${{ matrix.php }} - version: ${{ matrix.phpunit }} From ddce5b20a7eba834abc6b584a34a7c405dfafc53 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 18:49:49 -0700 Subject: [PATCH 04/10] Debugging github action failure --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 83fddd2..63d1125 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,11 +51,9 @@ jobs: strategy: matrix: php: - - 8.1 - 8.2 - 8.3 include: - - php: 8.1 - php: 8.2 - php: 8.3 From dd09fbc62db9e490ffd892f31af25c229d136af9 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 19:02:26 -0700 Subject: [PATCH 05/10] Debugging github action failure --- composer.json | 2 +- phpunit.xml.dist | 36 ++++++++++++++++-------------------- tests/AssertClassTest.php | 2 +- tests/AssertTraitTest.php | 12 ++++++------ 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index b5d851f..082bdb2 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "^9", + "phpunit/phpunit": "^10", "codacy/coverage": "dev-master", "symfony/http-foundation": "^2.8|^3.0|^5.0" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 111d4a7..bbe3525 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,22 +1,18 @@ - - - - - tests/ - - - - - - src/ - - - - - + + + + tests/ + + + + + + + + + + src/ + + diff --git a/tests/AssertClassTest.php b/tests/AssertClassTest.php index adcf42f..541e8c1 100644 --- a/tests/AssertClassTest.php +++ b/tests/AssertClassTest.php @@ -18,6 +18,6 @@ class AssertClassTest extends TestCase { public function testClassInstance() { - static::assertInstanceOf('EnricoStahn\JsonAssert\AssertClass', new AssertClass()); + static::assertInstanceOf('EnricoStahn\JsonAssert\AssertClass', new AssertClass('AssertClassTest')); } } diff --git a/tests/AssertTraitTest.php b/tests/AssertTraitTest.php index 46b6098..abbaab0 100644 --- a/tests/AssertTraitTest.php +++ b/tests/AssertTraitTest.php @@ -256,7 +256,7 @@ public function testAssertJsonMatchesSchemaString() * @dataProvider assertJsonValueEqualsProvider * * @param string $expression - * @param mixed $value + * @param mixed $value */ public function testAssertJsonValueEquals(string $expression, $value) { @@ -267,16 +267,16 @@ public function testAssertJsonValueEquals(string $expression, $value) public function testAssertWithSchemaStore() { - $obj = new AssertTraitImpl(); + $obj = new AssertTraitImpl('testAssertWithSchemaStore'); $obj->setUp(); - $schemaStore = $obj->testWithSchemaStore('foobar', (object)['type' => 'string']); + $schemaStore = $obj->testWithSchemaStore('foobar', (object) ['type' => 'string']); self::assertInstanceOf('JsonSchema\SchemaStorage', $schemaStore); - self::assertEquals($schemaStore->getSchema('foobar'), (object)['type' => 'string']); + self::assertEquals($schemaStore->getSchema('foobar'), (object) ['type' => 'string']); } - public function assertJsonValueEqualsProvider(): array + public static function assertJsonValueEqualsProvider(): array { return [ ['foo', '123'], @@ -300,7 +300,7 @@ public function testGetJsonObject($expected, $actual) self::assertEquals($expected, AssertTraitImpl::getJsonObject($actual)); } - public function jsonObjectProvider(): array + public static function jsonObjectProvider(): array { return [ [[], []], From 0f700171f4043510a510d81024bb44aa51e2d36e Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 19:06:54 -0700 Subject: [PATCH 06/10] Debugging github action failure --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63d1125..ce5fffb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,9 @@ jobs: - 8.3 include: - php: 8.2 + phpunit: 10 - php: 8.3 + phpunit: 10 steps: - uses: actions/checkout@v3 @@ -73,3 +75,4 @@ jobs: - uses: php-actions/phpunit@v3 with: php_version: ${{ matrix.php }} + version: ${{ matrix.phpunit }} From e74fe1ec7df2594ee88790360d9e2fd2cfbe070d Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 19:08:33 -0700 Subject: [PATCH 07/10] wip --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce5fffb..a6cd21b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ name: Test -on: [pull_request] +on: [push, pull_request] jobs: psalm: From b4aae15f339f7a6da0be7287b88b16d84955ef79 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 19:15:32 -0700 Subject: [PATCH 08/10] wip --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6cd21b..4bccff1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,3 +76,9 @@ jobs: with: php_version: ${{ matrix.php }} version: ${{ matrix.phpunit }} + php_extensions: xdebug + coverage_text: true + bootstrap: vendor/autoload.php + args: --coverage-filter src tests + env: + XDEBUG_MODE: coverage From 666587aaf0f779acca30a25e9b5bd6f3ae17e492 Mon Sep 17 00:00:00 2001 From: J-T-McC Date: Tue, 13 Aug 2024 19:18:06 -0700 Subject: [PATCH 09/10] wip --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bccff1..d68cbd2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,7 +66,7 @@ jobs: uses: actions/cache@v2 with: path: /tmp/composer-cache - key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-${{ hashFiles('composer.lock') }} - uses: php-actions/composer@v6 with: From 31be5cf89d69b616e101dd4a1aa618add05980e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 02:18:53 +0000 Subject: [PATCH 10/10] chore: bump actions/cache from 2 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d68cbd2..a94da35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: # fetch-depth: 0 # - name: Cache Composer dependencies - # uses: actions/cache@v2 + # uses: actions/cache@v4 # with: # path: /tmp/composer-cache # key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} @@ -63,7 +63,7 @@ jobs: - uses: actions/checkout@v3 - name: Cache Composer dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: /tmp/composer-cache key: ${{ runner.os }}-${{ hashFiles('composer.lock') }}