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