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
|
|
|
|
|
|
|
use GraphQL\FormattedError;
|
|
|
|
use GraphQL\Language\SourceLocation;
|
|
|
|
use GraphQL\Validator\Rules\NoFragmentCycles;
|
|
|
|
|
|
|
|
class NoFragmentCyclesTest extends TestCase
|
|
|
|
{
|
|
|
|
// Validate: No circular fragment spreads
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it single reference is valid
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testSingleReferenceIsValid()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new NoFragmentCycles(), '
|
|
|
|
fragment fragA on Dog { ...fragB }
|
|
|
|
fragment fragB on Dog { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it spreading twice is not circular
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testSpreadingTwiceIsNotCircular()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB, ...fragB }
|
|
|
|
fragment fragB on Dog { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it spreading twice indirectly is not circular
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testSpreadingTwiceIndirectlyIsNotCircular()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB, ...fragC }
|
|
|
|
fragment fragB on Dog { ...fragC }
|
|
|
|
fragment fragC on Dog { name }
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it double spread within abstract types
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testDoubleSpreadWithinAbstractTypes()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new NoFragmentCycles, '
|
|
|
|
fragment nameFragment on Pet {
|
|
|
|
... on Dog { name }
|
|
|
|
... on Cat { name }
|
|
|
|
}
|
|
|
|
|
|
|
|
fragment spreadsInAnon on Pet {
|
|
|
|
... on Dog { ...nameFragment }
|
|
|
|
... on Cat { ...nameFragment }
|
|
|
|
}
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it does not false positive on unknown fragment
|
|
|
|
*/
|
|
|
|
public function testDoesNotFalsePositiveOnUnknownFragment()
|
|
|
|
{
|
|
|
|
$this->expectPassesRule(new NoFragmentCycles, '
|
|
|
|
fragment nameFragment on Pet {
|
|
|
|
...UnknownFragment
|
|
|
|
}
|
|
|
|
');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @it spreading recursively within field fails
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testSpreadingRecursivelyWithinFieldFails()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Human { relatives { ...fragA } },
|
|
|
|
', [
|
|
|
|
$this->cycleError('fragA', [], 2, 45)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself directly
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfDirectly()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragA }
|
|
|
|
', [
|
|
|
|
$this->cycleError('fragA', [], 2, 31)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself directly within inline fragment
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfDirectlyWithinInlineFragment()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Pet {
|
|
|
|
... on Dog {
|
|
|
|
...fragA
|
|
|
|
}
|
|
|
|
}
|
|
|
|
', [
|
|
|
|
$this->cycleError('fragA', [], 4, 11)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself indirectly
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfIndirectly()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB }
|
|
|
|
fragment fragB on Dog { ...fragA }
|
|
|
|
', [
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']),
|
2015-07-15 20:05:46 +03:00
|
|
|
[ new SourceLocation(2, 31), new SourceLocation(3, 31) ]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself indirectly reports opposite order
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfIndirectlyReportsOppositeOrder()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragB on Dog { ...fragA }
|
|
|
|
fragment fragA on Dog { ...fragB }
|
|
|
|
', [
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragB', ['fragA']),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation(2, 31), new SourceLocation(3, 31)]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself indirectly within inline fragment
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfIndirectlyWithinInlineFragment()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Pet {
|
|
|
|
... on Dog {
|
|
|
|
...fragB
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fragment fragB on Pet {
|
|
|
|
... on Dog {
|
|
|
|
...fragA
|
|
|
|
}
|
|
|
|
}
|
|
|
|
', [
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation(4, 11), new SourceLocation(9, 11)]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself deeply
|
|
|
|
*/
|
2015-07-15 20:05:46 +03:00
|
|
|
public function testNoSpreadingItselfDeeply()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB }
|
|
|
|
fragment fragB on Dog { ...fragC }
|
|
|
|
fragment fragC on Dog { ...fragO }
|
|
|
|
fragment fragX on Dog { ...fragY }
|
|
|
|
fragment fragY on Dog { ...fragZ }
|
|
|
|
fragment fragZ on Dog { ...fragO }
|
2016-04-25 00:57:09 +03:00
|
|
|
fragment fragO on Dog { ...fragP }
|
|
|
|
fragment fragP on Dog { ...fragA, ...fragX }
|
2015-07-15 20:05:46 +03:00
|
|
|
', [
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
2016-04-25 00:57:09 +03:00
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragB', 'fragC', 'fragO', 'fragP' ]),
|
2015-07-15 20:05:46 +03:00
|
|
|
[
|
|
|
|
new SourceLocation(2, 31),
|
|
|
|
new SourceLocation(3, 31),
|
|
|
|
new SourceLocation(4, 31),
|
|
|
|
new SourceLocation(8, 31),
|
2016-04-25 00:57:09 +03:00
|
|
|
new SourceLocation(9, 31),
|
2015-07-15 20:05:46 +03:00
|
|
|
]
|
|
|
|
),
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
2016-04-25 00:57:09 +03:00
|
|
|
NoFragmentCycles::cycleErrorMessage('fragO', [ 'fragP', 'fragX', 'fragY', 'fragZ' ]),
|
2015-07-15 20:05:46 +03:00
|
|
|
[
|
2016-04-25 00:57:09 +03:00
|
|
|
new SourceLocation(8, 31),
|
|
|
|
new SourceLocation(9, 41),
|
2015-07-15 20:05:46 +03:00
|
|
|
new SourceLocation(5, 31),
|
|
|
|
new SourceLocation(6, 31),
|
|
|
|
new SourceLocation(7, 31),
|
|
|
|
]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself deeply two paths
|
|
|
|
*/
|
|
|
|
public function testNoSpreadingItselfDeeplyTwoPaths()
|
2015-07-15 20:05:46 +03:00
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB, ...fragC }
|
|
|
|
fragment fragB on Dog { ...fragA }
|
|
|
|
fragment fragC on Dog { ...fragA }
|
|
|
|
', [
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', ['fragB']),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation(2, 31), new SourceLocation(3, 31)]
|
|
|
|
),
|
2015-08-17 17:01:55 +03:00
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', ['fragC']),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation(2, 41), new SourceLocation(4, 31)]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2016-04-25 00:57:09 +03:00
|
|
|
/**
|
|
|
|
* @it no spreading itself deeply two paths -- alt traverse order
|
|
|
|
*/
|
|
|
|
public function testNoSpreadingItselfDeeplyTwoPathsTraverseOrder()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragC }
|
|
|
|
fragment fragB on Dog { ...fragC }
|
|
|
|
fragment fragC on Dog { ...fragA, ...fragB }
|
|
|
|
', [
|
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragC' ]),
|
|
|
|
[new SourceLocation(2,31), new SourceLocation(4,31)]
|
|
|
|
),
|
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragC', [ 'fragB' ]),
|
|
|
|
[new SourceLocation(4, 41), new SourceLocation(3, 31)]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @it no spreading itself deeply and immediately
|
|
|
|
*/
|
|
|
|
public function testNoSpreadingItselfDeeplyAndImmediately()
|
|
|
|
{
|
|
|
|
$this->expectFailsRule(new NoFragmentCycles, '
|
|
|
|
fragment fragA on Dog { ...fragB }
|
|
|
|
fragment fragB on Dog { ...fragB, ...fragC }
|
|
|
|
fragment fragC on Dog { ...fragA, ...fragB }
|
|
|
|
', [
|
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragB', []),
|
|
|
|
[new SourceLocation(3, 31)]
|
|
|
|
),
|
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragA', [ 'fragB', 'fragC' ]),
|
|
|
|
[
|
|
|
|
new SourceLocation(2, 31),
|
|
|
|
new SourceLocation(3, 41),
|
|
|
|
new SourceLocation(4, 31)
|
|
|
|
]
|
|
|
|
),
|
|
|
|
FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage('fragB', [ 'fragC' ]),
|
|
|
|
[new SourceLocation(3, 41), new SourceLocation(4, 41)]
|
|
|
|
)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2015-07-15 20:05:46 +03:00
|
|
|
private function cycleError($fargment, $spreadNames, $line, $column)
|
|
|
|
{
|
2015-08-17 17:01:55 +03:00
|
|
|
return FormattedError::create(
|
|
|
|
NoFragmentCycles::cycleErrorMessage($fargment, $spreadNames),
|
2015-07-15 20:05:46 +03:00
|
|
|
[new SourceLocation($line, $column)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|