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