From f5af183f0f09043a680251863aae1da27e6376df Mon Sep 17 00:00:00 2001 From: Julien VITTE Date: Fri, 12 Feb 2021 10:01:03 +0100 Subject: [PATCH] feat(json): adds checks for json formats --- composer.json | 3 +- phpunit.xml.dist | 2 +- src/Checks/JsonCheck.php | 60 +++++++++++++++++++ src/Constraint/Json/MatchesSchema.php | 62 ++++++++++++++++++++ tests/Constraint/Json/MatchesSchemaTest.php | 64 +++++++++++++++++++++ tests/Fixture/Person.php | 7 ++- tests/Fixture/files/batman.json | 3 + tests/Fixture/files/robin.json | 3 + tests/JsonCheckTest.php | 37 ++++++++++++ tests/bootstrap.php | 7 +++ 10 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 src/Checks/JsonCheck.php create mode 100644 src/Constraint/Json/MatchesSchema.php create mode 100644 tests/Constraint/Json/MatchesSchemaTest.php create mode 100644 tests/Fixture/files/batman.json create mode 100644 tests/Fixture/files/robin.json create mode 100644 tests/JsonCheckTest.php create mode 100644 tests/bootstrap.php diff --git a/composer.json b/composer.json index 845eac1..5896ed9 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,8 @@ "phpunit/phpunit": "8.5", "phpcollection/phpcollection": "^0.5.0", "psr/http-message": "^1.0", - "pitchart/transformer": "^1.6" + "pitchart/transformer": "^1.6", + "justinrainbow/json-schema": "^5.2" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d81c719..8094f92 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,5 @@ -value = $value; + } + + public function isEqualTo(string $expected): self + { + Assert::assertJsonStringEqualsJsonString($expected, $this->value, $this->message); + return $this; + } + + public function isNotEqualTo(string $expected): self + { + Assert::assertJsonStringNotEqualsJsonString($expected, $this->value, $this->message); + return $this; + } + + public function isEqualToFileContent(string $path): self + { + Assert::assertJsonStringEqualsJsonFile($path, $this->value, $this->message); + return $this; + } + + public function isNotEqualToFileContent(string $path): self + { + Assert::assertJsonStringNotEqualsJsonFile($path, $this->value, $this->message); + return $this; + } + + public function matchesSchema($schema) + { + Assert::assertThat($this->value, new MatchesSchema($schema), $this->message); + return $this; + } + +} \ No newline at end of file diff --git a/src/Constraint/Json/MatchesSchema.php b/src/Constraint/Json/MatchesSchema.php new file mode 100644 index 0000000..5cf1778 --- /dev/null +++ b/src/Constraint/Json/MatchesSchema.php @@ -0,0 +1,62 @@ +schema = $this->convertToObject($schema); + } + + + public function toString(): string + { + return sprintf('matches Json Schema %s', json_encode($this->schema)); + } + + /** + * @inheritdoc + */ + protected function matches($other): bool + { + $other = $this->convertToObject($other); + + $validator = new Validator(); + $validator->check($other, $this->schema); + + return $validator->isValid(); + } + + /** + * @inheritdoc + */ + protected function additionalFailureDescription($other): string + { + $other = $this->convertToObject($other); + + $validator = new Validator(); + $validator->check($other, $this->schema); + + return implode("\n", array_map(function ($error) { + return sprintf("[%s] %s", $error['property'], $error['message']); + }, $validator->getErrors())); + } + + private function convertToObject($value): \stdClass + { + if (is_string($value)) { + return json_decode($value); + } + + return json_decode(json_encode($value)); + } + +} \ No newline at end of file diff --git a/tests/Constraint/Json/MatchesSchemaTest.php b/tests/Constraint/Json/MatchesSchemaTest.php new file mode 100644 index 0000000..14189dd --- /dev/null +++ b/tests/Constraint/Json/MatchesSchemaTest.php @@ -0,0 +1,64 @@ +constraint = new MatchesSchema(['type' => 'object', 'required' => ['name'], 'properties' => ['name' => ['type' => 'string']]]); + } + + public function test_succeeds_when_sut_is_a_string_matching_provided_format() + { + $this->assertTrue($this->constraint->evaluate('{"name": "Batman"}', '', true)); + } + + public function test_succeeds_when_sut_is_an_array_matching_provided_format() + { + $this->assertTrue($this->constraint->evaluate(['name' => 'Batman'], '', true)); + } + + public function test_succeeds_when_sut_is_an_object_matching_provided_format() + { + $this->assertTrue($this->constraint->evaluate(new Person('Batman'), '', true)); + } + + public function test_succeeds_when_sut_is_is_more_than_provided_format() + { + $this->assertTrue($this->constraint->evaluate(['name' => 'Batman', 'city' => 'Gotham City'], '', true)); + } + + public function test_fails_when_sut_has_a_property_missing() + { + $this->assertFalse($this->constraint->evaluate(['lastname' => 'Batman'], '', true)); + } + + public function test_fails_when_sut_do_not_respect_format() + { + $this->assertFalse($this->constraint->evaluate(['name' => null], '', true)); + } + + public function test_fails_with_a_clear_and_complete_error_message() + { + $json = '{"name":null}'; + + $this->assertHasFailingMessage( + <<name = $name; } + function jsonSerialize() + { + return ['name' => $this->name]; + } + } \ No newline at end of file diff --git a/tests/Fixture/files/batman.json b/tests/Fixture/files/batman.json new file mode 100644 index 0000000..563c57c --- /dev/null +++ b/tests/Fixture/files/batman.json @@ -0,0 +1,3 @@ +{ + "name": "Batman" +} \ No newline at end of file diff --git a/tests/Fixture/files/robin.json b/tests/Fixture/files/robin.json new file mode 100644 index 0000000..dd4b0d2 --- /dev/null +++ b/tests/Fixture/files/robin.json @@ -0,0 +1,3 @@ +{ + "name":"Robin" +} \ No newline at end of file diff --git a/tests/JsonCheckTest.php b/tests/JsonCheckTest.php new file mode 100644 index 0000000..db5d732 --- /dev/null +++ b/tests/JsonCheckTest.php @@ -0,0 +1,37 @@ +asJson(), $method], $arguments)) + ->isAnInstanceOf(JsonCheck::class); + } + + public function methodsAreFulentProvider() + { + $batman = file_get_contents(TEST_FILES_PATH.'batman.json'); + yield from [ + 'isEqualTo' => ['{"name":"Batman"}', 'isEqualTo', [$batman]], + 'isNotEqualTo' => ['{"name":"Batman"}', 'isNotEqualTo', ['{"name":"Robin"}']], + 'isEqualToFileContent' => ['{"name":"Batman"}', 'isEqualToFileContent', [TEST_FILES_PATH.'batman.json']], + 'isNotEqualToFileContent' => ['{"name":"Batman"}', 'isNotEqualToFileContent', [TEST_FILES_PATH.'robin.json']], + 'matchesSchema' => ['{"name":"Batman"}', 'matchesSchema', [['type' => 'object', 'required' => ['name'], 'properties' => ['name' => ['type' => 'string']]], + ]] + ]; + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..80f7b05 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,7 @@ +