Completed
Push — master ( 951598...dccc62 )
by David
03:06 queued 44s
created

php$2 ➔ testSourceFieldIsId()   A

Complexity

Conditions 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
rs 9.9666
cc 1
1
<?php
2
3
namespace TheCodingMachine\GraphQL\Controllers;
4
5
use Doctrine\Common\Annotations\AnnotationReader;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, TheCodingMachine\GraphQL...ollers\AnnotationReader. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use GraphQL\Type\Definition\BooleanType;
7
use GraphQL\Type\Definition\FloatType;
8
use GraphQL\Type\Definition\IDType;
9
use GraphQL\Type\Definition\InputObjectType;
10
use GraphQL\Type\Definition\IntType;
11
use GraphQL\Type\Definition\ListOfType;
12
use GraphQL\Type\Definition\NonNull;
13
use GraphQL\Type\Definition\StringType;
14
use GraphQL\Type\Definition\ObjectType;
15
use GraphQL\Type\Definition\UnionType;
16
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestController;
17
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestControllerNoReturnType;
18
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestControllerWithInvalidInputType;
19
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestControllerWithInvalidReturnType;
20
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestObject;
21
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestType;
22
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeId;
23
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeMissingAnnotation;
24
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeMissingField;
25
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeWithSourceFieldInterface;
26
use TheCodingMachine\GraphQL\Controllers\Containers\EmptyContainer;
27
use TheCodingMachine\GraphQL\Controllers\Containers\BasicAutoWiringContainer;
28
use TheCodingMachine\GraphQL\Controllers\Mappers\CannotMapTypeException;
29
use TheCodingMachine\GraphQL\Controllers\Security\AuthenticationServiceInterface;
30
use TheCodingMachine\GraphQL\Controllers\Security\AuthorizationServiceInterface;
31
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthenticationService;
32
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthorizationService;
33
use TheCodingMachine\GraphQL\Controllers\Annotations\Query;
34
use TheCodingMachine\GraphQL\Controllers\Types\DateTimeType;
35
36
class ControllerQueryProviderTest extends AbstractQueryProviderTest
37
{
38
    public function testQueryProvider()
39
    {
40
        $controller = new TestController();
41
42
        $queryProvider = $this->buildControllerQueryProvider($controller);
43
44
        $queries = $queryProvider->getQueries();
45
46
        $this->assertCount(6, $queries);
47
        $usersQuery = $queries[0];
48
        $this->assertSame('test', $usersQuery->name);
49
50
        $this->assertCount(8, $usersQuery->args);
51
        $this->assertSame('int', $usersQuery->args[0]->name);
52
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[0]->getType());
53
        $this->assertInstanceOf(IntType::class, $usersQuery->args[0]->getType()->getWrappedType());
54
        $this->assertInstanceOf(StringType::class, $usersQuery->args[7]->getType());
55
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[1]->getType());
56
        $this->assertInstanceOf(ListOfType::class, $usersQuery->args[1]->getType()->getWrappedType());
57
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType());
58
        $this->assertInstanceOf(InputObjectType::class, $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType()->getWrappedType());
59
        $this->assertInstanceOf(BooleanType::class, $usersQuery->args[2]->getType());
60
        $this->assertInstanceOf(FloatType::class, $usersQuery->args[3]->getType());
61
        $this->assertInstanceOf(DateTimeType::class, $usersQuery->args[4]->getType());
62
        $this->assertInstanceOf(DateTimeType::class, $usersQuery->args[5]->getType());
63
        $this->assertInstanceOf(StringType::class, $usersQuery->args[6]->getType());
64
        $this->assertSame('TestObject', $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
65
66
        $context = ['int' => 42, 'string' => 'foo', 'list' => [
67
            ['test' => 42],
68
            ['test' => 12],
69
        ],
70
            'boolean' => true,
71
            'float' => 4.2,
72
            'dateTimeImmutable' => '2017-01-01 01:01:01',
73
            'dateTime' => '2017-01-01 01:01:01'
74
        ];
75
76
        $resolve = $usersQuery->resolveFn;
77
        $result = $resolve('foo', $context);
78
79
        $this->assertInstanceOf(TestObject::class, $result);
80
        $this->assertSame('foo424212true4.22017010101010120170101010101default', $result->getTest());
81
82
        unset($context['string']); // Testing null default value
83
        $result = $resolve('foo', $context);
84
85
        $this->assertSame('424212true4.22017010101010120170101010101default', $result->getTest());
86
    }
87
88
    public function testMutations()
89
    {
90
        $controller = new TestController();
91
92
        $queryProvider = $this->buildControllerQueryProvider($controller);
93
94
        $mutations = $queryProvider->getMutations();
95
96
        $this->assertCount(1, $mutations);
97
        $mutation = $mutations[0];
98
        $this->assertSame('mutation', $mutation->name);
99
100
        $resolve = $mutation->resolveFn;
101
        $result = $resolve('foo', ['testObject' => ['test' => 42]]);
102
103
        $this->assertInstanceOf(TestObject::class, $result);
104
        $this->assertEquals('42', $result->getTest());
105
    }
106
107
    public function testErrors()
108
    {
109
        $controller = new class
110
        {
111
            /**
112
             * @Query
113
             * @return string
114
             */
115
            public function test($noTypeHint): string
0 ignored issues
show
Unused Code introduced by
The parameter $noTypeHint is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

115
            public function test(/** @scrutinizer ignore-unused */ $noTypeHint): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
116
            {
117
                return 'foo';
118
            }
119
        };
120
121
        $queryProvider = $this->buildControllerQueryProvider($controller);
122
123
        $this->expectException(MissingTypeHintException::class);
124
        $queryProvider->getQueries();
125
    }
126
127
    public function testQueryProviderWithFixedReturnType()
128
    {
129
        $controller = new TestController();
130
131
        $queryProvider = $this->buildControllerQueryProvider($controller);
132
133
        $queries = $queryProvider->getQueries();
134
135
        $this->assertCount(6, $queries);
136
        $fixedQuery = $queries[1];
137
138
        $this->assertInstanceOf(StringType::class, $fixedQuery->getType());
139
    }
140
141
    public function testNameFromAnnotation()
142
    {
143
        $controller = new TestController();
144
145
        $queryProvider = $this->buildControllerQueryProvider($controller);
146
147
        $queries = $queryProvider->getQueries();
148
149
        $query = $queries[2];
150
151
        $this->assertSame('nameFromAnnotation', $query->name);
152
    }
153
154
    public function testSourceField()
155
    {
156
        $controller = new TestType($this->getRegistry());
0 ignored issues
show
Unused Code introduced by
The call to TheCodingMachine\GraphQL...TestType::__construct() has too many arguments starting with $this->getRegistry(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

156
        $controller = /** @scrutinizer ignore-call */ new TestType($this->getRegistry());

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
157
158
        $queryProvider = $this->buildControllerQueryProvider($controller);
159
160
        $fields = $queryProvider->getFields();
161
162
        $this->assertCount(2, $fields);
163
164
        $this->assertSame('customField', $fields['customField']->name);
165
        $this->assertSame('test', $fields['test']->name);
166
    }
167
168
    public function testLoggedInSourceField()
169
    {
170
        $queryProvider = new ControllerQueryProvider(
171
            new TestType(),
172
            $this->getAnnotationReader(),
173
            $this->getTypeMapper(),
174
            $this->getHydrator(),
175
            new class implements AuthenticationServiceInterface {
176
                public function isLogged(): bool
177
                {
178
                    return true;
179
                }
180
            },
181
            new VoidAuthorizationService(),
182
            new EmptyContainer()
183
        );
184
185
        $fields = $queryProvider->getFields();
186
        $this->assertCount(3, $fields);
187
188
        $this->assertSame('testBool', $fields['testBool']->name);
189
190
    }
191
192
    public function testRightInSourceField()
193
    {
194
        $queryProvider = new ControllerQueryProvider(
195
            new TestType(),
196
            $this->getAnnotationReader(),
197
            $this->getTypeMapper(),
198
            $this->getHydrator(),
199
            new VoidAuthenticationService(),
200
            new class implements AuthorizationServiceInterface {
201
                public function isAllowed(string $right): bool
202
                {
203
                    return true;
204
                }
205
            },new EmptyContainer()
206
        );
207
208
        $fields = $queryProvider->getFields();
209
        $this->assertCount(3, $fields);
210
211
        $this->assertSame('testRight', $fields['testRight']->name);
212
213
    }
214
215
    public function testMissingTypeAnnotation()
216
    {
217
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeMissingAnnotation());
218
219
        $this->expectException(MissingAnnotationException::class);
220
        $queryProvider->getFields();
221
    }
222
223
    public function testSourceFieldDoesNotExists()
224
    {
225
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeMissingField());
226
227
        $this->expectException(FieldNotFoundException::class);
228
        $this->expectExceptionMessage("There is an issue with a @SourceField annotation in class \"TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeMissingField\": Could not find a getter or a isser for field \"notExists\". Looked for: \"TheCodingMachine\GraphQL\Controllers\Fixtures\TestObject::getNotExists()\", \"TheCodingMachine\GraphQL\Controllers\Fixtures\TestObject::isNotExists()");
229
        $queryProvider->getFields();
230
    }
231
232
    public function testSourceFieldIsId()
233
    {
234
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeId());
235
        $fields = $queryProvider->getFields();
236
        $this->assertCount(1, $fields);
237
238
        $this->assertSame('test', $fields['test']->name);
239
        $this->assertInstanceOf(NonNull::class, $fields['test']->getType());
240
        $this->assertInstanceOf(IDType::class, $fields['test']->getType()->getWrappedType());
241
    }
242
243
    public function testFromSourceFieldsInterface()
244
    {
245
        $queryProvider = new ControllerQueryProvider(
246
            new TestTypeWithSourceFieldInterface(),
247
            $this->getAnnotationReader(),
248
            $this->getTypeMapper(),
249
            $this->getHydrator(),
250
            new VoidAuthenticationService(),
251
            new VoidAuthorizationService(),
252
            new EmptyContainer()
253
        );
254
        $fields = $queryProvider->getFields();
255
        $this->assertCount(1, $fields);
256
257
        $this->assertSame('test', $fields['test']->name);
258
259
    }
260
261
    public function testQueryProviderWithIterableClass()
262
    {
263
        $controller = new TestController();
264
265
        $queryProvider = $this->buildControllerQueryProvider($controller);
266
267
        $queries = $queryProvider->getQueries();
268
269
        $this->assertCount(6, $queries);
270
        $iterableQuery = $queries[3];
271
272
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType());
273
        $this->assertInstanceOf(ListOfType::class, $iterableQuery->getType()->getWrappedType());
274
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType()->getWrappedType()->getWrappedType());
275
        $this->assertInstanceOf(ObjectType::class, $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType());
276
        $this->assertSame('TestObject', $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
277
    }
278
279
    public function testQueryProviderWithIterable()
280
    {
281
        $controller = new TestController();
282
283
        $queryProvider = $this->buildControllerQueryProvider($controller);
284
285
        $queries = $queryProvider->getQueries();
286
287
        $this->assertCount(6, $queries);
288
        $iterableQuery = $queries[4];
289
290
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType());
291
        $this->assertInstanceOf(ListOfType::class, $iterableQuery->getType()->getWrappedType());
292
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType()->getWrappedType()->getWrappedType());
293
        $this->assertInstanceOf(ObjectType::class, $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType());
294
        $this->assertSame('TestObject', $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
295
    }
296
297
    public function testNoReturnTypeError()
298
    {
299
        $queryProvider = $this->buildControllerQueryProvider(new TestControllerNoReturnType());
300
        $this->expectException(TypeMappingException::class);
301
        $queryProvider->getQueries();
302
    }
303
304
    public function testQueryProviderWithUnion()
305
    {
306
        $controller = new TestController();
307
308
        $queryProvider = $this->buildControllerQueryProvider($controller);
309
310
        $queries = $queryProvider->getQueries();
311
312
        $this->assertCount(6, $queries);
313
        $unionQuery = $queries[5];
314
315
        $this->assertInstanceOf(NonNull::class, $unionQuery->getType());
316
        $this->assertInstanceOf(UnionType::class, $unionQuery->getType()->getWrappedType());
317
        $this->assertInstanceOf(ObjectType::class, $unionQuery->getType()->getWrappedType()->getTypes()[0]);
318
        $this->assertSame('TestObject', $unionQuery->getType()->getWrappedType()->getTypes()[0]->name);
319
        $this->assertInstanceOf(ObjectType::class, $unionQuery->getType()->getWrappedType()->getTypes()[1]);
320
        $this->assertSame('TestObject2', $unionQuery->getType()->getWrappedType()->getTypes()[1]->name);
321
    }
322
323
    public function testQueryProviderWithInvalidInputType()
324
    {
325
        $controller = new TestControllerWithInvalidInputType();
326
327
        $queryProvider = $this->buildControllerQueryProvider($controller);
328
329
        $this->expectException(CannotMapTypeException::class);
330
        $this->expectExceptionMessage('For parameter $foo, in TheCodingMachine\GraphQL\Controllers\Fixtures\TestControllerWithInvalidInputType::test, cannot map class "Exception" to a known GraphQL input type. Check your TypeMapper configuration.');
331
        $queryProvider->getQueries();
332
    }
333
334
    public function testQueryProviderWithInvalidReturnType()
335
    {
336
        $controller = new TestControllerWithInvalidReturnType();
337
338
        $queryProvider = $this->buildControllerQueryProvider($controller);
339
340
        $this->expectException(CannotMapTypeException::class);
341
        $this->expectExceptionMessage('For return type of TheCodingMachine\GraphQL\Controllers\Fixtures\TestControllerWithInvalidReturnType::test, cannot map class "Exception" to a known GraphQL type. Check your TypeMapper configuration.');
342
        $queryProvider->getQueries();
343
    }
344
}
345