2015-07-15 20:05:46 +03:00
|
|
|
<?php
|
2016-04-09 10:36:53 +03:00
|
|
|
namespace GraphQL\Tests\Validator;
|
2015-07-15 20:05:46 +03:00
|
|
|
|
2016-10-21 12:39:57 +03:00
|
|
|
use GraphQL\Error\FormattedError;
|
2015-07-15 20:05:46 +03:00
|
|
|
use GraphQL\Language\SourceLocation;
|
|
|
|
use GraphQL\Validator\Rules\PossibleFragmentSpreads;
|
|
|
|
|
2018-07-29 18:43:10 +03:00
|
|
|
class PossibleFragmentSpreadsTest extends ValidatorTestCase
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
|
|
|
// Validate: Possible fragment spreads
|
2016-04-25 16:29:17 +03:00
|
|
|
|
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('of the same object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testOfTheSameObject()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads(), '
|
|
|
|
fragment objectWithinObject on Dog { ...dogFragment }
|
|
|
|
fragment dogFragment on Dog { barkVolume }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('of the same object with inline fragment')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testOfTheSameObjectWithInlineFragment()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('object into an implemented interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testObjectIntoAnImplementedInterface()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment objectWithinInterface on Pet { ...dogFragment }
|
|
|
|
fragment dogFragment on Dog { barkVolume }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('object into containing union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testObjectIntoContainingUnion()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment objectWithinUnion on CatOrDog { ...dogFragment }
|
|
|
|
fragment dogFragment on Dog { barkVolume }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into contained object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoContainedObject()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment unionWithinObject on Dog { ...catOrDogFragment }
|
|
|
|
fragment catOrDogFragment on CatOrDog { __typename }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into overlapping interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoOverlappingInterface()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment unionWithinInterface on Pet { ...catOrDogFragment }
|
|
|
|
fragment catOrDogFragment on CatOrDog { __typename }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into overlapping union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoOverlappingUnion()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
|
|
|
|
fragment catOrDogFragment on CatOrDog { __typename }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into implemented object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoImplementedObject()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment interfaceWithinObject on Dog { ...petFragment }
|
|
|
|
fragment petFragment on Pet { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into overlapping interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoOverlappingInterface()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment interfaceWithinInterface on Pet { ...beingFragment }
|
|
|
|
fragment beingFragment on Being { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into overlapping interface in inline fragment')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoOverlappingInterfaceInInlineFragment()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment interfaceWithinInterface on Pet { ... on Being { name } }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into overlapping union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoOverlappingUnion()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment interfaceWithinUnion on CatOrDog { ...petFragment }
|
|
|
|
fragment petFragment on Pet { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2018-02-13 20:08:05 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('ignores incorrect type (caught by FragmentsOnCompositeTypes)')
|
2018-02-13 20:08:05 +03:00
|
|
|
*/
|
|
|
|
public function testIgnoresIncorrectTypeCaughtByFragmentsOnCompositeTypes()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment petFragment on Pet { ...badInADifferentWay }
|
|
|
|
fragment badInADifferentWay on String { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('different object into object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testDifferentObjectIntoObject()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidObjectWithinObject on Cat { ...dogFragment }
|
|
|
|
fragment dogFragment on Dog { barkVolume }
|
|
|
|
',
|
|
|
|
[$this->error('dogFragment', 'Cat', 'Dog', 2, 51)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('different object into object in inline fragment')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testDifferentObjectIntoObjectInInlineFragment()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidObjectWithinObjectAnon on Cat {
|
|
|
|
... on Dog { barkVolume }
|
|
|
|
}
|
|
|
|
',
|
|
|
|
[$this->errorAnon('Cat', 'Dog', 3, 9)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('object into not implementing interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testObjectIntoNotImplementingInterface()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidObjectWithinInterface on Pet { ...humanFragment }
|
|
|
|
fragment humanFragment on Human { pets { name } }
|
|
|
|
',
|
|
|
|
[$this->error('humanFragment', 'Pet', 'Human', 2, 54)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('object into not containing union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testObjectIntoNotContainingUnion()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
|
|
|
|
fragment humanFragment on Human { pets { name } }
|
|
|
|
',
|
|
|
|
[$this->error('humanFragment', 'CatOrDog', 'Human', 2, 55)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into not contained object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoNotContainedObject()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
|
|
|
|
fragment catOrDogFragment on CatOrDog { __typename }
|
|
|
|
',
|
|
|
|
[$this->error('catOrDogFragment', 'Human', 'CatOrDog', 2, 52)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into non overlapping interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoNonOverlappingInterface()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
|
|
|
|
fragment humanOrAlienFragment on HumanOrAlien { __typename }
|
|
|
|
',
|
|
|
|
[$this->error('humanOrAlienFragment', 'Pet', 'HumanOrAlien', 2, 53)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('union into non overlapping union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testUnionIntoNonOverlappingUnion()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
|
|
|
|
fragment humanOrAlienFragment on HumanOrAlien { __typename }
|
|
|
|
',
|
|
|
|
[$this->error('humanOrAlienFragment', 'CatOrDog', 'HumanOrAlien', 2, 54)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into non implementing object')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoNonImplementingObject()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
|
|
|
|
fragment intelligentFragment on Intelligent { iq }
|
|
|
|
',
|
|
|
|
[$this->error('intelligentFragment', 'Cat', 'Intelligent', 2, 54)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into non overlapping interface')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoNonOverlappingInterface()
|
|
|
|
{
|
2017-03-04 23:10:52 +03:00
|
|
|
// Ideally this should fail, but our new lazy schema doesn't scan through all types and fields
|
|
|
|
// So we don't have enough knowledge to check interface intersection and always allow this to pass:
|
|
|
|
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
2015-07-15 20:05:46 +03:00
|
|
|
fragment invalidInterfaceWithinInterface on Pet {
|
|
|
|
...intelligentFragment
|
|
|
|
}
|
|
|
|
fragment intelligentFragment on Intelligent { iq }
|
2017-03-04 23:10:52 +03:00
|
|
|
');
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into non overlapping interface in inline fragment')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoNonOverlappingInterfaceInInlineFragment()
|
|
|
|
{
|
2017-03-04 23:10:52 +03:00
|
|
|
// Ideally this should fail, but our new lazy schema doesn't scan through all types and fields
|
|
|
|
// So we don't have enough knowledge to check interface intersection and always allow this to pass:
|
|
|
|
|
|
|
|
$this->expectPassesRule(new PossibleFragmentSpreads, '
|
2015-07-15 20:05:46 +03:00
|
|
|
fragment invalidInterfaceWithinInterfaceAnon on Pet {
|
|
|
|
...on Intelligent { iq }
|
|
|
|
}
|
2017-03-04 23:10:52 +03:00
|
|
|
');
|
2015-07-15 20:05:46 +03:00
|
|
|
}
|
|
|
|
|
2016-04-25 16:29:17 +03:00
|
|
|
/**
|
2018-08-31 11:55:14 +03:00
|
|
|
* @see it('interface into non overlapping union')
|
2016-04-25 16:29:17 +03:00
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testInterfaceIntoNonOverlappingUnion()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new PossibleFragmentSpreads, '
|
|
|
|
fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
|
|
|
|
fragment petFragment on Pet { name }
|
|
|
|
',
|
|
|
|
[$this->error('petFragment', 'HumanOrAlien', 'Pet', 2, 62)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function error($fragName, $parentType, $fragType, $line, $column)
|
|
|
|
{
|
2015-08-17 17:01:55 +03:00
|
|
|
return FormattedError::create(
|
|
|
|
PossibleFragmentSpreads::typeIncompatibleSpreadMessage($fragName, $parentType, $fragType),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation($line, $column)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function errorAnon($parentType, $fragType, $line, $column)
|
|
|
|
{
|
2015-08-17 17:01:55 +03:00
|
|
|
return FormattedError::create(
|
|
|
|
PossibleFragmentSpreads::typeIncompatibleAnonSpreadMessage($parentType, $fragType),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation($line, $column)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|