From c53c87bfde560ef41d6f99b0933676e324917bc4 Mon Sep 17 00:00:00 2001 From: Erayd Date: Fri, 17 Mar 2017 17:21:32 +1300 Subject: [PATCH] Add more unit tests (#366) * Add test coverage for coercion API * Complete test coverage for SchemaStorage * Add test coverage for ObjectIterator * Add exception test for JsonPointer * MabeEnum\Enum appears to use singletons - add testing const * Don't check this line for coverage mbstring is on all test platforms, so this line will never be reached. * Add test for TypeConstraint::validateTypeNameWording() * Add test for exception on TypeConstraint::validateType() * PHPunit doesn't like an explanation with its @codeCoverageIgnore... * Add various tests for UriRetriever * Add tests for FileGetContents * Add tests for JsonSchema\Uri\Retrievers\Curl * Add missing bad-syntax test file * Restrict ignore to the exception line only * Fix exception scope --- .../Constraints/StringConstraint.php | 3 +- src/JsonSchema/Uri/Retrievers/Curl.php | 4 +- .../Uri/Retrievers/FileGetContents.php | 6 +- tests/Constraints/CoerciveTest.php | 9 ++ tests/Constraints/TypeTest.php | 27 ++++++ tests/Entity/JsonPointerTest.php | 9 ++ tests/Iterators/ObjectIteratorTest.php | 89 +++++++++++++++++++ tests/SchemaStorageTest.php | 14 +++ tests/Uri/Retrievers/CurlTest.php | 57 ++++++++++++ tests/Uri/Retrievers/FileGetContentsTest.php | 79 ++++++++++++---- tests/Uri/UriRetrieverTest.php | 84 +++++++++++++++++ tests/fixtures/bad-syntax.json | 1 + 12 files changed, 362 insertions(+), 20 deletions(-) create mode 100644 tests/Iterators/ObjectIteratorTest.php create mode 100644 tests/Uri/Retrievers/CurlTest.php create mode 100644 tests/fixtures/bad-syntax.json diff --git a/src/JsonSchema/Constraints/StringConstraint.php b/src/JsonSchema/Constraints/StringConstraint.php index 5b15de7a..c66af1ed 100644 --- a/src/JsonSchema/Constraints/StringConstraint.php +++ b/src/JsonSchema/Constraints/StringConstraint.php @@ -54,6 +54,7 @@ private function strlen($string) return mb_strlen($string, mb_detect_encoding($string)); } - return strlen($string); + // mbstring is present on all test platforms, so strlen() can be ignored for coverage + return strlen($string); // @codeCoverageIgnore } } diff --git a/src/JsonSchema/Uri/Retrievers/Curl.php b/src/JsonSchema/Uri/Retrievers/Curl.php index a4125aa6..81c86037 100644 --- a/src/JsonSchema/Uri/Retrievers/Curl.php +++ b/src/JsonSchema/Uri/Retrievers/Curl.php @@ -9,6 +9,7 @@ namespace JsonSchema\Uri\Retrievers; +use JsonSchema\Exception\RuntimeException; use JsonSchema\Validator; /** @@ -23,7 +24,8 @@ class Curl extends AbstractRetriever public function __construct() { if (!function_exists('curl_init')) { - throw new \RuntimeException('cURL not installed'); + // Cannot test this, because curl_init is present on all test platforms plus mock + throw new RuntimeException('cURL not installed'); // @codeCoverageIgnore } } diff --git a/src/JsonSchema/Uri/Retrievers/FileGetContents.php b/src/JsonSchema/Uri/Retrievers/FileGetContents.php index 7f0c399a..7019814f 100644 --- a/src/JsonSchema/Uri/Retrievers/FileGetContents.php +++ b/src/JsonSchema/Uri/Retrievers/FileGetContents.php @@ -50,8 +50,10 @@ public function retrieve($uri) $this->messageBody = $response; if (!empty($http_response_header)) { - $this->fetchContentType($http_response_header); - } else { + // $http_response_header cannot be tested, because it's defined in the method's local scope + // See http://php.net/manual/en/reserved.variables.httpresponseheader.php for more info. + $this->fetchContentType($http_response_header); // @codeCoverageIgnore + } else { // @codeCoverageIgnore // Could be a "file://" url or something else - fake up the response $this->contentType = null; } diff --git a/tests/Constraints/CoerciveTest.php b/tests/Constraints/CoerciveTest.php index cb5c5518..36c0cf6b 100644 --- a/tests/Constraints/CoerciveTest.php +++ b/tests/Constraints/CoerciveTest.php @@ -116,6 +116,15 @@ public function testInvalidCoerceCasesUsingAssoc($input, $schema, $errors = arra $this->assertFalse($validator->isValid(), print_r($validator->getErrors(), true)); } + public function testCoerceAPI() + { + $input = json_decode('{"propertyOne": "10"}'); + $schema = json_decode('{"properties":{"propertyOne":{"type":"number"}}}'); + $v = new Validator(); + $v->coerce($input, $schema); + $this->assertEquals('{"propertyOne":10}', json_encode($input)); + } + public function getValidCoerceTests() { return array( diff --git a/tests/Constraints/TypeTest.php b/tests/Constraints/TypeTest.php index df8d6dd1..24138478 100644 --- a/tests/Constraints/TypeTest.php +++ b/tests/Constraints/TypeTest.php @@ -92,4 +92,31 @@ private function assertTypeConstraintError($expected, TypeConstraint $actual) $this->assertEquals($expected, $actualMessage); // first equal for the diff $this->assertSame($expected, $actualMessage); // the same for the strictness } + + public function testValidateTypeNameWording() + { + $t = new TypeConstraint(); + $r = new \ReflectionObject($t); + $m = $r->getMethod('validateTypeNameWording'); + $m->setAccessible(true); + + $this->setExpectedException( + '\UnexpectedValueException', + "No wording for 'notAValidTypeName' available, expected wordings are: [an integer, a number, a boolean, an object, an array, a string, a null]" + ); + $m->invoke($t, 'notAValidTypeName'); + } + + public function testValidateTypeException() + { + $t = new TypeConstraint(); + $data = new \StdClass(); + $schema = json_decode('{"type": "notAValidTypeName"}'); + + $this->setExpectedException( + 'JsonSchema\Exception\InvalidArgumentException', + 'object is an invalid type for notAValidTypeName' + ); + $t->check($data, $schema); + } } diff --git a/tests/Entity/JsonPointerTest.php b/tests/Entity/JsonPointerTest.php index 6a5ff4bf..65859895 100644 --- a/tests/Entity/JsonPointerTest.php +++ b/tests/Entity/JsonPointerTest.php @@ -109,4 +109,13 @@ public function testJsonPointerWithPropertyPaths() $this->assertEquals(array('~definitions/general', '%custom%'), $modified->getPropertyPaths()); $this->assertEquals('#/~0definitions~1general/%25custom%25', $modified->getPropertyPathAsString()); } + + public function testCreateWithInvalidValue() + { + $this->setExpectedException( + '\JsonSchema\Exception\InvalidArgumentException', + 'Ref value must be a string' + ); + new JsonPointer(null); + } } diff --git a/tests/Iterators/ObjectIteratorTest.php b/tests/Iterators/ObjectIteratorTest.php new file mode 100644 index 00000000..703df833 --- /dev/null +++ b/tests/Iterators/ObjectIteratorTest.php @@ -0,0 +1,89 @@ +testObject = (object) array( + 'subOne' => (object) array( + 'propertyOne' => 'valueOne', + 'propertyTwo' => 'valueTwo', + 'propertyThree' => 'valueThree' + ), + 'subTwo' => (object) array( + 'propertyFour' => 'valueFour', + 'subThree' => (object) array( + 'propertyFive' => 'valueFive', + 'propertySix' => 'valueSix' + ) + ), + 'propertySeven' => 'valueSeven' + ); + } + + public function testCreate() + { + $i = new ObjectIterator($this->testObject); + + $this->assertInstanceOf('\JsonSchema\Iterator\ObjectIterator', $i); + } + + public function testInitialState() + { + $i = new ObjectIterator($this->testObject); + + $this->assertEquals($this->testObject, $i->current()); + } + + public function testCount() + { + $i = new ObjectIterator($this->testObject); + + $this->assertEquals(4, $i->count()); + } + + public function testKey() + { + $i = new ObjectIterator($this->testObject); + + while ($i->key() != 2) { + $i->next(); + } + + $this->assertEquals($this->testObject->subTwo->subThree, $i->current()); + } + + public function testAlwaysObjects() + { + $i= new ObjectIterator($this->testObject); + + foreach ($i as $item) { + $this->assertInstanceOf('\StdClass', $item); + } + } + + public function testReachesAllProperties() + { + $i = new ObjectIterator($this->testObject); + + $count = 0; + foreach ($i as $item) { + $count += count(get_object_vars($item)); + } + + $this->assertEquals(10, $count); + } +} diff --git a/tests/SchemaStorageTest.php b/tests/SchemaStorageTest.php index 92e1d5c3..cf20ff7b 100644 --- a/tests/SchemaStorageTest.php +++ b/tests/SchemaStorageTest.php @@ -264,4 +264,18 @@ private function getInvalidSchema() ) ); } + + public function testGetUriRetriever() + { + $s = new SchemaStorage(); + $s->addSchema('http://json-schema.org/draft-04/schema#'); + $this->assertInstanceOf('\JsonSchema\Uri\UriRetriever', $s->getUriRetriever()); + } + + public function testGetUriResolver() + { + $s = new SchemaStorage(); + $s->addSchema('http://json-schema.org/draft-04/schema#'); + $this->assertInstanceOf('\JsonSchema\Uri\UriResolver', $s->getUriResolver()); + } } diff --git a/tests/Uri/Retrievers/CurlTest.php b/tests/Uri/Retrievers/CurlTest.php new file mode 100644 index 00000000..f833b590 --- /dev/null +++ b/tests/Uri/Retrievers/CurlTest.php @@ -0,0 +1,57 @@ +retrieve(realpath(__DIR__ . '/../../fixtures/foobar.json')); + } + + public function testRetrieveNonexistantFile() + { + $c = new Curl(); + + $this->setExpectedException( + '\JsonSchema\Exception\ResourceNotFoundException', + 'JSON schema not found' + ); + $c->retrieve(__DIR__ . '/notARealFile'); + } + + public function testNoContentType() + { + $c = new Curl(); + $c->retrieve(realpath(__DIR__ . '/../../fixtures') . '/foobar-noheader.json'); + } + } +} + +namespace JsonSchema\Uri\Retrievers +{ + function curl_exec($curl) + { + $uri = curl_getinfo($curl, \CURLINFO_EFFECTIVE_URL); + + if ($uri === realpath(__DIR__ . '/../../fixtures/foobar.json')) { + // return file with headers + $headers = implode("\n", array( + 'Content-Type: application/json' + )); + + return sprintf("%s\r\n\r\n%s", $headers, file_get_contents($uri)); + } elseif ($uri === realpath(__DIR__ . '/../../fixtures') . '/foobar-noheader.json') { + // return file without headers + $uri = realpath(__DIR__ . '/../../fixtures/foobar.json'); + + return "\r\n\r\n" . file_get_contents($uri); + } + + // fallback to real curl_exec + return \curl_exec($curl); + } +} diff --git a/tests/Uri/Retrievers/FileGetContentsTest.php b/tests/Uri/Retrievers/FileGetContentsTest.php index 7b67facb..d9b06263 100644 --- a/tests/Uri/Retrievers/FileGetContentsTest.php +++ b/tests/Uri/Retrievers/FileGetContentsTest.php @@ -1,27 +1,74 @@ retrieve(__DIR__ . '/Fixture/missing.json'); + /** + * @expectedException \JsonSchema\Exception\ResourceNotFoundException + */ + public function testFetchMissingFile() + { + $res = new FileGetContents(); + $res->retrieve(__DIR__ . '/Fixture/missing.json'); + } + + public function testFetchFile() + { + $res = new FileGetContents(); + $result = $res->retrieve(__DIR__ . '/../Fixture/child.json'); + $this->assertNotEmpty($result); + } + + public function testFalseReturn() + { + $res = new FileGetContents(); + + $this->setExpectedException( + '\JsonSchema\Exception\ResourceNotFoundException', + 'JSON schema not found at http://example.com/false' + ); + $res->retrieve('http://example.com/false'); + } + + public function testFetchDirectory() + { + $res = new FileGetContents(); + + $this->setExpectedException( + '\JsonSchema\Exception\ResourceNotFoundException', + 'JSON schema not found at file:///this/is/a/directory/' + ); + $res->retrieve('file:///this/is/a/directory/'); + } + + public function testContentType() + { + $res = new FileGetContents(); + + $reflector = new \ReflectionObject($res); + $fetchContentType = $reflector->getMethod('fetchContentType'); + $fetchContentType->setAccessible(true); + + $this->assertTrue($fetchContentType->invoke($res, array('Content-Type: application/json'))); + $this->assertFalse($fetchContentType->invoke($res, array('X-Some-Header: whateverValue'))); + } } +} - public function testFetchFile() +namespace JsonSchema\Uri\Retrievers +{ + function file_get_contents($uri) { - $res = new FileGetContents(); - $result = $res->retrieve(__DIR__ . '/../Fixture/child.json'); - $this->assertNotEmpty($result); + switch ($uri) { + case 'http://example.com/false': return false; + case 'file:///this/is/a/directory/': return ''; + default: return \file_get_contents($uri); + } } } diff --git a/tests/Uri/UriRetrieverTest.php b/tests/Uri/UriRetrieverTest.php index 01df161b..f5db5ca1 100644 --- a/tests/Uri/UriRetrieverTest.php +++ b/tests/Uri/UriRetrieverTest.php @@ -328,4 +328,88 @@ public function testRetrieveSchemaFromPackage() // check that the schema was loaded & processed correctly $this->assertEquals('454f423bd7edddf0bc77af4130ed9161', md5(json_encode($schema))); } + + public function testJsonSchemaOrgMediaTypeHack() + { + $mock = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); + $mock->method('getContentType')->willReturn('Application/X-Fake-Type'); + $retriever = new UriRetriever(); + + $this->assertTrue($retriever->confirmMediaType($mock, 'http://json-schema.org/')); + } + + public function testSchemaCache() + { + $retriever = new UriRetriever(); + $reflector = new \ReflectionObject($retriever); + + // inject a schema cache value + $schemaCache = $reflector->getProperty('schemaCache'); + $schemaCache->setAccessible(true); + $schemaCache->setValue($retriever, array('local://test/uri' => 'testSchemaValue')); + + // retrieve from schema cache + $loadSchema = $reflector->getMethod('loadSchema'); + $loadSchema->setAccessible(true); + $this->assertEquals( + 'testSchemaValue', + $loadSchema->invoke($retriever, 'local://test/uri') + ); + } + + public function testLoadSchemaJSONDecodingException() + { + $retriever = new UriRetriever(); + + $this->setExpectedException( + 'JsonSchema\Exception\JsonDecodingException', + 'JSON syntax is malformed' + ); + $schema = $retriever->retrieve('package://tests/fixtures/bad-syntax.json'); + } + + public function testGenerateURI() + { + $retriever = new UriRetriever(); + $components = array( + 'scheme' => 'scheme', + 'authority' => 'authority', + 'path' => '/path', + 'query' => '?query', + 'fragment' => '#fragment' + ); + $this->assertEquals('scheme://authority/path?query#fragment', $retriever->generate($components)); + } + + public function testResolveHTTP() + { + $retriever = new UriRetriever(); + $this->assertEquals( + 'http://example.com/schema', + $retriever->resolve('http://example.com/schema') + ); + } + + public function combinedURITests() + { + return array( + array('blue', 'http://example.com/red', 'http://example.com/blue'), + array('blue', 'http://example.com/', 'http://example.com/blue'), + ); + } + + /** + * @dataProvider combinedURITests + */ + public function testResolveCombinedURI($uri, $baseURI, $combinedURI) + { + $retriever = new UriRetriever(); + $this->assertEquals($combinedURI, $retriever->resolve($uri, $baseURI)); + } + + public function testIsValidURI() + { + $retriever = new UriRetriever(); + $this->assertTrue($retriever->isValid('http://example.com/schema')); + } } diff --git a/tests/fixtures/bad-syntax.json b/tests/fixtures/bad-syntax.json new file mode 100644 index 00000000..98232c64 --- /dev/null +++ b/tests/fixtures/bad-syntax.json @@ -0,0 +1 @@ +{