Completed
Push — master ( 655442...cd31b0 )
by Vladimir
17s queued 14s
created

QueryComplexityTest   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 27
eloc 79
c 1
b 0
f 0
dl 0
loc 223
rs 10

22 Methods

Rating   Name   Duplication   Size   Complexity  
A testSimpleQueries() 0 5 1
A testCustomComplexityWithVariablesQueries() 0 7 1
A testGetQueryComplexity() 0 13 1
A assertDocumentValidators() 0 10 4
A getRule() 0 9 3
A testFragmentQueries() 0 5 1
A testCustomComplexityQueries() 0 5 1
A testQueryWithEnabledSkipDirectives() 0 7 1
A testQueryWithMultipleDirectives() 0 10 1
A testAliasesQueries() 0 5 1
A testQueryWithEnabledIncludeDirectives() 0 7 1
A testQueryWithDisabledIncludeDirectives() 0 7 1
A testQueryWithDisabledSkipDirectives() 0 7 1
A testInlineFragmentQueries() 0 5 1
A testCustomComplexityWithArgsQueries() 0 5 1
A testSkippedWhenThereAreOtherValidationErrors() 0 32 1
A testIntrospectionTypeMetaFieldQuery() 0 3 1
A testQueryWithCustomDirective() 0 5 1
A testTypeNameMetaFieldQuery() 0 3 1
A getErrorMessage() 0 3 1
A testQueryWithCustomAndSkipDirective() 0 7 1
A testComplexityIntrospectionQuery() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQL\Tests\Validator;
6
7
use GraphQL\Error\Error;
8
use GraphQL\Language\AST\NodeKind;
9
use GraphQL\Language\Parser;
10
use GraphQL\Validator\DocumentValidator;
11
use GraphQL\Validator\Rules\CustomValidationRule;
12
use GraphQL\Validator\Rules\QueryComplexity;
13
use GraphQL\Validator\ValidationContext;
14
use function count;
15
16
class QueryComplexityTest extends QuerySecurityTestCase
17
{
18
    /** @var QueryComplexity */
19
    private static $rule;
20
21
    public function testSimpleQueries() : void
22
    {
23
        $query = 'query MyQuery { human { firstName } }';
24
25
        $this->assertDocumentValidators($query, 2, 3);
26
    }
27
28
    public function testGetQueryComplexity() : void
29
    {
30
        $query = 'query MyQuery { human { firstName } }';
31
32
        $rule = $this->getRule(5);
33
34
        DocumentValidator::validate(
35
            QuerySecuritySchema::buildSchema(),
36
            Parser::parse($query),
37
            [$rule]
38
        );
39
40
        self::assertEquals(2, $rule->getQueryComplexity(), $query);
41
    }
42
43
    private function assertDocumentValidators($query, $queryComplexity, $startComplexity)
44
    {
45
        for ($maxComplexity = $startComplexity; $maxComplexity >= 0; --$maxComplexity) {
46
            $positions = [];
47
48
            if ($maxComplexity < $queryComplexity && $maxComplexity !== QueryComplexity::DISABLED) {
49
                $positions = [$this->createFormattedError($maxComplexity, $queryComplexity)];
50
            }
51
52
            $this->assertDocumentValidator($query, $maxComplexity, $positions);
53
        }
54
    }
55
56
    public function testInlineFragmentQueries() : void
57
    {
58
        $query = 'query MyQuery { human { ... on Human { firstName } } }';
59
60
        $this->assertDocumentValidators($query, 2, 3);
61
    }
62
63
    public function testFragmentQueries() : void
64
    {
65
        $query = 'query MyQuery { human { ...F1 } } fragment F1 on Human { firstName}';
66
67
        $this->assertDocumentValidators($query, 2, 3);
68
    }
69
70
    public function testAliasesQueries() : void
71
    {
72
        $query = 'query MyQuery { thomas: human(name: "Thomas") { firstName } jeremy: human(name: "Jeremy") { firstName } }';
73
74
        $this->assertDocumentValidators($query, 4, 5);
75
    }
76
77
    public function testCustomComplexityQueries() : void
78
    {
79
        $query = 'query MyQuery { human { dogs { name } } }';
80
81
        $this->assertDocumentValidators($query, 12, 13);
82
    }
83
84
    public function testCustomComplexityWithArgsQueries() : void
85
    {
86
        $query = 'query MyQuery { human { dogs(name: "Root") { name } } }';
87
88
        $this->assertDocumentValidators($query, 3, 4);
89
    }
90
91
    public function testCustomComplexityWithVariablesQueries() : void
92
    {
93
        $query = 'query MyQuery($dog: String!) { human { dogs(name: $dog) { name } } }';
94
95
        $this->getRule()->setRawVariableValues(['dog' => 'Roots']);
96
97
        $this->assertDocumentValidators($query, 3, 4);
98
    }
99
100
    /**
101
     * @param int $maxDepth
102
     *
103
     * @return QueryComplexity
104
     */
105
    protected function getRule($maxDepth = null)
106
    {
107
        if (self::$rule === null) {
108
            self::$rule = new QueryComplexity($maxDepth);
109
        } elseif ($maxDepth !== null) {
110
            self::$rule->setMaxQueryComplexity($maxDepth);
111
        }
112
113
        return self::$rule;
114
    }
115
116
    public function testQueryWithEnabledIncludeDirectives() : void
117
    {
118
        $query = 'query MyQuery($withDogs: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name } } }';
119
120
        $this->getRule()->setRawVariableValues(['withDogs' => true]);
121
122
        $this->assertDocumentValidators($query, 3, 4);
123
    }
124
125
    public function testQueryWithDisabledIncludeDirectives() : void
126
    {
127
        $query = 'query MyQuery($withDogs: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name } } }';
128
129
        $this->getRule()->setRawVariableValues(['withDogs' => false]);
130
131
        $this->assertDocumentValidators($query, 1, 2);
132
    }
133
134
    public function testQueryWithEnabledSkipDirectives() : void
135
    {
136
        $query = 'query MyQuery($withoutDogs: Boolean!) { human { dogs(name: "Root") @skip(if:$withoutDogs) { name } } }';
137
138
        $this->getRule()->setRawVariableValues(['withoutDogs' => true]);
139
140
        $this->assertDocumentValidators($query, 1, 2);
141
    }
142
143
    public function testQueryWithDisabledSkipDirectives() : void
144
    {
145
        $query = 'query MyQuery($withoutDogs: Boolean!) { human { dogs(name: "Root") @skip(if:$withoutDogs) { name } } }';
146
147
        $this->getRule()->setRawVariableValues(['withoutDogs' => false]);
148
149
        $this->assertDocumentValidators($query, 3, 4);
150
    }
151
152
    public function testQueryWithMultipleDirectives() : void
153
    {
154
        $query = 'query MyQuery($withDogs: Boolean!, $withoutDogName: Boolean!) { human { dogs(name: "Root") @include(if:$withDogs) { name @skip(if:$withoutDogName) } } }';
155
156
        $this->getRule()->setRawVariableValues([
157
            'withDogs'       => true,
158
            'withoutDogName' => true,
159
        ]);
160
161
        $this->assertDocumentValidators($query, 2, 3);
162
    }
163
164
    public function testQueryWithCustomDirective() : void
165
    {
166
        $query = 'query MyQuery { human { ... on Human { firstName @foo(bar: false) } } }';
167
168
        $this->assertDocumentValidators($query, 2, 3);
169
    }
170
171
    public function testQueryWithCustomAndSkipDirective() : void
172
    {
173
        $query = 'query MyQuery($withoutDogs: Boolean!) { human { dogs(name: "Root") @skip(if:$withoutDogs) { name @foo(bar: true) } } }';
174
175
        $this->getRule()->setRawVariableValues(['withoutDogs' => true]);
176
177
        $this->assertDocumentValidators($query, 1, 2);
178
    }
179
180
    public function testComplexityIntrospectionQuery() : void
181
    {
182
        $this->assertIntrospectionQuery(181);
183
    }
184
185
    public function testIntrospectionTypeMetaFieldQuery() : void
186
    {
187
        $this->assertIntrospectionTypeMetaFieldQuery(2);
188
    }
189
190
    public function testTypeNameMetaFieldQuery() : void
191
    {
192
        $this->assertTypeNameMetaFieldQuery(3);
193
    }
194
195
    public function testSkippedWhenThereAreOtherValidationErrors() : void
196
    {
197
        $query = 'query MyQuery { human(name: INVALID_VALUE) { dogs {name} } }';
198
199
        $reportedError = new Error('OtherValidatorError');
200
        $otherRule     = new CustomValidationRule(
201
            'otherRule',
202
            static function (ValidationContext $context) use ($reportedError) {
203
                return [
204
                    NodeKind::OPERATION_DEFINITION => [
205
                        'leave' => static function () use ($context, $reportedError) {
206
                            $context->reportError($reportedError);
207
                        },
208
                    ],
209
                ];
210
            }
211
        );
212
213
        $errors = DocumentValidator::validate(
214
            QuerySecuritySchema::buildSchema(),
215
            Parser::parse($query),
216
            [$otherRule, $this->getRule(1)]
217
        );
218
219
        self::assertEquals(1, count($errors));
220
        self::assertSame($reportedError, $errors[0]);
221
222
        $this->expectException(Error::class);
223
        DocumentValidator::validate(
224
            QuerySecuritySchema::buildSchema(),
225
            Parser::parse($query),
226
            [$this->getRule(1)]
227
        );
228
    }
229
230
    /**
231
     * @param int $max
232
     * @param int $count
233
     *
234
     * @return string
235
     */
236
    protected function getErrorMessage($max, $count)
237
    {
238
        return QueryComplexity::maxQueryComplexityErrorMessage($max, $count);
239
    }
240
}
241