Skip to content

Commit 4a1dc14

Browse files
committed
better ajv tests compliance
1 parent 39aa0db commit 4a1dc14

File tree

11 files changed

+195
-85
lines changed

11 files changed

+195
-85
lines changed

spec/ajv

Submodule ajv updated from ef40fbb to 2726d29

src/Constraint/Ref.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public function setData($data)
5656

5757
/**
5858
* @return mixed
59-
* @throws \Exception
6059
*/
6160
public function getData()
6261
{

src/Context.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Context extends MagicMap
3333
/** @var bool do not tolerate special symbols even if base64_decode accepts string */
3434
public $strictBase64Validation = false;
3535

36+
/** @var bool pack/unpack application/json in string content */
3637
public $unpackContentMediaType = true;
3738

3839
/** @var string property mapping set name */

src/RefResolver.php

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Swaggest\JsonSchema;
44

5+
use PhpLang\ScopeExit;
56
use Swaggest\JsonSchema\Constraint\Ref;
67
use Swaggest\JsonSchema\RemoteRef\BasicFetcher;
78

@@ -100,6 +101,12 @@ public function __construct($rootData = null)
100101
$this->rootData = $rootData;
101102
}
102103

104+
public function setRootData($rootData)
105+
{
106+
$this->rootData = $rootData;
107+
return $this;
108+
}
109+
103110

104111
public function setRemoteRefProvider(RemoteRefProvider $provider)
105112
{
@@ -115,16 +122,10 @@ private function getRefProvider()
115122
return $this->refProvider;
116123
}
117124

118-
private function registerIdData($id, $data)
119-
{
120-
121-
}
122-
123-
124125
/**
125126
* @param string $referencePath
126127
* @return Ref
127-
* @throws \Exception
128+
* @throws InvalidValue
128129
*/
129130
public function resolveReference($referencePath)
130131
{
@@ -185,6 +186,7 @@ public function resolveReference($referencePath)
185186
$rootResolver = $this->rootResolver ? $this->rootResolver : $this;
186187
/** @var null|RefResolver $refResolver */
187188
$refResolver = &$rootResolver->remoteRefResolvers[$url];
189+
$this->setResolutionScope($url);
188190
if (null === $refResolver) {
189191
$rootData = $rootResolver->getRefProvider()->getSchemaData($url);
190192
$refResolver = new RefResolver($rootData);
@@ -203,4 +205,52 @@ public function resolveReference($referencePath)
203205
}
204206

205207

208+
/**
209+
* @param $data
210+
* @param Context $options
211+
* @param int $nestingLevel
212+
* @throws Exception
213+
*/
214+
public function preProcessReferences($data, Context $options, $nestingLevel = 0)
215+
{
216+
if ($nestingLevel > 200) {
217+
throw new Exception('Too deep nesting level', Exception::DEEP_NESTING);
218+
}
219+
if (is_array($data)) {
220+
foreach ($data as $key => $item) {
221+
$this->preProcessReferences($item, $options, $nestingLevel + 1);
222+
}
223+
} elseif ($data instanceof \stdClass) {
224+
/** @var JsonSchema $data */
225+
if (
226+
isset($data->{Schema::ID_D4})
227+
&& is_string($data->{Schema::ID_D4})
228+
&& (($options->version === Schema::VERSION_AUTO) || $options->version === Schema::VERSION_DRAFT_04)
229+
) {
230+
$prev = $this->setupResolutionScope($data->{Schema::ID_D4}, $data);
231+
/** @noinspection PhpUnusedLocalVariableInspection */
232+
$_ = new ScopeExit(function () use ($prev, $options) {
233+
$this->setResolutionScope($prev);
234+
});
235+
}
236+
237+
if (isset($data->{Schema::ID})
238+
&& is_string($data->{Schema::ID})
239+
&& (($options->version === Schema::VERSION_AUTO) || $options->version >= Schema::VERSION_DRAFT_06)
240+
) {
241+
$prev = $this->setupResolutionScope($data->{Schema::ID}, $data);
242+
/** @noinspection PhpUnusedLocalVariableInspection */
243+
$_ = new ScopeExit(function () use ($prev, $options) {
244+
$this->setResolutionScope($prev);
245+
});
246+
}
247+
248+
249+
foreach ((array)$data as $key => $value) {
250+
$this->preProcessReferences($value, $options, $nestingLevel + 1);
251+
}
252+
}
253+
}
254+
255+
206256
}

src/RemoteRef/Preloaded.php

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,54 @@
22

33
namespace Swaggest\JsonSchema\RemoteRef;
44

5+
use Swaggest\JsonSchema\Context;
6+
use Swaggest\JsonSchema\RefResolver;
57
use Swaggest\JsonSchema\RemoteRefProvider;
8+
use Swaggest\JsonSchema\Schema;
69

710
class Preloaded implements RemoteRefProvider
811
{
912
private $storage;
1013

11-
public function __construct()
12-
{
13-
$this->setSchemaData('http://json-schema.org/draft-04/schema',
14-
json_decode(file_get_contents(__DIR__ . '/../../spec/json-schema.json')));
15-
$this->setSchemaData('http://json-schema.org/draft-06/schema',
16-
json_decode(file_get_contents(__DIR__ . '/../../spec/json-schema-draft6.json')));
17-
$this->setSchemaData('http://json-schema.org/draft-07/schema',
18-
json_decode(file_get_contents(__DIR__ . '/../../spec/json-schema-draft7.json')));
19-
}
14+
private $cachePaths = array(
15+
'http://json-schema.org/draft-04/schema' => __DIR__ . '/../../spec/json-schema.json',
16+
'http://json-schema.org/draft-06/schema' => __DIR__ . '/../../spec/json-schema-draft6.json',
17+
'http://json-schema.org/draft-07/schema' => __DIR__ . '/../../spec/json-schema-draft7.json',
18+
);
2019

2120
public function getSchemaData($url)
2221
{
2322
if (isset($this->storage[$url])) {
2423
return $this->storage[$url];
24+
} elseif (isset($this->cachePaths[$url])) {
25+
$this->storage[$url] = json_decode(file_get_contents($this->cachePaths[$url]));
26+
return $this->storage[$url];
2527
}
2628
return false;
2729
}
2830

31+
/**
32+
* @param RefResolver $refResolver
33+
* @param Context|null $options
34+
* @throws \Swaggest\JsonSchema\Exception
35+
*/
36+
public function populateSchemas(RefResolver $refResolver, Context $options = null)
37+
{
38+
if ($options === null) {
39+
$options = new Context();
40+
$options->refResolver = $refResolver;
41+
$options->version = Schema::VERSION_AUTO;
42+
}
43+
44+
$prev = $refResolver->getResolutionScope();
45+
foreach ($this->storage as $url => $schemaData) {
46+
$refResolver->setupResolutionScope($url, $schemaData);
47+
$refResolver->setResolutionScope($url);
48+
$refResolver->preProcessReferences($schemaData, $options);
49+
}
50+
$refResolver->setResolutionScope($prev);
51+
}
52+
2953
public function setSchemaData($url, $schemaData)
3054
{
3155
$this->storage[$url] = $schemaData;

src/Schema.php

Lines changed: 15 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ class Schema extends JsonSchema implements MetaHolder
4040
const VERSION_DRAFT_06 = 6;
4141
const VERSION_DRAFT_07 = 7;
4242

43-
const SCHEMA_DRAFT_04_URL = 'http://json-schema.org/draft-04/schema';
44-
4543
const REF = '$ref';
4644
const ID = '$id';
4745
const ID_D4 = 'id';
@@ -108,47 +106,6 @@ public function addPropertyMapping($dataName, $propertyName, $mapping = self::DE
108106
return $this;
109107
}
110108

111-
private function preProcessReferences($data, Context $options, $nestingLevel = 0)
112-
{
113-
if ($nestingLevel > 200) {
114-
throw new Exception('Too deep nesting level', Exception::DEEP_NESTING);
115-
}
116-
if (is_array($data)) {
117-
foreach ($data as $key => $item) {
118-
$this->preProcessReferences($item, $options, $nestingLevel + 1);
119-
}
120-
} elseif ($data instanceof \stdClass) {
121-
/** @var JsonSchema $data */
122-
if (
123-
isset($data->{Schema::ID_D4})
124-
&& is_string($data->{Schema::ID_D4})
125-
&& (($options->version === self::VERSION_AUTO) || $options->version === self::VERSION_DRAFT_04)
126-
) {
127-
$prev = $options->refResolver->setupResolutionScope($data->{Schema::ID_D4}, $data);
128-
/** @noinspection PhpUnusedLocalVariableInspection */
129-
$_ = new ScopeExit(function () use ($prev, $options) {
130-
$options->refResolver->setResolutionScope($prev);
131-
});
132-
}
133-
134-
if (isset($data->{self::ID})
135-
&& is_string($data->{self::ID})
136-
&& (($options->version === self::VERSION_AUTO) || $options->version >= self::VERSION_DRAFT_06)
137-
) {
138-
$prev = $options->refResolver->setupResolutionScope($data->{self::ID}, $data);
139-
/** @noinspection PhpUnusedLocalVariableInspection */
140-
$_ = new ScopeExit(function () use ($prev, $options) {
141-
$options->refResolver->setResolutionScope($prev);
142-
});
143-
}
144-
145-
146-
foreach ((array)$data as $key => $value) {
147-
$this->preProcessReferences($value, $options, $nestingLevel + 1);
148-
}
149-
}
150-
}
151-
152109
public static function import($data, Context $options = null)
153110
{
154111
// string $data is expected to be $ref uri
@@ -160,6 +117,14 @@ public static function import($data, Context $options = null)
160117
return parent::import($data, $options);
161118
}
162119

120+
/**
121+
* @param $data
122+
* @param Context|null $options
123+
* @return array|mixed|null|object|\stdClass
124+
* @throws Exception
125+
* @throws InvalidValue
126+
* @throws \Exception
127+
*/
163128
public function in($data, Context $options = null)
164129
{
165130
if ($options === null) {
@@ -168,13 +133,18 @@ public function in($data, Context $options = null)
168133

169134
$options->import = true;
170135

171-
$options->refResolver = new RefResolver($data);
136+
if ($options->refResolver === null) {
137+
$options->refResolver = new RefResolver($data);
138+
} else {
139+
$options->refResolver->setRootData($data);
140+
}
141+
172142
if ($options->remoteRefProvider) {
173143
$options->refResolver->setRemoteRefProvider($options->remoteRefProvider);
174144
}
175145

176146
if ($options->import) {
177-
$this->preProcessReferences($data, $options);
147+
$options->refResolver->preProcessReferences($data, $options);
178148
}
179149

180150
return $this->process($data, $options, '#');
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Swaggest\JsonSchema\Tests\PHPUnit\Ref;
4+
5+
6+
use Swaggest\JsonSchema\Exception;
7+
use Swaggest\JsonSchema\RefResolver;
8+
use Swaggest\JsonSchema\RemoteRef\Preloaded;
9+
10+
class InnerScopeTest extends \PHPUnit_Framework_TestCase
11+
{
12+
public function testInnerDef()
13+
{
14+
$p = new Preloaded();
15+
$p->setSchemaData('http://localhost:1234/scope_change.json',
16+
json_decode(file_get_contents(__DIR__ . '/../../../../spec/ajv/spec/remotes/scope_change.json')));
17+
$r = new RefResolver();
18+
$r->setRemoteRefProvider($p);
19+
try {
20+
$p->populateSchemas($r);
21+
$ref = $r->resolveReference('http://localhost:1234/scope_foo.json#/definitions/bar');
22+
$this->assertEquals((object)array('type' => 'string'), $ref->getData());
23+
} catch (Exception $e) {
24+
$this->fail($e->getMessage());
25+
}
26+
}
27+
28+
}

tests/src/PHPUnit/RefTest.php renamed to tests/src/PHPUnit/Ref/RefTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
22

3-
namespace Swaggest\JsonSchema\Tests\PHPUnit;
4-
3+
namespace Swaggest\JsonSchema\Tests\PHPUnit\Ref;
54

65
use Swaggest\JsonSchema\Context;
76
use Swaggest\JsonSchema\Exception\LogicException;
@@ -135,7 +134,7 @@ public function testRemoteRef()
135134
$refProvider->setSchemaData(
136135
'http://localhost:1234/subSchemas.json',
137136
json_decode(file_get_contents(
138-
__DIR__ . '/../../../spec/JSON-Schema-Test-Suite/remotes/subSchemas.json'
137+
__DIR__ . '/../../../../spec/JSON-Schema-Test-Suite/remotes/subSchemas.json'
139138
))
140139
);
141140

tests/src/PHPUnit/Spec/AjvTest.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Swaggest\JsonSchema\Tests\PHPUnit\Spec;
44

55

6+
use Swaggest\JsonSchema\RefResolver;
67
use Swaggest\JsonSchema\RemoteRef\Preloaded;
78
use Swaggest\JsonSchema\Schema;
89

@@ -40,7 +41,7 @@ public static function getProvider()
4041

4142
$dir = __DIR__ . '/../../../../spec/ajv/spec/remotes/';
4243
foreach (new \DirectoryIterator($dir) as $path) {
43-
if ($path === '.' || $path === '..') {
44+
if ($path->getFilename() === '.' || $path->getFilename() === '..') {
4445
continue;
4546
}
4647
$refProvider->setSchemaData('http://localhost:1234/'
@@ -55,18 +56,34 @@ public static function getProvider()
5556
protected function skipTest($name)
5657
{
5758
static $skip = array(
58-
'format.json validation of uuid strings: not valid uuid' => 1,
59-
'format.json validation of JSON-pointer URI fragment strings: not a valid JSON-pointer as uri fragment (% not URL-encoded)' => 1,
60-
'format.json validation of URL strings: an invalid URL string' => 1,
61-
'62_resolution_scope_change.json change resolution scope - change filename (#62): string is valid' => 1,
59+
//'format.json validation of uuid strings: not valid uuid [2]' => 1,
60+
//'format.json validation of JSON-pointer URI fragment strings: not a valid JSON-pointer as uri fragment (% not URL-encoded) [1]' => 1,
61+
//'62_resolution_scope_change.json change resolution scope - change filename (#62): string is valid [0]' => 1,
6262
);
6363

64-
// debug particular test
65-
//return '1_ids_in_refs.json IDs in refs with root id: valid' !== $name;
64+
// debug single test case
65+
//return '13_root_ref_in_ref_in_remote_ref.json root ref in remote ref (#13): string is valid [0]' !== $name;
6666

6767
return isset($skip[$name]);
6868
}
6969

70+
/**
71+
* @param $schemaData
72+
* @param $data
73+
* @param $isValid
74+
* @param $name
75+
* @param $version
76+
* @throws \Exception
77+
*/
78+
protected function runSpecTest($schemaData, $data, $isValid, $name, $version)
79+
{
80+
if (isset($schemaData->format) && in_array($schemaData->format, array('url', 'uuid', 'json-pointer-uri-fragment'))) {
81+
$this->markTestSkipped($schemaData->format . ' format is not supported');
82+
return;
83+
}
84+
parent::runSpecTest($schemaData, $data, $isValid, $name, $version);
85+
}
86+
7087

7188
public function specOptionalProvider()
7289
{
@@ -87,6 +104,16 @@ public function specExtrasProvider()
87104
return $this->provider($path);
88105
}
89106

107+
protected function makeOptions($version)
108+
{
109+
$options = parent::makeOptions($version);
110+
$options->refResolver = new RefResolver();
111+
if ($options->remoteRefProvider instanceof Preloaded) {
112+
$options->remoteRefProvider->populateSchemas($options->refResolver, $options);
113+
}
114+
return $options;
115+
}
116+
90117
/**
91118
* @dataProvider specExtrasProvider
92119
* @param $schemaData

0 commit comments

Comments
 (0)