Skip to content

Commit

Permalink
BREAKING CHANGE: Upgrade libraries, changing interfaces and introduci… (
Browse files Browse the repository at this point in the history
#19)

BREAKING CHANGE: Upgrade libraries, changing interfaces and introducing schema storage

* `justinrainbow/json-schema` has been upgraded to the latest version (5.x)
* Interface for `assertJsonMatchesSchema` has changed from `assertJsonMatchesSchema($schema, $content)` to `assertJsonMatchesSchema($content, $schema = null)`
* Support for schema storage within traits (see README.md)
  • Loading branch information
estahn authored Oct 26, 2018
1 parent 2f66884 commit e75e20d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 23 deletions.
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class MyTestCase extends \PHPUnit_Framework_TestCase

$json = json_decode('{"foo":1}');

$this->assertJsonMatchesSchema('./my-schema.json', $json);
$this->assertJsonMatchesSchema($json, './my-schema.json');
$this->assertJsonValueEquals(1, '* | [0]', $json);
}
}
Expand Down Expand Up @@ -115,7 +115,63 @@ class MyTestCase extends \PHPUnit_Framework_TestCase

$json = json_decode('{"foo":1}');

JsonAssert::assertJsonMatchesSchema('./my-schema.json', $json);
JsonAssert::assertJsonMatchesSchema($json, './my-schema.json');
JsonAssert::assertJsonValueEquals(1, '* | [0]', $json);
}
}
```

## Schema storage

The [schema storage](https://github.com/justinrainbow/json-schema/blob/master/src/JsonSchema/SchemaStorage.php) of `justinrainbow/json-schema` allows to register schemas which will effectively override the actual schema location.

Example:
```json
{"$ref" : "https://iglu.foobar.com/myschema.json#/definitions/positiveInteger"}
```

The resolver will fetch the schema from this endpoint and match the JSON document against it. Using schema storage you're able to override this behaviour.

```php
$schemastorage->addSchema('https://iglu.foobar.com/myschema.json', (object)['type' => 'string']);
```

With this in place the resolver will take the schema that is already in place without downloading it again.

```php
<?php

namespace EnricoStahn\JsonAssert\Tests;

use EnricoStahn\JsonAssert\AssertClass as JsonAssert;

class MyTestCase extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
self::$schemaStorage = new SchemaStorage();

self::$schemaStorage->addSchema('<id>', obj);
...
}

public function testJsonDocumentIsValid()
{
// my-schema.json
//
// {
// "type" : "object",
// "properties" : {
// "foo" : {
// "type" : "integer"
// }
// },
// "required" : [ "foo" ]
// }

$json = json_decode('{"foo":1}');

JsonAssert::assertJsonMatchesSchema($json, './my-schema.json');
JsonAssert::assertJsonValueEquals(1, '* | [0]', $json);
}
}
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"minimum-stability": "stable",
"require": {
"php": "^7.0",
"justinrainbow/json-schema": "^2.0",
"justinrainbow/json-schema": "^5.0",
"mtdowling/jmespath.php": "^2.3"
},
"require-dev": {
Expand Down
58 changes: 45 additions & 13 deletions src/Assert.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@

namespace EnricoStahn\JsonAssert;

use JsonSchema\RefResolver;
use JsonSchema\Uri\UriResolver;
use JsonSchema\Uri\UriRetriever;
use JsonSchema\Constraints\Factory;
use JsonSchema\SchemaStorage;
use JsonSchema\Validator;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;

/**
* Asserts to validate JSON data.
Expand All @@ -25,25 +25,40 @@
*/
trait Assert
{
/**
* @var SchemaStorage
*/
private static $schemaStorage = null;

/**
* Asserts that json content is valid according to the provided schema file.
*
* Example:
*
* static::assertJsonMatchesSchema('./schema.json', json_decode('{"foo":1}'))
* static::assertJsonMatchesSchema(json_decode('{"foo":1}'), './schema.json')
*
* @param string $schema Path to the schema file
* @param string|null $schema Path to the schema file
* @param array|object $content JSON array or object
*/
public static function assertJsonMatchesSchema($schema, $content)
public static function assertJsonMatchesSchema($content, $schema = null)
{
// Assume references are relative to the current file
// Create an issue or pull request if you need more complex use cases
$refResolver = new RefResolver(new UriRetriever(), new UriResolver());
$schemaObj = $refResolver->resolve('file://'.realpath($schema));
if (self::$schemaStorage === null) {
self::$schemaStorage = new SchemaStorage();
}

if ($schema !== null && !file_exists($schema)) {
throw new FileNotFoundException($schema);
}

$schemaObject = null;

$validator = new Validator();
$validator->check($content, $schemaObj);
if ($schema !== null) {
$schemaObject = json_decode(file_get_contents($schema));
self::$schemaStorage->addSchema('file://'.$schema, $schemaObject);
}

$validator = new Validator(new Factory(self::$schemaStorage));
$validator->validate($content, $schemaObject);

$message = '- Property: %s, Contraint: %s, Message: %s';
$messages = array_map(function ($exception) use ($message) {
Expand All @@ -54,6 +69,23 @@ public static function assertJsonMatchesSchema($schema, $content)
\PHPUnit\Framework\Assert::assertTrue($validator->isValid(), implode("\n", $messages));
}

/**
* Asserts that json content is valid according to the provided schema file.
*
* Example:
*
* static::assertJsonMatchesSchema(json_decode('{"foo":1}'), './schema.json')
*
* @param string|null $schema Path to the schema file
* @param array|object $content JSON array or object
*
* @deprecated This will be removed in the next major version (4.x).
*/
public static function assertJsonMatchesSchemaDepr($schema, $content)
{
self::assertJsonMatchesSchema($content, $schema);
}

/**
* Asserts that json content is valid according to the provided schema string.
*
Expand All @@ -65,7 +97,7 @@ public static function assertJsonMatchesSchemaString($schema, $content)
$file = tempnam(sys_get_temp_dir(), 'json-schema-');
file_put_contents($file, $schema);

self::assertJsonMatchesSchema($file, $content);
self::assertJsonMatchesSchema($content, $file);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Extension/Symfony.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ trait Symfony
*/
public static function assertJsonMatchesSchema($schema, Response $response)
{
Assert::assertJsonMatchesSchema($schema, json_decode($response->getContent()));
Assert::assertJsonMatchesSchemaDepr($schema, json_decode($response->getContent()));
}

/**
Expand Down
19 changes: 19 additions & 0 deletions tests/AssertTraitImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,28 @@
namespace EnricoStahn\JsonAssert\Tests;

use EnricoStahn\JsonAssert\Assert as JsonAssert;
use JsonSchema\SchemaStorage;
use PHPUnit\Framework\TestCase;

class AssertTraitImpl extends TestCase
{
use JsonAssert;

public function setUp()
{
self::$schemaStorage = new SchemaStorage();
}

/**
* @param string $id
* @param string $schema
*
* @return SchemaStorage
*/
public function testWithSchemaStore($id, $schema)
{
self::$schemaStorage->addSchema($id, $schema);

return self::$schemaStorage;
}
}
23 changes: 17 additions & 6 deletions tests/AssertTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public function testAssertJsonMatchesSchemaSimple()
{
$content = json_decode(file_get_contents(Utils::getJsonPath('assertJsonMatchesSchema_simple.json')));

AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('assertJsonMatchesSchema_simple.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('assertJsonMatchesSchema_simple.schema.json'), $content);
}

public function testAssertJsonMatchesSchema()
{
$content = json_decode('{"foo":123}');

AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('test.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('test.schema.json'), $content);
}

/**
Expand All @@ -42,7 +42,7 @@ public function testAssertJsonMatchesSchemaFail()
{
$content = json_decode('{"foo":"123"}');

AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('test.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('test.schema.json'), $content);
}

public function testAssertJsonMatchesSchemaFailMessage()
Expand All @@ -52,7 +52,7 @@ public function testAssertJsonMatchesSchemaFailMessage()
$exception = null;

try {
AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('test.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('test.schema.json'), $content);
} catch (ExpectationFailedException $exception) {
self::assertContains('- Property: foo, Contraint: type, Message: String value found, but an integer is required', $exception->getMessage());
self::assertContains('- Response: {"foo":"123"}', $exception->getMessage());
Expand All @@ -68,7 +68,7 @@ public function testAssertJsonMatchesSchemaWithRefs()
{
$content = json_decode('{"code":123, "message":"Nothing works."}');

AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('error.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('error.schema.json'), $content);
}

/**
Expand All @@ -78,7 +78,7 @@ public function testAssertJsonMatchesSchemaWithRefsFails()
{
$content = json_decode('{"code":"123", "message":"Nothing works."}');

AssertTraitImpl::assertJsonMatchesSchema(Utils::getSchemaPath('error.schema.json'), $content);
AssertTraitImpl::assertJsonMatchesSchemaDepr(Utils::getSchemaPath('error.schema.json'), $content);
}

public function testAssertJsonMatchesSchemaString()
Expand All @@ -104,6 +104,17 @@ public function testAssertJsonValueEquals($expression, $value)
AssertTraitImpl::assertJsonValueEquals($value, $expression, $content);
}

public function testAssertWithSchemaStore()
{
$obj = new AssertTraitImpl();
$obj->setUp();

$schemastore = $obj->testWithSchemaStore('foobar', (object) ['type' => 'string']);

self::assertInstanceOf('JsonSchema\SchemaStorage', $schemastore);
self::assertEquals($schemastore->getSchema('foobar'), (object) ['type' => 'string']);
}

public function assertJsonValueEqualsProvider()
{
return [
Expand Down

0 comments on commit e75e20d

Please sign in to comment.