mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Tests for Config
This commit is contained in:
parent
c5bba0e7d4
commit
51d816d280
@ -59,6 +59,14 @@ class Config
|
||||
self::$allowCustomOptions = $allowCustomOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidationEnabled()
|
||||
{
|
||||
return self::$enableValidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config
|
||||
* @param array $definition
|
||||
@ -90,20 +98,6 @@ class Config
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $definition
|
||||
* @param int $flags
|
||||
* @return \stdClass
|
||||
*/
|
||||
public static function map(array $definition, $flags = 0)
|
||||
{
|
||||
$tmp = new \stdClass();
|
||||
$tmp->isMap = true;
|
||||
$tmp->definition = $definition;
|
||||
$tmp->flags = $flags;
|
||||
return $tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|int $definition
|
||||
* @param int $flags
|
||||
@ -133,7 +127,7 @@ class Config
|
||||
|
||||
if (!empty($unexpectedKeys)) {
|
||||
if (!self::$allowCustomOptions) {
|
||||
trigger_error(sprintf('"%s" type definition: Non-standard keys "%s" ' . $suffix, $typeName, implode(', ', $unexpectedKeys)));
|
||||
trigger_error(sprintf('Error in "%s" type definition: Non-standard keys "%s" ' . $suffix, $typeName, implode(', ', $unexpectedKeys)));
|
||||
}
|
||||
$map = array_intersect_key($map, $definitions);
|
||||
}
|
||||
@ -143,7 +137,7 @@ class Config
|
||||
$missingKeys = array_keys(array_diff_key($requiredKeys, $map));
|
||||
Utils::invariant(
|
||||
empty($missingKeys),
|
||||
"Error in '$typeName' type definition: Required keys missing: '%s' $suffix", implode(', ', $missingKeys)
|
||||
'Error in "' . $typeName . '" type definition: Required keys missing: "%s" %s', implode(', ', $missingKeys), $suffix
|
||||
);
|
||||
|
||||
// Make sure that every map value is valid given the definition
|
||||
@ -163,30 +157,26 @@ class Config
|
||||
private static function validateEntry($typeName, $key, $value, $def, $pathStr)
|
||||
{
|
||||
$type = Utils::getVariableType($value);
|
||||
$err = 'Error in "'.$typeName.'" type definition: expecting %s at "' . $pathStr . '", but got "' . $type . '"';
|
||||
$err = 'Error in "'.$typeName.'" type definition: expecting "%s" at "' . $pathStr . '", but got "' . $type . '"';
|
||||
|
||||
if ($def instanceof \stdClass) {
|
||||
if ($def->flags & self::REQUIRED === 0 && $value === null) {
|
||||
if (($def->flags & self::REQUIRED) === 0 && $value === null) {
|
||||
return ;
|
||||
}
|
||||
if (($def->flags & self::MAYBE_THUNK) > 0) {
|
||||
// TODO: consider wrapping thunk with other function to force validation of value returned by thunk
|
||||
Utils::invariant(is_array($value) || is_callable($value), $err, 'array or callable');
|
||||
} else {
|
||||
Utils::invariant(is_array($value), $err, 'array');
|
||||
}
|
||||
|
||||
if (!empty($def->isMap)) {
|
||||
if ($def->flags & self::KEY_AS_NAME) {
|
||||
$value += ['name' => $key];
|
||||
}
|
||||
self::validateMap($typeName, $value, $def->definition, $pathStr);
|
||||
} else if (!empty($def->isArray)) {
|
||||
if (!empty($def->isArray)) {
|
||||
|
||||
if ($def->flags & self::REQUIRED) {
|
||||
Utils::invariant(!empty($value), 'Error in "'.$typeName.'" type definition: ' . "Value at '$pathStr' cannot be empty array");
|
||||
}
|
||||
|
||||
$err = 'Error in "'.$typeName.'" type definition: ' . "Each entry at '$pathStr' must be an array, but '%s' is '%s'";
|
||||
$err = 'Error in "'.$typeName.'" type definition: ' . "Each entry at '$pathStr' must be an array, but entry at '%s' is '%s'";
|
||||
|
||||
foreach ($value as $arrKey => $arrValue) {
|
||||
if (is_array($def->definition)) {
|
||||
@ -199,7 +189,7 @@ class Config
|
||||
|
||||
Utils::invariant(is_array($arrValue), $err, $arrKey, Utils::getVariableType($arrValue));
|
||||
|
||||
if ($def->flags & self::KEY_AS_NAME) {
|
||||
if ($def->flags & self::KEY_AS_NAME && is_string($arrKey)) {
|
||||
$arrValue += ['name' => $arrKey];
|
||||
}
|
||||
self::validateMap($typeName, $arrValue, $def->definition, "$pathStr:$arrKey");
|
||||
@ -214,7 +204,7 @@ class Config
|
||||
Utils::invariant(is_int($def), 'Error in "'.$typeName.'" type definition: ' . "Definition for '$pathStr' is expected to be single integer value");
|
||||
|
||||
if ($def & self::REQUIRED) {
|
||||
Utils::invariant($value !== null, 'Value at "%s" can not be null', $pathStr);
|
||||
Utils::invariant($value !== null, 'Error in "'.$typeName.'" type definition: ' . 'Value at "%s" can not be null', $pathStr);
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
@ -234,7 +224,7 @@ class Config
|
||||
Utils::invariant(is_numeric($value), $err, 'numeric');
|
||||
break;
|
||||
case $def & self::FLOAT:
|
||||
Utils::invariant(is_float($value), $err, 'float');
|
||||
Utils::invariant(is_float($value) || is_int($value), $err, 'float');
|
||||
break;
|
||||
case $def & self::INT:
|
||||
Utils::invariant(is_int($value), $err, 'int');
|
||||
@ -246,6 +236,7 @@ class Config
|
||||
Utils::invariant(is_scalar($value), $err, 'scalar');
|
||||
break;
|
||||
case $def & self::NAME:
|
||||
Utils::invariant(is_string($value), $err, 'name');
|
||||
Utils::invariant(
|
||||
preg_match('~^[_a-zA-Z][_a-zA-Z0-9]*$~', $value),
|
||||
'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%s" does not.',
|
||||
@ -256,28 +247,28 @@ class Config
|
||||
Utils::invariant(
|
||||
is_callable($value) || $value instanceof InputType,
|
||||
$err,
|
||||
'callable or InputType definition'
|
||||
'InputType definition'
|
||||
);
|
||||
break;
|
||||
case $def & self::OUTPUT_TYPE:
|
||||
Utils::invariant(
|
||||
is_callable($value) || $value instanceof OutputType,
|
||||
$err,
|
||||
'callable or OutputType definition'
|
||||
'OutputType definition'
|
||||
);
|
||||
break;
|
||||
case $def & self::INTERFACE_TYPE:
|
||||
Utils::invariant(
|
||||
is_callable($value) || $value instanceof InterfaceType,
|
||||
$err,
|
||||
'callable or InterfaceType definition'
|
||||
'InterfaceType definition'
|
||||
);
|
||||
break;
|
||||
case $def & self::OBJECT_TYPE:
|
||||
Utils::invariant(
|
||||
is_callable($value) || $value instanceof ObjectType,
|
||||
$err,
|
||||
'callable or ObjectType definition'
|
||||
'ObjectType definition'
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
676
tests/Type/ConfigTest.php
Normal file
676
tests/Type/ConfigTest.php
Normal file
@ -0,0 +1,676 @@
|
||||
<?php
|
||||
namespace Type;
|
||||
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use GraphQL\Type\Definition\Config;
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
|
||||
class ConfigTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
Config::disableValidation();
|
||||
}
|
||||
|
||||
public function testToggling()
|
||||
{
|
||||
// Disabled by default
|
||||
$this->assertEquals(false, Config::isValidationEnabled());
|
||||
Config::validate(['test' => []], ['test' => Config::STRING]); // must not throw
|
||||
|
||||
Config::enableValidation();
|
||||
$this->assertEquals(true, Config::isValidationEnabled());
|
||||
|
||||
try {
|
||||
Config::validate(['test' => []], ['test' => Config::STRING]);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
Config::disableValidation();
|
||||
$this->assertEquals(false, Config::isValidationEnabled());
|
||||
Config::validate(['test' => []], ['test' => Config::STRING]);
|
||||
}
|
||||
|
||||
public function testValidateString()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
[
|
||||
'test' => 'string',
|
||||
'empty' => ''
|
||||
],
|
||||
[
|
||||
'test' => Config::STRING,
|
||||
'empty' => Config::STRING
|
||||
]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => 1],
|
||||
['test' => Config::STRING],
|
||||
$this->typeError('expecting "string" at "test", but got "integer"')
|
||||
);
|
||||
}
|
||||
|
||||
public function testArray()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
[],
|
||||
['nested' => 'A'],
|
||||
['nested' => null]
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING]
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
null
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING]
|
||||
)],
|
||||
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'NULL'")
|
||||
);
|
||||
|
||||
$this->expectValidationPasses(
|
||||
['test' => null],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING]
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => null],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING],
|
||||
Config::REQUIRED
|
||||
)],
|
||||
$this->typeError('expecting "array" at "test", but got "NULL"')
|
||||
);
|
||||
|
||||
// Check validation nesting:
|
||||
$this->expectValidationPasses(
|
||||
['nest' => [
|
||||
['nest' => [
|
||||
['test' => 'value']
|
||||
]]
|
||||
]],
|
||||
['nest' => Config::arrayOf([
|
||||
'nest' => Config::arrayOf([
|
||||
'test' => Config::STRING
|
||||
])
|
||||
])]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['nest' => [
|
||||
['nest' => [
|
||||
['test' => 'notInt']
|
||||
]]
|
||||
]],
|
||||
['nest' => Config::arrayOf([
|
||||
'nest' => Config::arrayOf([
|
||||
'test' => Config::INT
|
||||
])
|
||||
])],
|
||||
$this->typeError('expecting "int" at "nest:0:nest:0:test", but got "string"')
|
||||
);
|
||||
|
||||
// Check arrays of types:
|
||||
$this->expectValidationPasses(
|
||||
['nest' => [
|
||||
Type::string(),
|
||||
Type::int()
|
||||
]],
|
||||
['nest' => Config::arrayOf(
|
||||
Config::OUTPUT_TYPE, Config::REQUIRED
|
||||
)]
|
||||
);
|
||||
|
||||
// Check arrays of types:
|
||||
$this->expectValidationThrows(
|
||||
['nest' => [
|
||||
Type::string(),
|
||||
new InputObjectType(['name' => 'test', 'fields' => []])
|
||||
]],
|
||||
['nest' => Config::arrayOf(
|
||||
Config::OUTPUT_TYPE, Config::REQUIRED
|
||||
)],
|
||||
$this->typeError('expecting "OutputType definition" at "nest:1", but got "test"')
|
||||
);
|
||||
}
|
||||
|
||||
public function testRequired()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
['required' => ''],
|
||||
['required' => Config::STRING | Config::REQUIRED]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
[],
|
||||
['required' => Config::STRING | Config::REQUIRED],
|
||||
$this->typeError('Required keys missing: "required" ')
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['required' => null],
|
||||
['required' => Config::STRING | Config::REQUIRED],
|
||||
$this->typeError('Value at "required" can not be null')
|
||||
);
|
||||
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
['nested' => '']
|
||||
]],
|
||||
['test' => Config::arrayOf([
|
||||
'nested' => Config::STRING | Config::REQUIRED
|
||||
])]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
[]
|
||||
]],
|
||||
['test' => Config::arrayOf([
|
||||
'nested' => Config::STRING | Config::REQUIRED
|
||||
])],
|
||||
$this->typeError('Required keys missing: "nested" at test:0')
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
['nested' => null]
|
||||
]],
|
||||
['test' => Config::arrayOf([
|
||||
'nested' => Config::STRING | Config::REQUIRED
|
||||
])],
|
||||
$this->typeError('Value at "test:0:nested" can not be null')
|
||||
);
|
||||
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
['nested' => null]
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING],
|
||||
Config::REQUIRED
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['nested' => Config::STRING],
|
||||
Config::REQUIRED
|
||||
)],
|
||||
$this->typeError("Value at 'test' cannot be empty array")
|
||||
);
|
||||
}
|
||||
|
||||
public function testKeyAsName()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
'name1' => ['key1' => null],
|
||||
['name' => 'name1'],
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING],
|
||||
Config::KEY_AS_NAME
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
'name1' => ['key1' => null]
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING]
|
||||
)],
|
||||
$this->typeError('Required keys missing: "name" at test:name1')
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
['key1' => null]
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::STRING | Config::REQUIRED, 'key1' => Config::STRING],
|
||||
Config::KEY_AS_NAME
|
||||
)],
|
||||
$this->typeError('Required keys missing: "name" at test:0')
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaybeThunk()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
[
|
||||
'test' => [
|
||||
['nested' => ''],
|
||||
['nested' => '1'],
|
||||
],
|
||||
'testThunk' => function() {
|
||||
// Currently config won't validate thunk return value
|
||||
}
|
||||
],
|
||||
[
|
||||
'test' => Config::arrayOf(
|
||||
['nested' => Config::STRING | Config::REQUIRED],
|
||||
Config::MAYBE_THUNK
|
||||
),
|
||||
'testThunk' => Config::arrayOf(
|
||||
['nested' => Config::STRING | Config::REQUIRED],
|
||||
Config::MAYBE_THUNK
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
[
|
||||
'testThunk' => function() {
|
||||
|
||||
}
|
||||
],
|
||||
[
|
||||
'testThunk' => Config::arrayOf(
|
||||
['nested' => Config::STRING | Config::REQUIRED]
|
||||
)
|
||||
],
|
||||
$this->typeError('expecting "array" at "testThunk", but got "Closure"')
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
[
|
||||
'testThunk' => 1
|
||||
],
|
||||
[
|
||||
'testThunk' => Config::arrayOf(
|
||||
['nested' => Config::STRING | Config::REQUIRED],
|
||||
Config::MAYBE_THUNK
|
||||
)
|
||||
],
|
||||
$this->typeError('expecting "array or callable" at "testThunk", but got "integer"')
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaybeType()
|
||||
{
|
||||
$type = new ObjectType([
|
||||
'name' => 'Test',
|
||||
'fields' => []
|
||||
]);
|
||||
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
$type,
|
||||
['type' => $type],
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['type' => Config::OBJECT_TYPE | Config::REQUIRED],
|
||||
Config::MAYBE_TYPE
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
['type' => 'str']
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['type' => Config::OBJECT_TYPE | Config::REQUIRED],
|
||||
Config::MAYBE_TYPE
|
||||
)],
|
||||
$this->typeError('expecting "ObjectType definition" at "test:0:type", but got "string"')
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
$type
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::OBJECT_TYPE | Config::REQUIRED]
|
||||
)],
|
||||
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'Test'")
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaybeName()
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
'some-name',
|
||||
['name' => 'other-name'],
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::STRING | Config::REQUIRED],
|
||||
Config::MAYBE_NAME
|
||||
)]
|
||||
);
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => [
|
||||
'some-name'
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::OBJECT_TYPE | Config::REQUIRED]
|
||||
)],
|
||||
$this->typeError("Each entry at 'test' must be an array, but entry at '0' is 'string'")
|
||||
);
|
||||
|
||||
$this->expectValidationPasses(
|
||||
['test' => [
|
||||
'some-key' => 'some-name',
|
||||
'some-name'
|
||||
]],
|
||||
['test' => Config::arrayOf(
|
||||
['name' => Config::STRING | Config::REQUIRED],
|
||||
Config::MAYBE_NAME | Config::KEY_AS_NAME
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
public function getValidValues()
|
||||
{
|
||||
return [
|
||||
// $type, $validValue
|
||||
[Config::ANY, null],
|
||||
[Config::ANY, 0],
|
||||
[Config::ANY, ''],
|
||||
[Config::ANY, '0'],
|
||||
[Config::ANY, 1],
|
||||
[Config::ANY, function() {}],
|
||||
[Config::ANY, []],
|
||||
[Config::ANY, new \stdClass()],
|
||||
[Config::STRING, null],
|
||||
[Config::STRING, ''],
|
||||
[Config::STRING, '0'],
|
||||
[Config::STRING, 'anything'],
|
||||
[Config::BOOLEAN, null],
|
||||
[Config::BOOLEAN, false],
|
||||
[Config::BOOLEAN, true],
|
||||
[Config::INT, null],
|
||||
[Config::INT, 0],
|
||||
[Config::INT, 1],
|
||||
[Config::INT, -1],
|
||||
[Config::INT, 5000000],
|
||||
[Config::INT, -5000000],
|
||||
[Config::FLOAT, null],
|
||||
[Config::FLOAT, 0],
|
||||
[Config::FLOAT, 0.0],
|
||||
[Config::FLOAT, 0.1],
|
||||
[Config::FLOAT, -12.5],
|
||||
[Config::NUMERIC, null],
|
||||
[Config::NUMERIC, '0'],
|
||||
[Config::NUMERIC, 0],
|
||||
[Config::NUMERIC, 1],
|
||||
[Config::NUMERIC, 0.0],
|
||||
[Config::NUMERIC, 1.0],
|
||||
[Config::NUMERIC, -1.0],
|
||||
[Config::NUMERIC, -1],
|
||||
[Config::NUMERIC, 1],
|
||||
[Config::CALLBACK, null],
|
||||
[Config::CALLBACK, function() {}],
|
||||
[Config::CALLBACK, [$this, 'getValidValues']],
|
||||
[Config::SCALAR, null],
|
||||
[Config::SCALAR, 0],
|
||||
[Config::SCALAR, 1],
|
||||
[Config::SCALAR, 0.0],
|
||||
[Config::SCALAR, 1.0],
|
||||
[Config::SCALAR, true],
|
||||
[Config::SCALAR, false],
|
||||
[Config::SCALAR, ''],
|
||||
[Config::SCALAR, '0'],
|
||||
[Config::SCALAR, 'anything'],
|
||||
[Config::NAME, null],
|
||||
[Config::NAME, 'CamelCaseIsOk'],
|
||||
[Config::NAME, 'underscore_is_ok'],
|
||||
[Config::NAME, 'numbersAreOk0123456789'],
|
||||
[Config::INPUT_TYPE, null],
|
||||
[Config::INPUT_TYPE, new InputObjectType(['name' => 'test', 'fields' => []])],
|
||||
[Config::INPUT_TYPE, new EnumType(['name' => 'test2', 'values' => ['A', 'B', 'C']])],
|
||||
[Config::INPUT_TYPE, Type::string()],
|
||||
[Config::INPUT_TYPE, Type::int()],
|
||||
[Config::INPUT_TYPE, Type::float()],
|
||||
[Config::INPUT_TYPE, Type::boolean()],
|
||||
[Config::INPUT_TYPE, Type::id()],
|
||||
[Config::INPUT_TYPE, Type::listOf(Type::string())],
|
||||
[Config::INPUT_TYPE, Type::nonNull(Type::string())],
|
||||
[Config::OUTPUT_TYPE, null],
|
||||
[Config::OUTPUT_TYPE, new ObjectType(['name' => 'test3', 'fields' => []])],
|
||||
[Config::OUTPUT_TYPE, new EnumType(['name' => 'test4', 'values' => ['A', 'B', 'C']])],
|
||||
[Config::OUTPUT_TYPE, Type::string()],
|
||||
[Config::OUTPUT_TYPE, Type::int()],
|
||||
[Config::OUTPUT_TYPE, Type::float()],
|
||||
[Config::OUTPUT_TYPE, Type::boolean()],
|
||||
[Config::OUTPUT_TYPE, Type::id()],
|
||||
[Config::OBJECT_TYPE, null],
|
||||
[Config::OBJECT_TYPE, new ObjectType(['name' => 'test6', 'fields' => []])],
|
||||
[Config::INTERFACE_TYPE, null],
|
||||
[Config::INTERFACE_TYPE, new InterfaceType(['name' => 'test7', 'fields' => []])],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidValues
|
||||
*/
|
||||
public function testValidValues($type, $validValue)
|
||||
{
|
||||
$this->expectValidationPasses(
|
||||
['test' => $validValue],
|
||||
['test' => $type]
|
||||
);
|
||||
}
|
||||
|
||||
public function getInvalidValues()
|
||||
{
|
||||
return [
|
||||
// $type, $typeLabel, $invalidValue, $actualTypeLabel
|
||||
[Config::STRING, 'string', 1, 'integer'],
|
||||
[Config::STRING, 'string', 0, 'integer'],
|
||||
[Config::STRING, 'string', false, 'boolean'],
|
||||
[Config::STRING, 'string', function() {}, 'Closure'],
|
||||
[Config::STRING, 'string', [], 'array'],
|
||||
[Config::STRING, 'string', new \stdClass(), 'stdClass'],
|
||||
[Config::BOOLEAN, 'boolean', '', 'string'],
|
||||
[Config::BOOLEAN, 'boolean', 1, 'integer'],
|
||||
[Config::BOOLEAN, 'boolean', function() {}, 'Closure'],
|
||||
[Config::BOOLEAN, 'boolean', [], 'array'],
|
||||
[Config::BOOLEAN, 'boolean', new \stdClass(), 'stdClass'],
|
||||
[Config::INT, 'int', false, 'boolean'],
|
||||
[Config::INT, 'int', '', 'string'],
|
||||
[Config::INT, 'int', '0', 'string'],
|
||||
[Config::INT, 'int', '1', 'string'],
|
||||
[Config::INT, 'int', function() {}, 'Closure'],
|
||||
[Config::INT, 'int', [], 'array'],
|
||||
[Config::INT, 'int', new \stdClass(), 'stdClass'],
|
||||
[Config::FLOAT, 'float', '', 'string'],
|
||||
[Config::FLOAT, 'float', '0', 'string'],
|
||||
[Config::FLOAT, 'float', function() {}, 'Closure'],
|
||||
[Config::FLOAT, 'float', [], 'array'],
|
||||
[Config::FLOAT, 'float', new \stdClass(), 'stdClass'],
|
||||
[Config::NUMERIC, 'numeric', '', 'string'],
|
||||
[Config::NUMERIC, 'numeric', 'tmp', 'string'],
|
||||
[Config::NUMERIC, 'numeric', [], 'array'],
|
||||
[Config::NUMERIC, 'numeric', new \stdClass(), 'stdClass'],
|
||||
[Config::NUMERIC, 'numeric', function() {}, 'Closure'],
|
||||
[Config::CALLBACK, 'callable', 1, 'integer'],
|
||||
[Config::CALLBACK, 'callable', '', 'string'],
|
||||
[Config::CALLBACK, 'callable', [], 'array'],
|
||||
[Config::CALLBACK, 'callable', new \stdClass(), 'stdClass'],
|
||||
[Config::SCALAR, 'scalar', [], 'array'],
|
||||
[Config::SCALAR, 'scalar', new \stdClass(), 'stdClass'],
|
||||
[Config::SCALAR, 'scalar', function() {}, 'Closure'],
|
||||
[Config::NAME, 'name', 5, 'integer'],
|
||||
[Config::NAME, 'name', function() {}, 'Closure'],
|
||||
[Config::NAME, 'name', [], 'array'],
|
||||
[Config::NAME, 'name', new \stdClass(), 'stdClass'],
|
||||
[Config::NAME, 'name', '', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "" does not.'],
|
||||
[Config::NAME, 'name', '0', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "0" does not.'],
|
||||
[Config::NAME, 'name', '4abc', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "4abc" does not.'],
|
||||
[Config::NAME, 'name', 'specialCharsAreBad!', null, 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "specialCharsAreBad!" does not.'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', new ObjectType(['name' => 'test3', 'fields' => []]), 'test3'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', '', 'string'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', 'test', 'string'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', 1, 'integer'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', 0.5, 'double'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', false, 'boolean'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', [], 'array'],
|
||||
[Config::INPUT_TYPE, 'InputType definition', new \stdClass(), 'stdClass'],
|
||||
[Config::OUTPUT_TYPE, 'OutputType definition', new InputObjectType(['name' => 'InputTypeTest']), 'InputTypeTest'],
|
||||
[Config::OBJECT_TYPE, 'ObjectType definition', '', 'string'],
|
||||
[Config::OBJECT_TYPE, 'ObjectType definition', new InputObjectType(['name' => 'InputTypeTest2']), 'InputTypeTest2'],
|
||||
[Config::INTERFACE_TYPE, 'InterfaceType definition', new ObjectType(['name' => 'ObjectTypeTest']), 'ObjectTypeTest'],
|
||||
[Config::INTERFACE_TYPE, 'InterfaceType definition', 'InputTypeTest2', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getInvalidValues
|
||||
*/
|
||||
public function testInvalidValues($type, $typeLabel, $invalidValue, $actualTypeLabel = null, $expectedFullError = null)
|
||||
{
|
||||
if (!$expectedFullError) {
|
||||
$expectedFullError = $this->typeError(
|
||||
$invalidValue === null ?
|
||||
'Value at "test" can not be null' :
|
||||
'expecting "' . $typeLabel . '" at "test", but got "' . $actualTypeLabel . '"'
|
||||
);
|
||||
}
|
||||
|
||||
$this->expectValidationThrows(
|
||||
['test' => $invalidValue],
|
||||
['test' => $type],
|
||||
$expectedFullError
|
||||
);
|
||||
}
|
||||
|
||||
public function testErrorMessageContainsTypeName()
|
||||
{
|
||||
$this->expectValidationThrows(
|
||||
[
|
||||
'name' => 'TypeName',
|
||||
'test' => 'notInt'
|
||||
],
|
||||
[
|
||||
'name' => Config::STRING | Config::REQUIRED,
|
||||
'test' => Config::INT
|
||||
],
|
||||
$this->typeError('expecting "int" at "test", but got "string"', 'TypeName')
|
||||
);
|
||||
}
|
||||
|
||||
public function testValidateField()
|
||||
{
|
||||
Config::enableValidation();
|
||||
|
||||
// Should just validate:
|
||||
Config::validateField(
|
||||
'TypeName',
|
||||
['test' => 'value'],
|
||||
['test' => Config::STRING]
|
||||
);
|
||||
|
||||
// Should include type name in error
|
||||
try {
|
||||
Config::validateField(
|
||||
'TypeName',
|
||||
['test' => 'notInt'],
|
||||
['test' => Config::INT]
|
||||
);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (InvariantViolation $e) {
|
||||
$this->assertEquals(
|
||||
$this->typeError('expecting "int" at "(Unknown Field):test", but got "string"', 'TypeName'),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
// Should include field type in error when field name is unknown:
|
||||
try {
|
||||
Config::validateField(
|
||||
'TypeName',
|
||||
['type' => Type::string()],
|
||||
['name' => Config::STRING | Config::REQUIRED, 'type' => Config::OUTPUT_TYPE]
|
||||
);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (InvariantViolation $e) {
|
||||
$this->assertEquals(
|
||||
$this->typeError('Required keys missing: "name" at (Unknown Field of type: String)', 'TypeName'),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
// Should include field name in error when field name is set:
|
||||
try {
|
||||
Config::validateField(
|
||||
'TypeName',
|
||||
['name' => 'fieldName', 'test' => 'notInt'],
|
||||
['name' => Config::STRING, 'test' => Config::INT]
|
||||
);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (InvariantViolation $e) {
|
||||
$this->assertEquals(
|
||||
$this->typeError('expecting "int" at "test", but got "string"', 'TypeName'),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testAllowCustomOptions()
|
||||
{
|
||||
// Disabled by default when validation is enabled
|
||||
Config::enableValidation(true);
|
||||
|
||||
Config::validate(
|
||||
['test' => 'value', 'test2' => 'value'],
|
||||
['test' => Config::STRING]
|
||||
);
|
||||
|
||||
Config::enableValidation(false);
|
||||
|
||||
try {
|
||||
Config::validate(
|
||||
['test' => 'value', 'test2' => 'value'],
|
||||
['test' => Config::STRING]
|
||||
);
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\PHPUnit_Framework_Error_Notice $e) {
|
||||
$this->assertEquals(
|
||||
$this->typeError('Non-standard keys "test2" '),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function expectValidationPasses($config, $definition)
|
||||
{
|
||||
Config::enableValidation(false);
|
||||
Config::validate($config, $definition);
|
||||
}
|
||||
|
||||
private function expectValidationThrows($config, $definition, $expectedError)
|
||||
{
|
||||
Config::enableValidation(false);
|
||||
try {
|
||||
Config::validate($config, $definition);
|
||||
$this->fail('Expected exception not thrown: ' . $expectedError);
|
||||
} catch (InvariantViolation $e) {
|
||||
$this->assertEquals($expectedError, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function typeError($err, $typeName = null)
|
||||
{
|
||||
return 'Error in "'. ($typeName ?: '(Unnamed Type)') . '" type definition: ' . $err;
|
||||
}
|
||||
}
|
@ -469,7 +469,7 @@ class DefinitionTest extends \PHPUnit_Framework_TestCase
|
||||
$this->fail('Expected exception not thrown');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame(
|
||||
'Error in "BadUnion" type definition: expecting callable or ObjectType definition at "types:0", but got "' . Utils::getVariableType($type) . '"',
|
||||
'Error in "BadUnion" type definition: expecting "ObjectType definition" at "types:0", but got "' . Utils::getVariableType($type) . '"',
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
namespace GraphQL\Tests\Type;
|
||||
|
||||
use GraphQL\Schema;
|
||||
use GraphQL\Type\Definition\Config;
|
||||
use GraphQL\Type\Definition\CustomScalarType;
|
||||
use GraphQL\Type\Definition\EnumType;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
@ -392,6 +393,9 @@ class SchemaValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testRejectsASchemaThatUsesAnInputTypeAsAField()
|
||||
{
|
||||
$enabled = Config::isValidationEnabled();
|
||||
Config::disableValidation();
|
||||
|
||||
$kinds = [
|
||||
'GraphQL\Type\Definition\ObjectType',
|
||||
];
|
||||
@ -415,6 +419,10 @@ class SchemaValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
$validationResult[0]->message
|
||||
);
|
||||
}
|
||||
|
||||
if ($enabled) {
|
||||
Config::enableValidation();
|
||||
}
|
||||
}
|
||||
|
||||
public function testAcceptsASchemaThatSimplyHasAnInputTypeAsAFieldArg()
|
||||
|
Loading…
Reference in New Issue
Block a user