2018-02-13 18:51:44 +03:00
|
|
|
<?php
|
|
|
|
namespace GraphQL\Tests\Utils;
|
|
|
|
|
2018-02-16 18:19:25 +03:00
|
|
|
use GraphQL\Error\Error;
|
2018-02-13 18:51:44 +03:00
|
|
|
use GraphQL\Executor\Values;
|
2018-02-16 18:19:25 +03:00
|
|
|
use GraphQL\Type\Definition\EnumType;
|
|
|
|
use GraphQL\Type\Definition\InputObjectType;
|
2018-02-13 18:51:44 +03:00
|
|
|
use GraphQL\Type\Definition\Type;
|
2018-02-16 18:19:25 +03:00
|
|
|
use GraphQL\Utils\Utils;
|
2018-02-13 18:51:44 +03:00
|
|
|
use GraphQL\Utils\Value;
|
2018-07-29 18:43:10 +03:00
|
|
|
use PHPUnit\Framework\TestCase;
|
2018-02-13 18:51:44 +03:00
|
|
|
|
2018-07-29 18:43:10 +03:00
|
|
|
class CoerceValueTest extends TestCase
|
2018-02-13 18:51:44 +03:00
|
|
|
{
|
2018-02-16 18:19:25 +03:00
|
|
|
private $testEnum;
|
|
|
|
private $testInputObject;
|
|
|
|
|
|
|
|
public function setUp()
|
|
|
|
{
|
|
|
|
$this->testEnum = new EnumType([
|
|
|
|
'name' => 'TestEnum',
|
|
|
|
'values' => [
|
|
|
|
'FOO' => 'InternalFoo',
|
|
|
|
'BAR' => 123456789,
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
|
|
|
$this->testInputObject = new InputObjectType([
|
|
|
|
'name' => 'TestInputObject',
|
|
|
|
'fields' => [
|
|
|
|
'foo' => Type::nonNull(Type::int()),
|
|
|
|
'bar' => Type::int(),
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2018-02-13 18:51:44 +03:00
|
|
|
// Describe: coerceValue
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('coercing an array to GraphQLString produces an error')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testCoercingAnArrayToGraphQLStringProducesAnError()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue([1, 2, 3], Type::string());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type String; String cannot represent an array value: [1,2,3]'
|
|
|
|
);
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
'String cannot represent an array value: [1,2,3]',
|
|
|
|
$result['errors'][0]->getPrevious()->getMessage()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Describe: for GraphQLInt
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for int input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsNoErrorForIntInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1', Type::int());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for negative int input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsNoErrorForNegativeIntInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('-1', Type::int());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for exponent input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsNoErrorForExponentInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1e3', Type::int());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for null')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsASingleErrorNull()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(null, Type::int());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for empty value')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsASingleErrorForEmptyValue()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('', Type::int());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Int; Int cannot represent non 32-bit signed integer value: (empty string)'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns error for float input as int')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsErrorForFloatInputAsInt()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1.5', Type::int());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Int; Int cannot represent non-integer value: 1.5'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for char input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsASingleErrorForCharInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('a', Type::int());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Int; Int cannot represent non 32-bit signed integer value: a'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for multi char input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testIntReturnsASingleErrorForMultiCharInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('meow', Type::int());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Int; Int cannot represent non 32-bit signed integer value: meow'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Describe: for GraphQLFloat
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for int input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsNoErrorForIntInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1', Type::float());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for exponent input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsNoErrorForExponentInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1e3', Type::float());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for float input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsNoErrorForFloatInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('1.5', Type::float());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for null')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsASingleErrorNull()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(null, Type::float());
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for empty value')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsASingleErrorForEmptyValue()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('', Type::float());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Float; Float cannot represent non numeric value: (empty string)'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for char input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsASingleErrorForCharInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('a', Type::float());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Float; Float cannot represent non numeric value: a'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns a single error for multi char input')
|
2018-02-13 18:51:44 +03:00
|
|
|
*/
|
|
|
|
public function testFloatReturnsASingleErrorForMultiCharInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('meow', Type::float());
|
|
|
|
$this->expectError(
|
|
|
|
$result,
|
|
|
|
'Expected type Float; Float cannot represent non numeric value: meow'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-02-16 18:19:25 +03:00
|
|
|
// DESCRIBE: for GraphQLEnum
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for a known enum name')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsNoErrorForAKnownEnumName()
|
|
|
|
{
|
|
|
|
$fooResult = Value::coerceValue('FOO', $this->testEnum);
|
|
|
|
$this->expectNoErrors($fooResult);
|
|
|
|
$this->assertEquals('InternalFoo', $fooResult['value']);
|
|
|
|
|
|
|
|
$barResult = Value::coerceValue('BAR', $this->testEnum);
|
|
|
|
$this->expectNoErrors($barResult);
|
|
|
|
$this->assertEquals(123456789, $barResult['value']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('results error for misspelled enum value')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForMisspelledEnumValue()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue('foo', $this->testEnum);
|
|
|
|
$this->expectError($result, 'Expected type TestEnum; did you mean FOO?');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('results error for incorrect value type')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForIncorrectValueType()
|
|
|
|
{
|
|
|
|
$result1 = Value::coerceValue(123, $this->testEnum);
|
|
|
|
$this->expectError($result1, 'Expected type TestEnum.');
|
|
|
|
|
|
|
|
$result2 = Value::coerceValue(['field' => 'value'], $this->testEnum);
|
|
|
|
$this->expectError($result2, 'Expected type TestEnum.');
|
|
|
|
}
|
|
|
|
|
|
|
|
// DESCRIBE: for GraphQLInputObject
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for a valid input')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsNoErrorForValidInput()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['foo' => 123], $this->testInputObject);
|
|
|
|
$this->expectNoErrors($result);
|
|
|
|
$this->assertEquals(['foo' => 123], $result['value']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for a non-object type')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForNonObjectType()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(123, $this->testInputObject);
|
|
|
|
$this->expectError($result, 'Expected type TestInputObject to be an object.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns no error for an invalid field')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnErrorForAnInvalidField()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['foo' => 'abc'], $this->testInputObject);
|
|
|
|
$this->expectError($result, 'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns multiple errors for multiple invalid fields')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsMultipleErrorsForMultipleInvalidFields()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['foo' => 'abc', 'bar' => 'def'], $this->testInputObject);
|
|
|
|
$this->assertEquals([
|
|
|
|
'Expected type Int at value.foo; Int cannot represent non 32-bit signed integer value: abc',
|
|
|
|
'Expected type Int at value.bar; Int cannot represent non 32-bit signed integer value: def',
|
|
|
|
], $result['errors']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns error for a missing required field')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForAMissingRequiredField()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['bar' => 123], $this->testInputObject);
|
|
|
|
$this->expectError($result, 'Field value.foo of required type Int! was not provided.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns error for an unknown field')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForAnUnknownField()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['foo' => 123, 'unknownField' => 123], $this->testInputObject);
|
|
|
|
$this->expectError($result, 'Field "unknownField" is not defined by type TestInputObject.');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('returns error for a misspelled field')
|
2018-02-16 18:19:25 +03:00
|
|
|
*/
|
|
|
|
public function testReturnsErrorForAMisspelledField()
|
|
|
|
{
|
|
|
|
$result = Value::coerceValue(['foo' => 123, 'bart' => 123], $this->testInputObject);
|
|
|
|
$this->expectError($result, 'Field "bart" is not defined by type TestInputObject; did you mean bar?');
|
|
|
|
}
|
|
|
|
|
2018-02-13 18:51:44 +03:00
|
|
|
private function expectNoErrors($result)
|
|
|
|
{
|
|
|
|
$this->assertInternalType('array', $result);
|
|
|
|
$this->assertNull($result['errors']);
|
2018-02-16 18:19:25 +03:00
|
|
|
$this->assertNotEquals(Utils::undefined(), $result['value']);
|
2018-02-13 18:51:44 +03:00
|
|
|
}
|
|
|
|
|
2018-02-16 18:19:25 +03:00
|
|
|
|
2018-02-13 18:51:44 +03:00
|
|
|
private function expectError($result, $expected) {
|
|
|
|
$this->assertInternalType('array', $result);
|
|
|
|
$this->assertInternalType('array', $result['errors']);
|
|
|
|
$this->assertCount(1, $result['errors']);
|
|
|
|
$this->assertEquals($expected, $result['errors'][0]->getMessage());
|
2018-02-16 18:19:25 +03:00
|
|
|
$this->assertEquals(Utils::undefined(), $result['value']);
|
2018-02-13 18:51:44 +03:00
|
|
|
}
|
|
|
|
}
|