mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Schema validation improvements
This commit is contained in:
parent
89fa0c3e67
commit
bdbb30c604
@ -14,6 +14,7 @@ use Throwable;
|
|||||||
use Traversable;
|
use Traversable;
|
||||||
use function array_filter;
|
use function array_filter;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
|
use function array_values;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function iterator_to_array;
|
use function iterator_to_array;
|
||||||
|
|
||||||
@ -232,12 +233,14 @@ class Error extends Exception implements JsonSerializable, ClientAware
|
|||||||
$this->nodes
|
$this->nodes
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->positions = array_filter(
|
$positions = array_filter(
|
||||||
$positions,
|
$positions,
|
||||||
static function ($p) {
|
static function ($p) {
|
||||||
return $p !== null;
|
return $p !== null;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->positions = array_values($positions);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->positions;
|
return $this->positions;
|
||||||
@ -273,7 +276,7 @@ class Error extends Exception implements JsonSerializable, ClientAware
|
|||||||
$positions
|
$positions
|
||||||
);
|
);
|
||||||
} elseif ($nodes) {
|
} elseif ($nodes) {
|
||||||
$this->locations = array_filter(
|
$locations = array_filter(
|
||||||
array_map(
|
array_map(
|
||||||
static function ($node) {
|
static function ($node) {
|
||||||
if ($node->loc && $node->loc->source) {
|
if ($node->loc && $node->loc->source) {
|
||||||
@ -283,6 +286,7 @@ class Error extends Exception implements JsonSerializable, ClientAware
|
|||||||
$nodes
|
$nodes
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
$this->locations = array_values($locations);
|
||||||
} else {
|
} else {
|
||||||
$this->locations = [];
|
$this->locations = [];
|
||||||
}
|
}
|
||||||
|
@ -144,12 +144,17 @@ class TypeInfo
|
|||||||
return self::extractTypes($type->getWrappedType(true), $typeMap);
|
return self::extractTypes($type->getWrappedType(true), $typeMap);
|
||||||
}
|
}
|
||||||
if (! $type instanceof Type) {
|
if (! $type instanceof Type) {
|
||||||
Warning::warnOnce(
|
// Preserve these invalid types in map (at numeric index) to make them
|
||||||
'One of the schema types is not a valid type definition instance. ' .
|
// detectable during $schema->validate()
|
||||||
'Try running $schema->assertValid() to find out the cause of this warning.',
|
$i = 0;
|
||||||
Warning::WARNING_NOT_A_TYPE
|
$alreadyInMap = false;
|
||||||
);
|
while (isset($typeMap[$i])) {
|
||||||
|
$alreadyInMap = $alreadyInMap || $typeMap[$i] === $type;
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
if (! $alreadyInMap) {
|
||||||
|
$typeMap[$i] = $type;
|
||||||
|
}
|
||||||
return $typeMap;
|
return $typeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class Utils
|
|||||||
/**
|
/**
|
||||||
* @param mixed|Traversable $traversable
|
* @param mixed|Traversable $traversable
|
||||||
*
|
*
|
||||||
* @return int[][]
|
* @return mixed[]
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
|
@ -7,6 +7,7 @@ namespace GraphQL\Tests\Type;
|
|||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Error\Warning;
|
use GraphQL\Error\Warning;
|
||||||
|
use GraphQL\Language\SourceLocation;
|
||||||
use GraphQL\Type\Definition\CustomScalarType;
|
use GraphQL\Type\Definition\CustomScalarType;
|
||||||
use GraphQL\Type\Definition\EnumType;
|
use GraphQL\Type\Definition\EnumType;
|
||||||
use GraphQL\Type\Definition\InputObjectType;
|
use GraphQL\Type\Definition\InputObjectType;
|
||||||
@ -76,12 +77,16 @@ class ValidationTest extends TestCase
|
|||||||
|
|
||||||
$this->SomeInterfaceType = new InterfaceType([
|
$this->SomeInterfaceType = new InterfaceType([
|
||||||
'name' => 'SomeInterface',
|
'name' => 'SomeInterface',
|
||||||
'fields' => ['f' => ['type' => Type::string()]],
|
'fields' => function () {
|
||||||
|
return ['f' => ['type' => $this->SomeObjectType]];
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->SomeObjectType = new ObjectType([
|
$this->SomeObjectType = new ObjectType([
|
||||||
'name' => 'SomeObject',
|
'name' => 'SomeObject',
|
||||||
'fields' => ['f' => ['type' => Type::string()]],
|
'fields' => function () {
|
||||||
|
return ['f' => ['type' => $this->SomeObjectType]];
|
||||||
|
},
|
||||||
'interfaces' => function () {
|
'interfaces' => function () {
|
||||||
return [$this->SomeInterfaceType];
|
return [$this->SomeInterfaceType];
|
||||||
},
|
},
|
||||||
@ -115,10 +120,9 @@ class ValidationTest extends TestCase
|
|||||||
$this->SomeInterfaceType,
|
$this->SomeInterfaceType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->notOutputTypes = $this->withModifiers([
|
$this->notOutputTypes = $this->withModifiers([
|
||||||
$this->SomeInputObjectType,
|
$this->SomeInputObjectType,
|
||||||
]);
|
]);
|
||||||
$this->notOutputTypes[] = $this->Number;
|
|
||||||
|
|
||||||
$this->inputTypes = $this->withModifiers([
|
$this->inputTypes = $this->withModifiers([
|
||||||
Type::string(),
|
Type::string(),
|
||||||
@ -133,8 +137,6 @@ class ValidationTest extends TestCase
|
|||||||
$this->SomeInterfaceType,
|
$this->SomeInterfaceType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->notInputTypes[] = $this->Number;
|
|
||||||
|
|
||||||
Warning::suppress(Warning::WARNING_NOT_A_TYPE);
|
Warning::suppress(Warning::WARNING_NOT_A_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +310,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'Query root type must be provided.']]
|
[['message' => 'Query root type must be provided.']]
|
||||||
);
|
);
|
||||||
@ -323,7 +325,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schemaWithDef->validate(),
|
$schemaWithDef->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Query root type must be provided.',
|
'message' => 'Query root type must be provided.',
|
||||||
@ -333,47 +335,45 @@ class ValidationTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function formatLocations(Error $error)
|
||||||
* @param InvariantViolation[]|Error[] $array
|
|
||||||
* @param string[][] $messages
|
|
||||||
*/
|
|
||||||
private function assertContainsValidationMessage($array, $messages)
|
|
||||||
{
|
{
|
||||||
$allErrors = implode(
|
return Utils::map($error->getLocations(), static function (SourceLocation $loc) {
|
||||||
"\n",
|
return ['line' => $loc->line, 'column' => $loc->column];
|
||||||
array_map(
|
});
|
||||||
static function ($error) {
|
}
|
||||||
return $error->getMessage();
|
|
||||||
},
|
|
||||||
$array
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($messages as $expected) {
|
/**
|
||||||
$msg = $expected['message'];
|
* @param Error[] $errors
|
||||||
$locations = $expected['locations'] ?? [];
|
* @param bool $withLocation
|
||||||
foreach ($array as $actual) {
|
*
|
||||||
if ($actual instanceof Error && $actual->getMessage() === $msg) {
|
* @return mixed[]
|
||||||
$actualLocations = [];
|
*/
|
||||||
foreach ($actual->getLocations() as $location) {
|
private function formatErrors(array $errors, $withLocation = true)
|
||||||
$actualLocations[] = $location->toArray();
|
{
|
||||||
}
|
return Utils::map($errors, function (Error $error) use ($withLocation) {
|
||||||
self::assertEquals(
|
if (! $withLocation) {
|
||||||
$locations,
|
return [ 'message' => $error->getMessage() ];
|
||||||
$actualLocations,
|
}
|
||||||
sprintf(
|
|
||||||
'Locations do not match for error: %s\n\nExpected:\n%s\n\nActual:\n%s',
|
return [
|
||||||
$msg,
|
'message' => $error->getMessage(),
|
||||||
print_r($locations, true),
|
'locations' => $this->formatLocations($error),
|
||||||
print_r($actualLocations, true)
|
];
|
||||||
)
|
});
|
||||||
);
|
}
|
||||||
// Found and valid, so check the next message (and don't fail)
|
|
||||||
continue 2;
|
private function assertMatchesValidationMessage($errors, $expected)
|
||||||
}
|
{
|
||||||
|
$expectedWithLocations = [];
|
||||||
|
foreach ($expected as $index => $err) {
|
||||||
|
if (! isset($err['locations']) && isset($errors[$index])) {
|
||||||
|
$expectedWithLocations[$index] = $err + ['locations' => $this->formatLocations($errors[$index])];
|
||||||
|
} else {
|
||||||
|
$expectedWithLocations[$index] = $err;
|
||||||
}
|
}
|
||||||
self::fail(sprintf("Expected error not found:\n%s\n\nActual errors:\n%s", $msg, $allErrors));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::assertEquals($expectedWithLocations, $this->formatErrors($errors));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -387,7 +387,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Query root type must be Object type, it cannot be Query.',
|
'message' => 'Query root type must be Object type, it cannot be Query.',
|
||||||
@ -406,7 +406,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schemaWithDef->validate(),
|
$schemaWithDef->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Query root type must be Object type, it cannot be SomeInputObject.',
|
'message' => 'Query root type must be Object type, it cannot be SomeInputObject.',
|
||||||
@ -431,7 +431,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Mutation root type must be Object type if provided, it cannot be Mutation.',
|
'message' => 'Mutation root type must be Object type if provided, it cannot be Mutation.',
|
||||||
@ -455,7 +455,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schemaWithDef->validate(),
|
$schemaWithDef->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Mutation root type must be Object type if provided, it cannot be SomeInputObject.',
|
'message' => 'Mutation root type must be Object type if provided, it cannot be SomeInputObject.',
|
||||||
@ -482,7 +482,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Subscription root type must be Object type if provided, it cannot be Subscription.',
|
'message' => 'Subscription root type must be Object type if provided, it cannot be Subscription.',
|
||||||
@ -506,7 +506,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schemaWithDef->validate(),
|
$schemaWithDef->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Subscription root type must be Object type if provided, it cannot be SomeInputObject.',
|
'message' => 'Subscription root type must be Object type if provided, it cannot be SomeInputObject.',
|
||||||
@ -526,7 +526,7 @@ class ValidationTest extends TestCase
|
|||||||
'directives' => ['somedirective'],
|
'directives' => ['somedirective'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'Expected directive but got: somedirective.']]
|
[['message' => 'Expected directive but got: somedirective.']]
|
||||||
);
|
);
|
||||||
@ -563,7 +563,7 @@ class ValidationTest extends TestCase
|
|||||||
type IncompleteObject
|
type IncompleteObject
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Type IncompleteObject must define one or more fields.',
|
'message' => 'Type IncompleteObject must define one or more fields.',
|
||||||
@ -579,7 +579,7 @@ class ValidationTest extends TestCase
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$manualSchema->validate(),
|
$manualSchema->validate(),
|
||||||
[['message' => 'Type IncompleteObject must define one or more fields.']]
|
[['message' => 'Type IncompleteObject must define one or more fields.']]
|
||||||
);
|
);
|
||||||
@ -593,7 +593,7 @@ class ValidationTest extends TestCase
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$manualSchema2->validate(),
|
$manualSchema2->validate(),
|
||||||
[['message' => 'Type IncompleteObject must define one or more fields.']]
|
[['message' => 'Type IncompleteObject must define one or more fields.']]
|
||||||
);
|
);
|
||||||
@ -604,18 +604,12 @@ class ValidationTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
private function schemaWithFieldType($type) : Schema
|
private function schemaWithFieldType($type) : Schema
|
||||||
{
|
{
|
||||||
$ifaceImplementation = new ObjectType([
|
|
||||||
'name' => 'SomeInterfaceImplementation',
|
|
||||||
'fields' => ['f' => ['type' => Type::string()]],
|
|
||||||
'interfaces' => [ $this->SomeInterfaceType ],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return new Schema([
|
return new Schema([
|
||||||
'query' => new ObjectType([
|
'query' => new ObjectType([
|
||||||
'name' => 'Query',
|
'name' => 'Query',
|
||||||
'fields' => ['f' => ['type' => $type]],
|
'fields' => ['f' => ['type' => $type]],
|
||||||
]),
|
]),
|
||||||
'types' => [$type, $ifaceImplementation],
|
'types' => [$type],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,7 +627,7 @@ class ValidationTest extends TestCase
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but ' .
|
'message' => 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but ' .
|
||||||
@ -697,7 +691,7 @@ class ValidationTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
$schema = new Schema(['query' => $QueryType]);
|
$schema = new Schema(['query' => $QueryType]);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.']]
|
[['message' => 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.']]
|
||||||
);
|
);
|
||||||
@ -743,7 +737,7 @@ class ValidationTest extends TestCase
|
|||||||
|
|
||||||
union BadUnion
|
union BadUnion
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Union type BadUnion must define one or more member types.',
|
'message' => 'Union type BadUnion must define one or more member types.',
|
||||||
@ -776,7 +770,7 @@ class ValidationTest extends TestCase
|
|||||||
| TypeB
|
| TypeB
|
||||||
| TypeA
|
| TypeA
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Union type BadUnion can only include type TypeA once.',
|
'message' => 'Union type BadUnion can only include type TypeA once.',
|
||||||
@ -809,7 +803,7 @@ class ValidationTest extends TestCase
|
|||||||
| String
|
| String
|
||||||
| TypeB
|
| TypeB
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Union type BadUnion can only include Object types, ' .
|
'message' => 'Union type BadUnion can only include Object types, ' .
|
||||||
@ -833,7 +827,7 @@ class ValidationTest extends TestCase
|
|||||||
$badSchema = $this->schemaWithFieldType(
|
$badSchema = $this->schemaWithFieldType(
|
||||||
new UnionType(['name' => 'BadUnion', 'types' => [$memberType]])
|
new UnionType(['name' => 'BadUnion', 'types' => [$memberType]])
|
||||||
);
|
);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$badSchema->validate(),
|
$badSchema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Union type BadUnion can only include Object types, ' .
|
'message' => 'Union type BadUnion can only include Object types, ' .
|
||||||
@ -875,7 +869,7 @@ class ValidationTest extends TestCase
|
|||||||
|
|
||||||
input SomeInputObject
|
input SomeInputObject
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Input Object type SomeInputObject must define one or more fields.',
|
'message' => 'Input Object type SomeInputObject must define one or more fields.',
|
||||||
@ -907,7 +901,7 @@ class ValidationTest extends TestCase
|
|||||||
goodInputObject: SomeInputObject
|
goodInputObject: SomeInputObject
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@ -934,7 +928,7 @@ class ValidationTest extends TestCase
|
|||||||
|
|
||||||
enum SomeEnum
|
enum SomeEnum
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Enum type SomeEnum must define one or more values.',
|
'message' => 'Enum type SomeEnum must define one or more values.',
|
||||||
@ -959,7 +953,7 @@ class ValidationTest extends TestCase
|
|||||||
SOME_VALUE
|
SOME_VALUE
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Enum type SomeEnum can include value SOME_VALUE only once.',
|
'message' => 'Enum type SomeEnum can include value SOME_VALUE only once.',
|
||||||
@ -1008,7 +1002,7 @@ class ValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
$schema = $this->schemaWithEnum($name);
|
$schema = $this->schemaWithEnum($name);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => $expectedMessage],
|
[['message' => $expectedMessage],
|
||||||
]
|
]
|
||||||
@ -1068,7 +1062,7 @@ class ValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
$schema = $this->schemaWithObjectFieldOfType(null);
|
$schema = $this->schemaWithObjectFieldOfType(null);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'The type of BadObject.badField must be Output Type but got: null.'],
|
[['message' => 'The type of BadObject.badField must be Output Type but got: null.'],
|
||||||
]
|
]
|
||||||
@ -1083,7 +1077,7 @@ class ValidationTest extends TestCase
|
|||||||
foreach ($this->notOutputTypes as $type) {
|
foreach ($this->notOutputTypes as $type) {
|
||||||
$schema = $this->schemaWithObjectFieldOfType($type);
|
$schema = $this->schemaWithObjectFieldOfType($type);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'The type of BadObject.badField must be Output Type but got: ' . Utils::printSafe($type) . '.',
|
'message' => 'The type of BadObject.badField must be Output Type but got: ' . Utils::printSafe($type) . '.',
|
||||||
@ -1093,6 +1087,21 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('rejects a non-type value as an Object field type')
|
||||||
|
*/
|
||||||
|
public function testRejectsANonTypeValueAsAnObjectFieldType()
|
||||||
|
{
|
||||||
|
$schema = $this->schemaWithObjectFieldOfType($this->Number);
|
||||||
|
$this->assertMatchesValidationMessage(
|
||||||
|
$schema->validate(),
|
||||||
|
[
|
||||||
|
['message' => 'The type of BadObject.badField must be Output Type but got: 1.'],
|
||||||
|
['message' => 'Expected GraphQL named type but got: 1.'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('rejects with relevant locations for a non-output type as an Object field type')
|
* @see it('rejects with relevant locations for a non-output type as an Object field type')
|
||||||
*/
|
*/
|
||||||
@ -1107,7 +1116,7 @@ class ValidationTest extends TestCase
|
|||||||
field: String
|
field: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'The type of Query.field must be Output Type but got: [SomeInputObject].',
|
'message' => 'The type of Query.field must be Output Type but got: [SomeInputObject].',
|
||||||
@ -1133,7 +1142,7 @@ class ValidationTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
$expected = ['message' => 'Type BadObject must only implement Interface types, it cannot implement null.'];
|
$expected = ['message' => 'Type BadObject must only implement Interface types, it cannot implement null.'];
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[$expected]
|
[$expected]
|
||||||
);
|
);
|
||||||
@ -1157,7 +1166,7 @@ class ValidationTest extends TestCase
|
|||||||
field: String
|
field: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Type BadObject must only implement Interface types, it cannot implement SomeInputObject.',
|
'message' => 'Type BadObject must only implement Interface types, it cannot implement SomeInputObject.',
|
||||||
@ -1185,7 +1194,7 @@ class ValidationTest extends TestCase
|
|||||||
field: String
|
field: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Type AnotherObject can only implement AnotherInterface once.',
|
'message' => 'Type AnotherObject can only implement AnotherInterface once.',
|
||||||
@ -1217,7 +1226,7 @@ class ValidationTest extends TestCase
|
|||||||
|
|
||||||
extend type AnotherObject implements AnotherInterface
|
extend type AnotherObject implements AnotherInterface
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Type AnotherObject can only implement AnotherInterface once.',
|
'message' => 'Type AnotherObject can only implement AnotherInterface once.',
|
||||||
@ -1264,7 +1273,7 @@ class ValidationTest extends TestCase
|
|||||||
'f' => ['type' => $BadInterfaceType],
|
'f' => ['type' => $BadInterfaceType],
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
'types' => [ $BadImplementingType, $this->SomeObjectType ],
|
'types' => [ $BadImplementingType ],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1274,9 +1283,11 @@ class ValidationTest extends TestCase
|
|||||||
public function testRejectsAnEmptyInterfaceFieldType() : void
|
public function testRejectsAnEmptyInterfaceFieldType() : void
|
||||||
{
|
{
|
||||||
$schema = $this->schemaWithInterfaceFieldOfType(null);
|
$schema = $this->schemaWithInterfaceFieldOfType(null);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'The type of BadInterface.badField must be Output Type but got: null.'],
|
[
|
||||||
|
['message' => 'The type of BadInterface.badField must be Output Type but got: null.'],
|
||||||
|
['message' => 'The type of BadImplementing.badField must be Output Type but got: null.'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1289,16 +1300,32 @@ class ValidationTest extends TestCase
|
|||||||
foreach ($this->notOutputTypes as $type) {
|
foreach ($this->notOutputTypes as $type) {
|
||||||
$schema = $this->schemaWithInterfaceFieldOfType($type);
|
$schema = $this->schemaWithInterfaceFieldOfType($type);
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[
|
||||||
'message' => 'The type of BadInterface.badField must be Output Type but got: ' . Utils::printSafe($type) . '.',
|
['message' => 'The type of BadInterface.badField must be Output Type but got: ' . Utils::printSafe($type) . '.'],
|
||||||
],
|
['message' => 'The type of BadImplementing.badField must be Output Type but got: ' . Utils::printSafe($type) . '.'],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('rejects a non-type value as an Interface field type')
|
||||||
|
*/
|
||||||
|
public function testRejectsANonTypeValueAsAnInterfaceFieldType()
|
||||||
|
{
|
||||||
|
$schema = $this->schemaWithInterfaceFieldOfType('string');
|
||||||
|
$this->assertMatchesValidationMessage(
|
||||||
|
$schema->validate(),
|
||||||
|
[
|
||||||
|
['message' => 'The type of BadInterface.badField must be Output Type but got: string.'],
|
||||||
|
['message' => 'Expected GraphQL named type but got: string.'],
|
||||||
|
['message' => 'The type of BadImplementing.badField must be Output Type but got: string.'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// DESCRIBE: Type System: Input Object fields must have input types
|
// DESCRIBE: Type System: Input Object fields must have input types
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1318,13 +1345,22 @@ class ValidationTest extends TestCase
|
|||||||
input SomeInputObject {
|
input SomeInputObject {
|
||||||
foo: String
|
foo: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SomeObject implements SomeInterface {
|
||||||
|
field: SomeInputObject
|
||||||
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[
|
||||||
'message' => 'The type of SomeInterface.field must be Output Type but got: SomeInputObject.',
|
[
|
||||||
'locations' => [['line' => 7, 'column' => 16]],
|
'message' => 'The type of SomeInterface.field must be Output Type but got: SomeInputObject.',
|
||||||
],
|
'locations' => [['line' => 7, 'column' => 16]],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'message' => 'The type of SomeObject.field must be Output Type but got: SomeInputObject.',
|
||||||
|
'locations' => [[ 'line' => 15, 'column' => 16 ]],
|
||||||
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1343,7 +1379,7 @@ class ValidationTest extends TestCase
|
|||||||
foo: String
|
foo: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface SomeInterface must be implemented by at least one Object type.',
|
'message' => 'Interface SomeInterface must be implemented by at least one Object type.',
|
||||||
@ -1385,6 +1421,7 @@ class ValidationTest extends TestCase
|
|||||||
'f' => ['type' => $BadObjectType],
|
'f' => ['type' => $BadObjectType],
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
|
'types' => [$this->SomeObjectType],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1394,7 +1431,7 @@ class ValidationTest extends TestCase
|
|||||||
public function testRejectsAnEmptyFieldArgType() : void
|
public function testRejectsAnEmptyFieldArgType() : void
|
||||||
{
|
{
|
||||||
$schema = $this->schemaWithArgOfType(null);
|
$schema = $this->schemaWithArgOfType(null);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: null.'],
|
[['message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: null.'],
|
||||||
]
|
]
|
||||||
@ -1410,16 +1447,30 @@ class ValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
foreach ($this->notInputTypes as $type) {
|
foreach ($this->notInputTypes as $type) {
|
||||||
$schema = $this->schemaWithArgOfType($type);
|
$schema = $this->schemaWithArgOfType($type);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[
|
||||||
'message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: ' . Utils::printSafe($type) . '.',
|
['message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: ' . Utils::printSafe($type) . '.'],
|
||||||
],
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('rejects a non-type value as a field arg type')
|
||||||
|
*/
|
||||||
|
public function testRejectsANonTypeValueAsAFieldArgType()
|
||||||
|
{
|
||||||
|
$schema = $this->schemaWithArgOfType('string');
|
||||||
|
$this->assertMatchesValidationMessage(
|
||||||
|
$schema->validate(),
|
||||||
|
[
|
||||||
|
['message' => 'The type of BadObject.badField(badArg:) must be Input Type but got: string.'],
|
||||||
|
['message' => 'Expected GraphQL named type but got: string.'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('rejects a non-input type as a field arg with locations')
|
* @see it('rejects a non-input type as a field arg with locations')
|
||||||
*/
|
*/
|
||||||
@ -1434,7 +1485,7 @@ class ValidationTest extends TestCase
|
|||||||
foo: String
|
foo: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'The type of Query.test(arg:) must be Input Type but got: SomeObject.',
|
'message' => 'The type of Query.test(arg:) must be Input Type but got: SomeObject.',
|
||||||
@ -1476,6 +1527,7 @@ class ValidationTest extends TestCase
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
]),
|
]),
|
||||||
|
'types' => [ $this->SomeObjectType ],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1485,7 +1537,7 @@ class ValidationTest extends TestCase
|
|||||||
public function testRejectsAnEmptyInputFieldType() : void
|
public function testRejectsAnEmptyInputFieldType() : void
|
||||||
{
|
{
|
||||||
$schema = $this->schemaWithInputFieldOfType(null);
|
$schema = $this->schemaWithInputFieldOfType(null);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[['message' => 'The type of BadInputObject.badField must be Input Type but got: null.'],
|
[['message' => 'The type of BadInputObject.badField must be Input Type but got: null.'],
|
||||||
]
|
]
|
||||||
@ -1499,7 +1551,7 @@ class ValidationTest extends TestCase
|
|||||||
{
|
{
|
||||||
foreach ($this->notInputTypes as $type) {
|
foreach ($this->notInputTypes as $type) {
|
||||||
$schema = $this->schemaWithInputFieldOfType($type);
|
$schema = $this->schemaWithInputFieldOfType($type);
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'The type of BadInputObject.badField must be Input Type but got: ' . Utils::printSafe($type) . '.',
|
'message' => 'The type of BadInputObject.badField must be Input Type but got: ' . Utils::printSafe($type) . '.',
|
||||||
@ -1509,6 +1561,21 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see it('rejects a non-type value as an input field type')
|
||||||
|
*/
|
||||||
|
public function testRejectsAAonTypeValueAsAnInputFieldType()
|
||||||
|
{
|
||||||
|
$schema = $this->schemaWithInputFieldOfType('string');
|
||||||
|
$this->assertMatchesValidationMessage(
|
||||||
|
$schema->validate(),
|
||||||
|
[
|
||||||
|
['message' => 'The type of BadInputObject.badField must be Input Type but got: string.'],
|
||||||
|
['message' => 'Expected GraphQL named type but got: string.'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see it('rejects a non-input type as an input object field with locations')
|
* @see it('rejects a non-input type as an input object field with locations')
|
||||||
*/
|
*/
|
||||||
@ -1527,7 +1594,7 @@ class ValidationTest extends TestCase
|
|||||||
bar: String
|
bar: String
|
||||||
}
|
}
|
||||||
');
|
');
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'The type of SomeInputObject.foo must be Input Type but got: SomeObject.',
|
'message' => 'The type of SomeInputObject.foo must be Input Type but got: SomeObject.',
|
||||||
@ -1632,7 +1699,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expected but ' .
|
'message' => 'Interface field AnotherInterface.field expected but ' .
|
||||||
@ -1662,7 +1729,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expects type String but ' .
|
'message' => 'Interface field AnotherInterface.field expects type String but ' .
|
||||||
@ -1695,7 +1762,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expects type A but ' .
|
'message' => 'Interface field AnotherInterface.field expects type A but ' .
|
||||||
@ -1775,7 +1842,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field argument AnotherInterface.field(input:) expected ' .
|
'message' => 'Interface field argument AnotherInterface.field(input:) expected ' .
|
||||||
@ -1805,7 +1872,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field argument AnotherInterface.field(input:) expects ' .
|
'message' => 'Interface field argument AnotherInterface.field(input:) expects ' .
|
||||||
@ -1835,7 +1902,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@ -1871,7 +1938,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Object field argument AnotherObject.field(anotherInput:) is of ' .
|
'message' => 'Object field argument AnotherObject.field(anotherInput:) is of ' .
|
||||||
@ -1924,7 +1991,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expects type [String] ' .
|
'message' => 'Interface field AnotherInterface.field expects type [String] ' .
|
||||||
@ -1954,7 +2021,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expects type String but ' .
|
'message' => 'Interface field AnotherInterface.field expects type String but ' .
|
||||||
@ -2006,7 +2073,7 @@ class ValidationTest extends TestCase
|
|||||||
}
|
}
|
||||||
');
|
');
|
||||||
|
|
||||||
$this->assertContainsValidationMessage(
|
$this->assertMatchesValidationMessage(
|
||||||
$schema->validate(),
|
$schema->validate(),
|
||||||
[[
|
[[
|
||||||
'message' => 'Interface field AnotherInterface.field expects type String! ' .
|
'message' => 'Interface field AnotherInterface.field expects type String! ' .
|
||||||
|
Loading…
Reference in New Issue
Block a user