Total Complexity | 29 |
Total Lines | 298 |
Duplicated Lines | 0 % |
Changes | 0 |
1 | <?php |
||
23 | class TypesTest extends \PHPUnit\Framework\TestCase |
||
24 | { |
||
25 | use EntityManagerTrait; |
||
26 | |||
27 | /** |
||
28 | * @var Types |
||
29 | */ |
||
30 | private $types; |
||
31 | |||
32 | public function setUp(): void |
||
33 | { |
||
34 | $this->setUpEntityManager(); |
||
35 | |||
36 | $mapping = [ |
||
37 | DateTime::class => DateTimeType::class, |
||
38 | stdClass::class => CustomType::class, |
||
39 | ]; |
||
40 | $this->types = new Types($this->entityManager, $mapping); |
||
41 | } |
||
42 | |||
43 | public function testBlogMapping(): void |
||
44 | { |
||
45 | $validator = new SchemaValidator($this->entityManager); |
||
46 | $errors = $validator->validateMapping(); |
||
47 | |||
48 | self::assertEmpty($errors, 'doctrine annotations should be valid'); |
||
49 | } |
||
50 | |||
51 | public function testGraphQLSchemaFromDocumentationMustBeValid(): void |
||
52 | { |
||
53 | $schema = new Schema([ |
||
54 | 'query' => new ObjectType([ |
||
55 | 'name' => 'query', |
||
56 | 'fields' => [ |
||
57 | 'users' => [ |
||
58 | 'type' => Type::listOf($this->types->get(User::class)), // Use automated ObjectType for output |
||
59 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
60 | // call to repository... |
||
61 | }, |
||
62 | ], |
||
63 | 'posts' => [ |
||
64 | 'type' => Type::listOf($this->types->get(Post::class)), // Use automated ObjectType for output |
||
65 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
66 | // call to repository... |
||
67 | }, |
||
68 | ], |
||
69 | ], |
||
70 | ]), |
||
71 | 'mutation' => new ObjectType([ |
||
72 | 'name' => 'mutation', |
||
73 | 'fields' => [ |
||
74 | 'createUser' => [ |
||
75 | 'type' => Type::nonNull($this->types->get(User::class)), |
||
76 | 'args' => [ |
||
77 | 'input' => Type::nonNull($this->types->getInput(User::class)), // Use automated InputObjectType for input |
||
78 | ], |
||
79 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
80 | // create new user and flush... |
||
81 | }, |
||
82 | ], |
||
83 | 'updateUser' => [ |
||
84 | 'type' => Type::nonNull($this->types->get(User::class)), |
||
85 | 'args' => [ |
||
86 | 'id' => Type::nonNull(Type::id()), // Use standard API when needed |
||
87 | 'input' => $this->types->getInput(User::class), |
||
88 | ], |
||
89 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
90 | // update existing user and flush... |
||
91 | }, |
||
92 | ], |
||
93 | 'createPost' => [ |
||
94 | 'type' => Type::nonNull($this->types->get(Post::class)), |
||
95 | 'args' => [ |
||
96 | 'input' => Type::nonNull($this->types->getInput(Post::class)), // Use automated InputObjectType for input |
||
97 | ], |
||
98 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
99 | // create new post and flush... |
||
100 | }, |
||
101 | ], |
||
102 | 'updatePost' => [ |
||
103 | 'type' => Type::nonNull($this->types->get(Post::class)), |
||
104 | 'args' => [ |
||
105 | 'id' => Type::nonNull(Type::id()), // Use standard API when needed |
||
106 | 'input' => $this->types->getInput(Post::class), |
||
107 | ], |
||
108 | 'resolve' => function ($root, $args): void { |
||
2 ignored issues
–
show
|
|||
109 | // update existing post and flush... |
||
110 | }, |
||
111 | ], |
||
112 | ], |
||
113 | ]), |
||
114 | ]); |
||
115 | |||
116 | $schema->assertValid(); |
||
117 | self::assertTrue(true, 'passed validation successfully'); |
||
118 | } |
||
119 | |||
120 | public function testCanGetUserDefinedScalarTypes(): void |
||
121 | { |
||
122 | $bool = $this->types->get(BooleanType::class); |
||
123 | $status = $this->types->get(PostStatusType::class); |
||
124 | |||
125 | self::assertInstanceOf(BooleanType::class, $bool, 'must be a instance of bool'); |
||
126 | self::assertInstanceOf(PostStatusType::class, $status, 'must be an instance of post status'); |
||
127 | |||
128 | self::assertSame($bool, $this->types->get(BooleanType::class), 'must returns the same instance of bool'); |
||
129 | self::assertSame($status, $this->types->get(PostStatusType::class), 'must returns the same instance of post status'); |
||
130 | } |
||
131 | |||
132 | public function testCanGetUserMappedTypes(): void |
||
133 | { |
||
134 | $type = $this->types->get(stdClass::class); |
||
135 | |||
136 | self::assertInstanceOf(CustomType::class, $type, 'must be a instance of CustomType'); |
||
137 | self::assertSame($type, $this->types->get('customName')); |
||
138 | } |
||
139 | |||
140 | public function testCanGetTypesWithBackslashPrefix(): void |
||
141 | { |
||
142 | $type = $this->types->get(stdClass::class); |
||
143 | self::assertSame($type, $this->types->get('\stdClass')); |
||
144 | } |
||
145 | |||
146 | public function testCanGetOutputTypes(): void |
||
147 | { |
||
148 | $userType = $this->types->get(User::class); |
||
149 | |||
150 | $this->assertObjectType('data/UserOutput.php', $userType); |
||
151 | self::assertSame($userType, $this->types->get(User::class), 'must returns the same instance of user type'); |
||
152 | |||
153 | $postType = $this->types->get(Post::class); |
||
154 | $this->assertObjectType('data/PostOutput.php', $postType); |
||
155 | self::assertSame($postType, $this->types->get(Post::class), 'must returns the same instance of post type'); |
||
156 | } |
||
157 | |||
158 | public function testCanGetInputTypes(): void |
||
159 | { |
||
160 | $userType = $this->types->getInput(User::class); |
||
161 | $this->assertInputType('data/UserInput.php', $userType); |
||
162 | self::assertSame($userType, $this->types->getInput(User::class), 'must returns the same instance of user type'); |
||
163 | |||
164 | $postType = $this->types->getInput(Post::class); |
||
165 | $this->assertInputType('data/PostInput.php', $postType); |
||
166 | self::assertSame($postType, $this->types->getInput(Post::class), 'must returns the same instance of post type'); |
||
167 | } |
||
168 | |||
169 | private function assertType(string $expectedFile, Type $type, bool $assertArgs): void |
||
170 | { |
||
171 | $fields = []; |
||
172 | foreach ($type->getFields() as $field) { |
||
173 | $data = [ |
||
174 | 'name' => $field->name, |
||
175 | 'type' => $field->getType()->toString(), |
||
176 | 'description' => $field->description, |
||
177 | ]; |
||
178 | |||
179 | if ($assertArgs) { |
||
180 | $args = []; |
||
181 | foreach ($field->args as $arg) { |
||
182 | $args[] = [ |
||
183 | 'name' => $arg->name, |
||
184 | 'type' => $arg->getType()->toString(), |
||
185 | 'description' => $arg->description, |
||
186 | 'defaultValue' => $arg->defaultValue, |
||
187 | ]; |
||
188 | } |
||
189 | $data['args'] = $args; |
||
190 | } else { |
||
191 | $data['defaultValue'] = $field->defaultValue; |
||
192 | } |
||
193 | |||
194 | $fields[] = $data; |
||
195 | } |
||
196 | |||
197 | $actual = [ |
||
198 | 'name' => $type->name, |
||
199 | 'description' => $type->description, |
||
200 | 'fields' => $fields, |
||
201 | ]; |
||
202 | |||
203 | $expected = require $expectedFile; |
||
204 | self::assertEquals($expected, $actual, 'Should equals expectation from: ' . $expectedFile); |
||
205 | } |
||
206 | |||
207 | private function assertInputType(string $expectedFile, InputObjectType $type): void |
||
208 | { |
||
209 | $this->assertType($expectedFile, $type, false); |
||
210 | } |
||
211 | |||
212 | private function assertObjectType(string $expectedFile, ObjectType $type): void |
||
215 | } |
||
216 | |||
217 | public function testNonPublicGetterMustBeIgnored(): void |
||
218 | { |
||
219 | $actual = $this->types->get(Blog\Model\Special\IgnoredGetter::class); |
||
220 | $this->assertObjectType('data/IgnoredGetter.php', $actual); |
||
221 | } |
||
222 | |||
223 | public function testCanDeclareArrayOfEntity(): void |
||
224 | { |
||
225 | $actual = $this->types->get(Blog\Model\Special\ArrayOfEntity::class); |
||
226 | $this->assertObjectType('data/ArrayOfEntity.php', $actual); |
||
227 | } |
||
228 | |||
229 | public function testDefaultValuesInput(): void |
||
230 | { |
||
231 | $actual = $this->types->getInput(Blog\Model\Special\DefaultValue::class); |
||
232 | $this->assertInputType('data/DefaultValueInput.php', $actual); |
||
233 | } |
||
234 | |||
235 | public function testDefaultValuesPartialInput(): void |
||
236 | { |
||
237 | $actual = $this->types->getPartialInput(Blog\Model\Special\DefaultValue::class); |
||
238 | $this->assertInputType('data/DefaultValuePartialInput.php', $actual); |
||
239 | } |
||
240 | |||
241 | public function testDefaultValuesOutput(): void |
||
242 | { |
||
243 | $actual = $this->types->get(Blog\Model\Special\DefaultValue::class); |
||
244 | $this->assertObjectType('data/DefaultValue.php', $actual); |
||
245 | } |
||
246 | |||
247 | public function testFieldWithoutTypeMustThrow(): void |
||
248 | { |
||
249 | $this->expectExceptionMessage('Could not find type for method `GraphQLTests\Doctrine\Blog\Model\Special\NoType::getWithoutTypeHint()`. Either type hint the return value, or specify the type with `@API\Field` annotation.'); |
||
250 | $type = $this->types->get(Blog\Model\Special\NoType::class); |
||
251 | $type->getFields(); |
||
252 | } |
||
253 | |||
254 | public function testFieldReturningCollectionWithoutTypeMustThrow(): void |
||
255 | { |
||
256 | $this->expectExceptionMessage('The method `GraphQLTests\Doctrine\Blog\Model\Special\NoTypeCollection::getFoos()` is type hinted with a return type of `Doctrine\Common\Collections\Collection`, but the entity contained in that collection could not be automatically detected. Either fix the type hint, fix the doctrine mapping, or specify the type with `@API\Field` annotation.'); |
||
257 | $type = $this->types->get(Blog\Model\Special\NoTypeCollection::class); |
||
258 | $type->getFields(); |
||
259 | } |
||
260 | |||
261 | public function testCannotGetInvalidType(): void |
||
262 | { |
||
263 | $this->expectExceptionMessage('Given class name `DateTimeImmutable` is not a Doctrine entity. Either register a custom GraphQL type for `DateTimeImmutable` when instantiating `GraphQL\Doctrine\Types`, or change the usage of that class to something else.'); |
||
264 | $this->types->get(\DateTimeImmutable::class); |
||
265 | } |
||
266 | |||
267 | public function testArgumentWithoutTypeMustThrow(): void |
||
268 | { |
||
269 | $this->expectExceptionMessage('Could not find type for parameter `$bar` for method `GraphQLTests\Doctrine\Blog\Model\Special\NoTypeArgument::getFoo()`. Either type hint the parameter, or specify the type with `@API\Argument` annotation.'); |
||
270 | $type = $this->types->get(Blog\Model\Special\NoTypeArgument::class); |
||
271 | $type->getFields(); |
||
272 | } |
||
273 | |||
274 | public function testInputWithoutTypeMustThrow(): void |
||
275 | { |
||
276 | $this->expectExceptionMessage('Could not find type for parameter `$bar` for method `GraphQLTests\Doctrine\Blog\Model\Special\NoTypeInput::setFoo()`. Either type hint the parameter, or specify the type with `@API\Input` annotation.'); |
||
277 | $type = $this->types->getInput(Blog\Model\Special\NoTypeInput::class); |
||
278 | $type->getFields(); |
||
279 | } |
||
280 | |||
281 | public function testFieldWithExtraArgumentMustThrow(): void |
||
282 | { |
||
283 | $this->expectExceptionMessage('The following arguments were declared via `@API\Argument` annotation but do not match actual parameter names on method `GraphQLTests\Doctrine\Blog\Model\Special\ExtraArgument::getWithParams()`. Either rename or remove the annotations: misspelled_name'); |
||
284 | $type = $this->types->get(Blog\Model\Special\ExtraArgument::class); |
||
285 | $type->getFields(); |
||
286 | } |
||
287 | |||
288 | public function testFieldWithArrayArgumentMustThrow(): void |
||
289 | { |
||
290 | $this->expectExceptionMessage('The parameter `$arg1` on method `GraphQLTests\Doctrine\Blog\Model\Special\ArrayArgument::getWithParams()` is type hinted as `array` and is not overridden via `@API\Argument` annotation. Either change the type hint or specify the type with `@API\Argument` annotation.'); |
||
291 | $type = $this->types->get(Blog\Model\Special\ArrayArgument::class); |
||
292 | $type->getFields(); |
||
293 | } |
||
294 | |||
295 | public function testFieldWithObjectTypeArgumentMustThrow(): void |
||
296 | { |
||
297 | $this->expectExceptionMessage('Type for parameter `$user` for method `GraphQLTests\Doctrine\Blog\Model\Special\ObjectTypeArgument::getWithParams()` must be an instance of `GraphQL\Type\Definition\InputType`, but was `GraphQL\Type\Definition\ObjectType`. Use `@API\Argument` annotation to specify a custom InputType.'); |
||
298 | $type = $this->types->get(Blog\Model\Special\ObjectTypeArgument::class); |
||
299 | $type->getFields(); |
||
300 | } |
||
301 | |||
302 | public function testCanGetMappedTypesEitherByMappedPhpClassOrDirectTypeClass(): void |
||
303 | { |
||
304 | $viaPhp = $this->types->get(DateTime::class); |
||
305 | $viaType = $this->types->get(DateTimeType::class); |
||
306 | self::assertSame($viaPhp, $viaType); |
||
307 | } |
||
308 | |||
309 | public function testFeieldWithObjectTypeArgumentMustThrow(): void |
||
321 | } |
||
322 | } |
||
323 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.