Completed
Push — master ( 5b603d...f09253 )
by David
12s
created

php$2 ➔ testQueryProviderWithIterable()   A

Complexity

Conditions 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
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\TestObject;
19
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestType;
20
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeId;
21
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeMissingAnnotation;
22
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeMissingField;
23
use TheCodingMachine\GraphQL\Controllers\Fixtures\TestTypeWithSourceFieldInterface;
24
use TheCodingMachine\GraphQL\Controllers\Containers\EmptyContainer;
25
use TheCodingMachine\GraphQL\Controllers\Containers\BasicAutoWiringContainer;
26
use TheCodingMachine\GraphQL\Controllers\Security\AuthenticationServiceInterface;
27
use TheCodingMachine\GraphQL\Controllers\Security\AuthorizationServiceInterface;
28
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthenticationService;
29
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthorizationService;
30
use TheCodingMachine\GraphQL\Controllers\Annotations\Query;
31
use TheCodingMachine\GraphQL\Controllers\Types\DateTimeType;
32
33
class ControllerQueryProviderTest extends AbstractQueryProviderTest
34
{
35
    public function testQueryProvider()
36
    {
37
        $controller = new TestController();
38
39
        $queryProvider = $this->buildControllerQueryProvider($controller);
40
41
        $queries = $queryProvider->getQueries();
42
43
        $this->assertCount(6, $queries);
44
        $usersQuery = $queries[0];
45
        $this->assertSame('test', $usersQuery->name);
46
47
        $this->assertCount(8, $usersQuery->args);
48
        $this->assertSame('int', $usersQuery->args[0]->name);
49
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[0]->getType());
50
        $this->assertInstanceOf(IntType::class, $usersQuery->args[0]->getType()->getWrappedType());
51
        $this->assertInstanceOf(StringType::class, $usersQuery->args[7]->getType());
52
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[1]->getType());
53
        $this->assertInstanceOf(ListOfType::class, $usersQuery->args[1]->getType()->getWrappedType());
54
        $this->assertInstanceOf(NonNull::class, $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType());
55
        $this->assertInstanceOf(InputObjectType::class, $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType()->getWrappedType());
56
        $this->assertInstanceOf(BooleanType::class, $usersQuery->args[2]->getType());
57
        $this->assertInstanceOf(FloatType::class, $usersQuery->args[3]->getType());
58
        $this->assertInstanceOf(DateTimeType::class, $usersQuery->args[4]->getType());
59
        $this->assertInstanceOf(DateTimeType::class, $usersQuery->args[5]->getType());
60
        $this->assertInstanceOf(StringType::class, $usersQuery->args[6]->getType());
61
        $this->assertSame('TestObject', $usersQuery->args[1]->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
62
63
        $context = ['int' => 42, 'string' => 'foo', 'list' => [
64
            ['test' => 42],
65
            ['test' => 12],
66
        ],
67
            'boolean' => true,
68
            'float' => 4.2,
69
            'dateTimeImmutable' => '2017-01-01 01:01:01',
70
            'dateTime' => '2017-01-01 01:01:01'
71
        ];
72
73
        $resolve = $usersQuery->resolveFn;
74
        $result = $resolve('foo', $context);
75
76
        $this->assertInstanceOf(TestObject::class, $result);
77
        $this->assertSame('foo424212true4.22017010101010120170101010101default', $result->getTest());
78
79
        unset($context['string']); // Testing null default value
80
        $result = $resolve('foo', $context);
81
82
        $this->assertSame('424212true4.22017010101010120170101010101default', $result->getTest());
83
    }
84
85
    public function testMutations()
86
    {
87
        $controller = new TestController();
88
89
        $queryProvider = $this->buildControllerQueryProvider($controller);
90
91
        $mutations = $queryProvider->getMutations();
92
93
        $this->assertCount(1, $mutations);
94
        $mutation = $mutations[0];
95
        $this->assertSame('mutation', $mutation->name);
96
97
        $resolve = $mutation->resolveFn;
98
        $result = $resolve('foo', ['testObject' => ['test' => 42]]);
99
100
        $this->assertInstanceOf(TestObject::class, $result);
101
        $this->assertEquals('42', $result->getTest());
102
    }
103
104
    public function testErrors()
105
    {
106
        $controller = new class
107
        {
108
            /**
109
             * @Query
110
             * @return string
111
             */
112
            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

112
            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...
113
            {
114
                return 'foo';
115
            }
116
        };
117
118
        $queryProvider = $this->buildControllerQueryProvider($controller);
119
120
        $this->expectException(MissingTypeHintException::class);
121
        $queryProvider->getQueries();
122
    }
123
124
    public function testQueryProviderWithFixedReturnType()
125
    {
126
        $controller = new TestController();
127
128
        $queryProvider = $this->buildControllerQueryProvider($controller);
129
130
        $queries = $queryProvider->getQueries();
131
132
        $this->assertCount(6, $queries);
133
        $fixedQuery = $queries[1];
134
135
        $this->assertInstanceOf(StringType::class, $fixedQuery->getType());
136
    }
137
138
    public function testNameFromAnnotation()
139
    {
140
        $controller = new TestController();
141
142
        $queryProvider = $this->buildControllerQueryProvider($controller);
143
144
        $queries = $queryProvider->getQueries();
145
146
        $query = $queries[2];
147
148
        $this->assertSame('nameFromAnnotation', $query->name);
149
    }
150
151
    public function testSourceField()
152
    {
153
        $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

153
        $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...
154
155
        $queryProvider = $this->buildControllerQueryProvider($controller);
156
157
        $fields = $queryProvider->getFields();
158
159
        $this->assertCount(2, $fields);
160
161
        $this->assertSame('customField', $fields['customField']->name);
162
        $this->assertSame('test', $fields['test']->name);
163
    }
164
165
    public function testLoggedInSourceField()
166
    {
167
        $queryProvider = new ControllerQueryProvider(
168
            new TestType(),
169
            $this->getAnnotationReader(),
170
            $this->getTypeMapper(),
171
            $this->getHydrator(),
172
            new class implements AuthenticationServiceInterface {
173
                public function isLogged(): bool
174
                {
175
                    return true;
176
                }
177
            },
178
            new VoidAuthorizationService(),
179
            new EmptyContainer()
180
        );
181
182
        $fields = $queryProvider->getFields();
183
        $this->assertCount(3, $fields);
184
185
        $this->assertSame('testBool', $fields['testBool']->name);
186
187
    }
188
189
    public function testRightInSourceField()
190
    {
191
        $queryProvider = new ControllerQueryProvider(
192
            new TestType(),
193
            $this->getAnnotationReader(),
194
            $this->getTypeMapper(),
195
            $this->getHydrator(),
196
            new VoidAuthenticationService(),
197
            new class implements AuthorizationServiceInterface {
198
                public function isAllowed(string $right): bool
199
                {
200
                    return true;
201
                }
202
            },new EmptyContainer()
203
        );
204
205
        $fields = $queryProvider->getFields();
206
        $this->assertCount(3, $fields);
207
208
        $this->assertSame('testRight', $fields['testRight']->name);
209
210
    }
211
212
    public function testMissingTypeAnnotation()
213
    {
214
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeMissingAnnotation());
215
216
        $this->expectException(MissingAnnotationException::class);
217
        $queryProvider->getFields();
218
    }
219
220
    public function testSourceFieldDoesNotExists()
221
    {
222
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeMissingField());
223
224
        $this->expectException(FieldNotFoundException::class);
225
        $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()");
226
        $queryProvider->getFields();
227
    }
228
229
    public function testSourceFieldIsId()
230
    {
231
        $queryProvider = $this->buildControllerQueryProvider(new TestTypeId());
232
        $fields = $queryProvider->getFields();
233
        $this->assertCount(1, $fields);
234
235
        $this->assertSame('test', $fields['test']->name);
236
        $this->assertInstanceOf(NonNull::class, $fields['test']->getType());
237
        $this->assertInstanceOf(IDType::class, $fields['test']->getType()->getWrappedType());
238
    }
239
240
    public function testFromSourceFieldsInterface()
241
    {
242
        $queryProvider = new ControllerQueryProvider(
243
            new TestTypeWithSourceFieldInterface(),
244
            $this->getAnnotationReader(),
245
            $this->getTypeMapper(),
246
            $this->getHydrator(),
247
            new VoidAuthenticationService(),
248
            new VoidAuthorizationService(),
249
            new EmptyContainer()
250
        );
251
        $fields = $queryProvider->getFields();
252
        $this->assertCount(1, $fields);
253
254
        $this->assertSame('test', $fields['test']->name);
255
256
    }
257
258
    public function testQueryProviderWithIterableClass()
259
    {
260
        $controller = new TestController();
261
262
        $queryProvider = $this->buildControllerQueryProvider($controller);
263
264
        $queries = $queryProvider->getQueries();
265
266
        $this->assertCount(6, $queries);
267
        $iterableQuery = $queries[3];
268
269
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType());
270
        $this->assertInstanceOf(ListOfType::class, $iterableQuery->getType()->getWrappedType());
271
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType()->getWrappedType()->getWrappedType());
272
        $this->assertInstanceOf(ObjectType::class, $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType());
273
        $this->assertSame('TestObject', $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
274
    }
275
276
    public function testQueryProviderWithIterable()
277
    {
278
        $controller = new TestController();
279
280
        $queryProvider = $this->buildControllerQueryProvider($controller);
281
282
        $queries = $queryProvider->getQueries();
283
284
        $this->assertCount(6, $queries);
285
        $iterableQuery = $queries[4];
286
287
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType());
288
        $this->assertInstanceOf(ListOfType::class, $iterableQuery->getType()->getWrappedType());
289
        $this->assertInstanceOf(NonNull::class, $iterableQuery->getType()->getWrappedType()->getWrappedType());
290
        $this->assertInstanceOf(ObjectType::class, $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType());
291
        $this->assertSame('TestObject', $iterableQuery->getType()->getWrappedType()->getWrappedType()->getWrappedType()->name);
292
    }
293
294
    public function testNoReturnTypeError()
295
    {
296
        $queryProvider = $this->buildControllerQueryProvider(new TestControllerNoReturnType());
297
        $this->expectException(TypeMappingException::class);
298
        $queryProvider->getQueries();
299
    }
300
301
    public function testQueryProviderWithUnion()
302
    {
303
        $controller = new TestController();
304
305
        $queryProvider = $this->buildControllerQueryProvider($controller);
306
307
        $queries = $queryProvider->getQueries();
308
309
        $this->assertCount(6, $queries);
310
        $unionQuery = $queries[5];
311
312
        $this->assertInstanceOf(NonNull::class, $unionQuery->getType());
313
        $this->assertInstanceOf(UnionType::class, $unionQuery->getType()->getWrappedType());
314
        $this->assertInstanceOf(ObjectType::class, $unionQuery->getType()->getWrappedType()->getTypes()[0]);
315
        $this->assertSame('TestObject', $unionQuery->getType()->getWrappedType()->getTypes()[0]->name);
316
        $this->assertInstanceOf(ObjectType::class, $unionQuery->getType()->getWrappedType()->getTypes()[1]);
317
        $this->assertSame('TestObject2', $unionQuery->getType()->getWrappedType()->getTypes()[1]->name);
318
    }
319
}
320