mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
58e0c7a178
This generalizes the "arguments of correct type" and "default values of correct type" to a single rule "values of correct type" which has been re-written to rely on a traversal rather than the utility function `isValidLiteralValue`. To reduce breaking scope, this does not remove that utility even though it's no longer used directly within the library. Since the default values rule included another validation rule that rule was renamed to a more apt "variable default value allowed". This also includes the original errors from custom scalars in the validation error output, solving the remainder of graphql/graphql-js#821. ref: graphql/graphql-js#1144
993 lines
33 KiB
PHP
993 lines
33 KiB
PHP
<?php
|
|
namespace GraphQL\Tests\Executor;
|
|
|
|
require_once __DIR__ . '/TestClasses.php';
|
|
|
|
use GraphQL\Error\Error;
|
|
use GraphQL\Executor\Executor;
|
|
use GraphQL\Language\Parser;
|
|
use GraphQL\Type\Schema;
|
|
use GraphQL\Type\Definition\InputObjectType;
|
|
use GraphQL\Type\Definition\ObjectType;
|
|
use GraphQL\Type\Definition\Type;
|
|
|
|
class VariablesTest extends \PHPUnit_Framework_TestCase
|
|
{
|
|
// Execute: Handles inputs
|
|
// Handles objects and nullability
|
|
|
|
/**
|
|
* @describe using inline structs
|
|
*/
|
|
public function testUsingInlineStructs()
|
|
{
|
|
// executes with complex input:
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"})
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
|
|
$expected = [
|
|
'data' => [
|
|
'fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}'
|
|
]
|
|
];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
|
|
// properly parses single value to list:
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
|
|
// properly parses null value to null
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: {a: null, b: null, c: "C", d: null})
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithObjectInput' => '{"a":null,"b":null,"c":"C","d":null}']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
|
|
// properly parses null value in list
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithObjectInput' => '{"b":["A",null,"C"],"c":"C"}']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
|
|
// does not use incorrect value
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: ["foo", "bar", "baz"])
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast)->toArray();
|
|
|
|
$expected = [
|
|
'data' => ['fieldWithObjectInput' => null],
|
|
'errors' => [[
|
|
'message' => 'Argument "input" has invalid value ["foo", "bar", "baz"].',
|
|
'path' => ['fieldWithObjectInput'],
|
|
'locations' => [['line' => 3, 'column' => 39]]
|
|
]]
|
|
];
|
|
$this->assertArraySubset($expected, $result);
|
|
|
|
// properly runs parseLiteral on complex scalar types
|
|
$doc = '
|
|
{
|
|
fieldWithObjectInput(input: {c: "foo", d: "SerializedValue"})
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$this->assertEquals(
|
|
['data' => ['fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}']],
|
|
Executor::execute($this->schema(), $ast)->toArray()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @describe using variables
|
|
*/
|
|
public function testUsingVariables()
|
|
{
|
|
// executes with complex input:
|
|
$doc = '
|
|
query q($input:TestInputObject) {
|
|
fieldWithObjectInput(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$params = ['input' => ['a' => 'foo', 'b' => ['bar'], 'c' => 'baz']];
|
|
$schema = $this->schema();
|
|
|
|
$this->assertEquals(
|
|
['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']],
|
|
Executor::execute($schema, $ast, null, null, $params)->toArray()
|
|
);
|
|
|
|
// uses default value when not provided:
|
|
$withDefaultsNode = Parser::parse('
|
|
query q($input: TestInputObject = {a: "foo", b: ["bar"], c: "baz"}) {
|
|
fieldWithObjectInput(input: $input)
|
|
}
|
|
');
|
|
|
|
$result = Executor::execute($this->schema(), $withDefaultsNode)->toArray();
|
|
$expected = [
|
|
'data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']
|
|
];
|
|
$this->assertEquals($expected, $result);
|
|
|
|
// properly parses single value to list:
|
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => 'baz']];
|
|
$this->assertEquals(
|
|
['data' => ['fieldWithObjectInput' => '{"a":"foo","b":["bar"],"c":"baz"}']],
|
|
Executor::execute($schema, $ast, null, null, $params)->toArray()
|
|
);
|
|
|
|
// executes with complex scalar input:
|
|
$params = [ 'input' => [ 'c' => 'foo', 'd' => 'SerializedValue' ] ];
|
|
$result = Executor::execute($schema, $ast, null, null, $params)->toArray();
|
|
$expected = [
|
|
'data' => [
|
|
'fieldWithObjectInput' => '{"c":"foo","d":"DeserializedValue"}'
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result);
|
|
|
|
// errors on null for nested non-null:
|
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar', 'c' => null]];
|
|
$result = Executor::execute($schema, $ast, null, null, $params);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value ' .
|
|
'{"a":"foo","b":"bar","c":null}; ' .
|
|
'Expected non-nullable type String! at value.c.',
|
|
'locations' => [['line' => 2, 'column' => 17]],
|
|
'category' => 'graphql'
|
|
]
|
|
]
|
|
];
|
|
|
|
$this->assertEquals($expected, $result->toArray());
|
|
|
|
// errors on incorrect type:
|
|
$params = [ 'input' => 'foo bar' ];
|
|
$result = Executor::execute($schema, $ast, null, null, $params);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value "foo bar"; ' .
|
|
'Expected object type TestInputObject.',
|
|
'locations' => [ [ 'line' => 2, 'column' => 17 ] ],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
|
|
// errors on omission of nested non-null:
|
|
$params = ['input' => ['a' => 'foo', 'b' => 'bar']];
|
|
|
|
$result = Executor::execute($schema, $ast, null, null, $params);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; '.
|
|
'Field value.c of required type String! was not provided.',
|
|
'locations' => [['line' => 2, 'column' => 17]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
|
|
// errors on deep nested errors and with many errors
|
|
$nestedDoc = '
|
|
query q($input: TestNestedInputObject) {
|
|
fieldWithNestedObjectInput(input: $input)
|
|
}
|
|
';
|
|
$nestedAst = Parser::parse($nestedDoc);
|
|
$params = [ 'input' => [ 'na' => [ 'a' => 'foo' ] ] ];
|
|
|
|
$result = Executor::execute($schema, $nestedAst, null, null, $params);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
|
'Field value.na.c of required type String! was not provided.',
|
|
'locations' => [['line' => 2, 'column' => 19]],
|
|
'category' => 'graphql',
|
|
],
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
|
|
'Field value.nb of required type String! was not provided.',
|
|
'locations' => [['line' => 2, 'column' => 19]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
|
|
|
|
// errors on addition of unknown input field
|
|
$params = ['input' => [ 'a' => 'foo', 'b' => 'bar', 'c' => 'baz', 'extra' => 'dog' ]];
|
|
$result = Executor::execute($schema, $ast, null, null, $params);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value ' .
|
|
'{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
|
|
'Field "extra" is not defined by type TestInputObject.',
|
|
'locations' => [['line' => 2, 'column' => 17]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
// Describe: Handles nullable scalars
|
|
|
|
/**
|
|
* @it allows nullable inputs to be omitted
|
|
*/
|
|
public function testAllowsNullableInputsToBeOmitted()
|
|
{
|
|
$doc = '
|
|
{
|
|
fieldWithNullableStringInput
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = [
|
|
'data' => ['fieldWithNullableStringInput' => null]
|
|
];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows nullable inputs to be omitted in a variable
|
|
*/
|
|
public function testAllowsNullableInputsToBeOmittedInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNullable($value: String) {
|
|
fieldWithNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNullableStringInput' => null]];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows nullable inputs to be omitted in an unlisted variable
|
|
*/
|
|
public function testAllowsNullableInputsToBeOmittedInAnUnlistedVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNullable {
|
|
fieldWithNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNullableStringInput' => null]];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows nullable inputs to be set to null in a variable
|
|
*/
|
|
public function testAllowsNullableInputsToBeSetToNullInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNullable($value: String) {
|
|
fieldWithNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNullableStringInput' => null]];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, ['value' => null])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows nullable inputs to be set to a value in a variable
|
|
*/
|
|
public function testAllowsNullableInputsToBeSetToAValueInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNullable($value: String) {
|
|
fieldWithNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['value' => 'a'])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows nullable inputs to be set to a value directly
|
|
*/
|
|
public function testAllowsNullableInputsToBeSetToAValueDirectly()
|
|
{
|
|
$doc = '
|
|
{
|
|
fieldWithNullableStringInput(input: "a")
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNullableStringInput' => '"a"']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
|
|
// Describe: Handles non-nullable scalars
|
|
|
|
/**
|
|
* @it allows non-nullable inputs to be omitted given a default
|
|
*/
|
|
public function testAllowsNonNullableInputsToBeOmittedGivenADefault()
|
|
{
|
|
$doc = '
|
|
query SetsNonNullable($value: String = "default") {
|
|
fieldWithNonNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = [
|
|
'data' => ['fieldWithNonNullableStringInput' => '"default"']
|
|
];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
|
|
}
|
|
|
|
/**
|
|
* @it does not allow non-nullable inputs to be omitted in a variable
|
|
*/
|
|
public function testDoesntAllowNonNullableInputsToBeOmittedInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNonNullable($value: String!) {
|
|
fieldWithNonNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast);
|
|
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' => 'Variable "$value" of required type "String!" was not provided.',
|
|
'locations' => [['line' => 2, 'column' => 31]],
|
|
'category' => 'graphql'
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow non-nullable inputs to be set to null in a variable
|
|
*/
|
|
public function testDoesNotAllowNonNullableInputsToBeSetToNullInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNonNullable($value: String!) {
|
|
fieldWithNonNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast, null, null, ['value' => null]);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$value" got invalid value null; ' .
|
|
'Expected non-nullable type String!.',
|
|
'locations' => [['line' => 2, 'column' => 31]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows non-nullable inputs to be set to a value in a variable
|
|
*/
|
|
public function testAllowsNonNullableInputsToBeSetToAValueInAVariable()
|
|
{
|
|
$doc = '
|
|
query SetsNonNullable($value: String!) {
|
|
fieldWithNonNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['value' => 'a'])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows non-nullable inputs to be set to a value directly
|
|
*/
|
|
public function testAllowsNonNullableInputsToBeSetToAValueDirectly()
|
|
{
|
|
$doc = '
|
|
{
|
|
fieldWithNonNullableStringInput(input: "a")
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['fieldWithNonNullableStringInput' => '"a"']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it reports error for missing non-nullable inputs
|
|
*/
|
|
public function testReportsErrorForMissingNonNullableInputs()
|
|
{
|
|
$doc = '
|
|
{
|
|
fieldWithNonNullableStringInput
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = [
|
|
'data' => ['fieldWithNonNullableStringInput' => null],
|
|
'errors' => [[
|
|
'message' => 'Argument "input" of required type "String!" was not provided.',
|
|
'locations' => [ [ 'line' => 3, 'column' => 9 ] ],
|
|
'path' => [ 'fieldWithNonNullableStringInput' ],
|
|
'category' => 'graphql',
|
|
]]
|
|
];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it reports error for array passed into string input
|
|
*/
|
|
public function testReportsErrorForArrayPassedIntoStringInput()
|
|
{
|
|
|
|
$doc = '
|
|
query SetsNonNullable($value: String!) {
|
|
fieldWithNonNullableStringInput(input: $value)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$variables = ['value' => [1, 2, 3]];
|
|
|
|
$expected = [
|
|
'errors' => [[
|
|
'message' =>
|
|
'Variable "$value" got invalid value [1,2,3]; Expected type ' .
|
|
'String; String cannot represent an array value: [1,2,3]',
|
|
'category' => 'graphql',
|
|
'locations' => [
|
|
['line' => 2, 'column' => 31]
|
|
]
|
|
]]
|
|
];
|
|
|
|
$result = Executor::execute($this->schema(), $ast, null, null, $variables)->toArray(true);
|
|
|
|
$this->assertEquals($expected, $result);
|
|
}
|
|
|
|
/**
|
|
* @it serializing an array via GraphQLString throws TypeError
|
|
*/
|
|
public function testSerializingAnArrayViaGraphQLStringThrowsTypeError()
|
|
{
|
|
$this->setExpectedException(
|
|
Error::class,
|
|
'String cannot represent non scalar value: [1,2,3]'
|
|
);
|
|
Type::string()->serialize([1, 2, 3]);
|
|
}
|
|
|
|
/**
|
|
* @it reports error for non-provided variables for non-nullable inputs
|
|
*/
|
|
public function testReportsErrorForNonProvidedVariablesForNonNullableInputs()
|
|
{
|
|
// Note: this test would typically fail validation before encountering
|
|
// this execution error, however for queries which previously validated
|
|
// and are being run against a new schema which have introduced a breaking
|
|
// change to make a formerly non-required argument required, this asserts
|
|
// failure before allowing the underlying code to receive a non-null value.
|
|
$doc = '
|
|
{
|
|
fieldWithNonNullableStringInput(input: $foo)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
|
|
$expected = [
|
|
'data' => ['fieldWithNonNullableStringInput' => null],
|
|
'errors' => [[
|
|
'message' =>
|
|
'Argument "input" of required type "String!" was provided the ' .
|
|
'variable "$foo" which was not provided a runtime value.',
|
|
'locations' => [['line' => 3, 'column' => 48]],
|
|
'path' => ['fieldWithNonNullableStringInput'],
|
|
'category' => 'graphql',
|
|
]]
|
|
];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast)->toArray());
|
|
}
|
|
|
|
// Describe: Handles lists and nullability
|
|
|
|
/**
|
|
* @it allows lists to be null
|
|
*/
|
|
public function testAllowsListsToBeNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]) {
|
|
list(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['list' => null]];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, ['input' => null])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows lists to contain values
|
|
*/
|
|
public function testAllowsListsToContainValues()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]) {
|
|
list(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['list' => '["A"]']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows lists to contain null
|
|
*/
|
|
public function testAllowsListsToContainNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]) {
|
|
list(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['list' => '["A",null,"B"]']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A',null,'B']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow non-null lists to be null
|
|
*/
|
|
public function testDoesNotAllowNonNullListsToBeNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]!) {
|
|
nnList(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast, null, null, ['input' => null]);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value null; ' .
|
|
'Expected non-nullable type [String]!.',
|
|
'locations' => [['line' => 2, 'column' => 17]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows non-null lists to contain values
|
|
*/
|
|
public function testAllowsNonNullListsToContainValues()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]!) {
|
|
nnList(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['nnList' => '["A"]']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows non-null lists to contain null
|
|
*/
|
|
public function testAllowsNonNullListsToContainNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String]!) {
|
|
nnList(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['nnList' => '["A",null,"B"]']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A',null,'B']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows lists of non-nulls to be null
|
|
*/
|
|
public function testAllowsListsOfNonNullsToBeNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]) {
|
|
listNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['listNN' => null]];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, ['input' => null])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows lists of non-nulls to contain values
|
|
*/
|
|
public function testAllowsListsOfNonNullsToContainValues()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]) {
|
|
listNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['listNN' => '["A"]']];
|
|
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow lists of non-nulls to contain null
|
|
*/
|
|
public function testDoesNotAllowListsOfNonNullsToContainNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]) {
|
|
listNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast, null, null, ['input' => ['A', null, 'B']]);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
|
'Expected non-nullable type String! at value[1].',
|
|
'locations' => [ ['line' => 2, 'column' => 17] ],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow non-null lists of non-nulls to be null
|
|
*/
|
|
public function testDoesNotAllowNonNullListsOfNonNullsToBeNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]!) {
|
|
nnListNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast, null, null, ['input' => null]);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value null; ' .
|
|
'Expected non-nullable type [String!]!.',
|
|
'locations' => [ ['line' => 2, 'column' => 17] ],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it allows non-null lists of non-nulls to contain values
|
|
*/
|
|
public function testAllowsNonNullListsOfNonNullsToContainValues()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]!) {
|
|
nnListNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$expected = ['data' => ['nnListNN' => '["A"]']];
|
|
$this->assertEquals($expected, Executor::execute($this->schema(), $ast, null, null, ['input' => ['A']])->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow non-null lists of non-nulls to contain null
|
|
*/
|
|
public function testDoesNotAllowNonNullListsOfNonNullsToContainNull()
|
|
{
|
|
$doc = '
|
|
query q($input:[String!]!) {
|
|
nnListNN(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$result = Executor::execute($this->schema(), $ast, null, null, ['input' => ['A', null, 'B']]);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" got invalid value ["A",null,"B"]; ' .
|
|
'Expected non-nullable type String! at value[1].',
|
|
'locations' => [ ['line' => 2, 'column' => 17] ],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow invalid types to be used as values
|
|
*/
|
|
public function testDoesNotAllowInvalidTypesToBeUsedAsValues()
|
|
{
|
|
$doc = '
|
|
query q($input: TestType!) {
|
|
fieldWithObjectInput(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$vars = [ 'input' => [ 'list' => [ 'A', 'B' ] ] ];
|
|
$result = Executor::execute($this->schema(), $ast, null, null, $vars);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" expected value of type "TestType!" which cannot ' .
|
|
'be used as an input type.',
|
|
'locations' => [['line' => 2, 'column' => 25]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
/**
|
|
* @it does not allow unknown types to be used as values
|
|
*/
|
|
public function testDoesNotAllowUnknownTypesToBeUsedAsValues()
|
|
{
|
|
$doc = '
|
|
query q($input: UnknownType!) {
|
|
fieldWithObjectInput(input: $input)
|
|
}
|
|
';
|
|
$ast = Parser::parse($doc);
|
|
$vars = ['input' => 'whoknows'];
|
|
|
|
$result = Executor::execute($this->schema(), $ast, null, null, $vars);
|
|
$expected = [
|
|
'errors' => [
|
|
[
|
|
'message' =>
|
|
'Variable "$input" expected value of type "UnknownType!" which ' .
|
|
'cannot be used as an input type.',
|
|
'locations' => [['line' => 2, 'column' => 25]],
|
|
'category' => 'graphql',
|
|
]
|
|
]
|
|
];
|
|
$this->assertEquals($expected, $result->toArray());
|
|
}
|
|
|
|
// Describe: Execute: Uses argument default values
|
|
/**
|
|
* @it when no argument provided
|
|
*/
|
|
public function testWhenNoArgumentProvided()
|
|
{
|
|
$ast = Parser::parse('{
|
|
fieldWithDefaultArgumentValue
|
|
}');
|
|
|
|
$this->assertEquals(
|
|
['data' => ['fieldWithDefaultArgumentValue' => '"Hello World"']],
|
|
Executor::execute($this->schema(), $ast)->toArray()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @it when omitted variable provided
|
|
*/
|
|
public function testWhenOmittedVariableProvided()
|
|
{
|
|
$ast = Parser::parse('query optionalVariable($optional: String) {
|
|
fieldWithDefaultArgumentValue(input: $optional)
|
|
}');
|
|
|
|
$this->assertEquals(
|
|
['data' => ['fieldWithDefaultArgumentValue' => '"Hello World"']],
|
|
Executor::execute($this->schema(), $ast)->toArray()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @it not when argument cannot be coerced
|
|
*/
|
|
public function testNotWhenArgumentCannotBeCoerced()
|
|
{
|
|
$ast = Parser::parse('{
|
|
fieldWithDefaultArgumentValue(input: WRONG_TYPE)
|
|
}');
|
|
|
|
$expected = [
|
|
'data' => ['fieldWithDefaultArgumentValue' => null],
|
|
'errors' => [[
|
|
'message' =>
|
|
'Argument "input" has invalid value WRONG_TYPE.',
|
|
'locations' => [ [ 'line' => 2, 'column' => 50 ] ],
|
|
'path' => [ 'fieldWithDefaultArgumentValue' ],
|
|
'category' => 'graphql',
|
|
]]
|
|
];
|
|
|
|
$this->assertEquals(
|
|
$expected,
|
|
Executor::execute($this->schema(), $ast)->toArray()
|
|
);
|
|
}
|
|
|
|
|
|
public function schema()
|
|
{
|
|
$ComplexScalarType = ComplexScalar::create();
|
|
|
|
$TestInputObject = new InputObjectType([
|
|
'name' => 'TestInputObject',
|
|
'fields' => [
|
|
'a' => ['type' => Type::string()],
|
|
'b' => ['type' => Type::listOf(Type::string())],
|
|
'c' => ['type' => Type::nonNull(Type::string())],
|
|
'd' => ['type' => $ComplexScalarType],
|
|
]
|
|
]);
|
|
|
|
$TestNestedInputObject = new InputObjectType([
|
|
'name' => 'TestNestedInputObject',
|
|
'fields' => [
|
|
'na' => [ 'type' => Type::nonNull($TestInputObject) ],
|
|
'nb' => [ 'type' => Type::nonNull(Type::string()) ],
|
|
],
|
|
]);
|
|
|
|
$TestType = new ObjectType([
|
|
'name' => 'TestType',
|
|
'fields' => [
|
|
'fieldWithObjectInput' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => $TestInputObject]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'fieldWithNullableStringInput' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::string()]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'fieldWithNonNullableStringInput' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::nonNull(Type::string())]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'fieldWithDefaultArgumentValue' => [
|
|
'type' => Type::string(),
|
|
'args' => [ 'input' => [ 'type' => Type::string(), 'defaultValue' => 'Hello World' ]],
|
|
'resolve' => function($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'fieldWithNestedInputObject' => [
|
|
'type' => Type::string(),
|
|
'args' => [
|
|
'input' => [
|
|
'type' => $TestNestedInputObject,
|
|
'defaultValue' => 'Hello World'
|
|
]
|
|
],
|
|
'resolve' => function($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'list' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::listOf(Type::string())]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'nnList' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::nonNull(Type::listOf(Type::string()))]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'listNN' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::listOf(Type::nonNull(Type::string()))]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
'nnListNN' => [
|
|
'type' => Type::string(),
|
|
'args' => ['input' => ['type' => Type::nonNull(Type::listOf(Type::nonNull(Type::string())))]],
|
|
'resolve' => function ($_, $args) {
|
|
return isset($args['input']) ? json_encode($args['input']) : null;
|
|
}
|
|
],
|
|
]
|
|
]);
|
|
|
|
$schema = new Schema(['query' => $TestType]);
|
|
return $schema;
|
|
}
|
|
}
|