TypesTest::provideLoadType()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 12
rs 9.9332
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GraphQLTests\Doctrine;
6
7
use DateTimeImmutable;
8
use Doctrine\ORM\Tools\SchemaValidator;
9
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
10
use GraphQL\Type\Definition\BooleanType;
11
use GraphQL\Type\Definition\ObjectType;
12
use GraphQL\Type\Definition\Type;
13
use GraphQL\Type\Schema;
14
use GraphQLTests\Doctrine\Blog\Model\Post;
15
use GraphQLTests\Doctrine\Blog\Types\CustomType;
16
use GraphQLTests\Doctrine\Blog\Types\DateTimeType;
17
use GraphQLTests\Doctrine\Blog\Types\PostStatusType;
18
use PHPUnit\Framework\Attributes\DataProvider;
19
use PHPUnit\Framework\TestCase;
20
use stdClass;
21
22
final class TypesTest extends TestCase
23
{
24
    use TypesTrait;
25
26
    public function testBlogMapping(): void
27
    {
28
        $validator = new SchemaValidator($this->entityManager);
29
        $errors = $validator->validateMapping();
30
31
        self::assertEmpty($errors, 'doctrine attributes should be valid');
32
    }
33
34
    public function testGraphQLSchemaFromDocumentationMustBeValid(): void
35
    {
36
        $types = $this->types;
37
        $schema = new Schema([
38
            'query' => new ObjectType([
39
                'name' => 'query',
40
                'fields' => [
41
                    'posts' => [
42
                        'type' => Type::listOf($types->getOutput(Post::class)), // Use automated ObjectType for output
43
                        'args' => [
44
                            [
45
                                'name' => 'filter',
46
                                'type' => $types->getFilter(Post::class), // Use automated filtering options
47
                            ],
48
                            [
49
                                'name' => 'sorting',
50
                                'type' => $types->getSorting(Post::class), // Use automated sorting options
51
                            ],
52
                        ],
53
                        'resolve' => function ($root, $args) use ($types): void {
54
                            $queryBuilder = $types->createFilteredQueryBuilder(Post::class, $args['filter'] ?? [], $args['sorting'] ?? []);
0 ignored issues
show
Unused Code introduced by
The assignment to $queryBuilder is dead and can be removed.
Loading history...
55
56
                            // execute query...
57
                        },
58
                    ],
59
                ],
60
            ]),
61
            'mutation' => new ObjectType([
62
                'name' => 'mutation',
63
                'fields' => [
64
                    'createPost' => [
65
                        'type' => Type::nonNull($types->getOutput(Post::class)),
66
                        'args' => [
67
                            'input' => Type::nonNull($types->getInput(Post::class)), // Use automated InputObjectType for input
68
                        ],
69
                        'resolve' => function ($root, $args): void {
70
                            // create new post and flush...
71
                        },
72
                    ],
73
                    'updatePost' => [
74
                        'type' => Type::nonNull($types->getOutput(Post::class)),
75
                        'args' => [
76
                            'id' => Type::nonNull(Type::id()), // Use standard API when needed
77
                            'input' => $types->getPartialInput(Post::class),  // Use automated InputObjectType for partial input for updates
78
                        ],
79
                        'resolve' => function ($root, $args): void {
80
                            // update existing post and flush...
81
                        },
82
                    ],
83
                ],
84
            ]),
85
        ]);
86
87
        $schema->assertValid();
88
        self::assertTrue(true, 'passed validation successfully');
89
    }
90
91
    public function testCanGetUserDefinedScalarTypes(): void
92
    {
93
        $bool = $this->types->get(BooleanType::class);
94
        $status = $this->types->get(PostStatusType::class);
95
96
        self::assertInstanceOf(BooleanType::class, $bool, 'must be a instance of bool');
97
        self::assertInstanceOf(PostStatusType::class, $status, 'must be an instance of post status');
98
99
        self::assertSame($bool, $this->types->get(BooleanType::class), 'must returns the same instance of bool');
100
        self::assertSame($status, $this->types->get(PostStatusType::class), 'must returns the same instance of post status');
101
    }
102
103
    public function testCanGetUserMappedTypes(): void
104
    {
105
        $type = $this->types->get(stdClass::class);
106
107
        self::assertInstanceOf(CustomType::class, $type, 'must be a instance of CustomType');
108
        self::assertSame($type, $this->types->get('customName'));
109
    }
110
111
    public function testCanGetMappedTypesEitherByMappedPhpClassOrDirectTypeClass(): void
112
    {
113
        $viaPhp = $this->types->get(DateTimeImmutable::class);
114
        $viaType = $this->types->get(DateTimeType::class);
115
        self::assertSame($viaPhp, $viaType);
116
    }
117
118
    public function testDoctrineWithMappingDriverChainUsingDefault(): void
119
    {
120
        // Replace attribute driver with a driver chain
121
        $config = $this->entityManager->getConfiguration();
122
        $chain = new MappingDriverChain();
123
        $chain->setDefaultDriver($config->getMetadataDriverImpl());
0 ignored issues
show
Bug introduced by
It seems like $config->getMetadataDriverImpl() can also be of type null; however, parameter $driver of Doctrine\Persistence\Map...ain::setDefaultDriver() does only seem to accept Doctrine\Persistence\Mapping\Driver\MappingDriver, maybe add an additional type check? ( Ignorable by Annotation )

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

123
        $chain->setDefaultDriver(/** @scrutinizer ignore-type */ $config->getMetadataDriverImpl());
Loading history...
124
        $config->setMetadataDriverImpl($chain);
125
126
        $type = $this->types->getOutput(Post::class);
127
        self::assertNotEmpty($type->getFields());
128
    }
129
130
    public function testDoctrineWithMappingDriverChainUsingNamespace(): void
131
    {
132
        // Replace attribute driver with a driver chain
133
        $config = $this->entityManager->getConfiguration();
134
        $chain = new MappingDriverChain();
135
        $driver = $config->getMetadataDriverImpl();
136
        if ($driver === null) {
137
            self::fail('driver missing');
138
        } else {
139
            $chain->addDriver($driver, 'GraphQLTests\Doctrine\Blog\Model');
140
            $config->setMetadataDriverImpl($chain);
141
            $type = $this->types->getOutput(Post::class);
142
            self::assertNotEmpty($type->getFields());
143
        }
144
    }
145
146
    public function testNonRegisteredCustomTypeMustThrow(): void
147
    {
148
        $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`');
149
        $this->types->get('foo');
150
    }
151
152
    public function testHas(): void
153
    {
154
        self::assertTrue($this->types->has(stdClass::class), 'should have custom registered key');
155
        self::assertFalse($this->types->has('non-existing'), 'should not have non-existing things');
156
157
        $this->types->get(stdClass::class);
158
        self::assertTrue($this->types->has('customName'), 'should have custom registered type by its name, even if custom key was different, once type is created');
159
    }
160
161
    #[DataProvider('provideLoadType')]
162
    public function testLoadType(string $typeName): void
163
    {
164
        $type = $this->types->loadType($typeName, 'GraphQLTests\Doctrine\Blog\Model');
165
        self::assertNotNull($type, 'should be able to lazy load a generated type by its name only');
166
        self::assertSame($typeName, $type->name(), 'loaded type must have same name');
0 ignored issues
show
Bug introduced by
The method name() does not exist on GraphQL\Type\Definition\Type. It seems like you code against a sub-type of said class. However, the method does not exist in GraphQL\Type\Definition\ListOfType or GraphQL\Type\Definition\NonNull. Are you sure you never get one of those? ( Ignorable by Annotation )

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

166
        self::assertSame($typeName, $type->/** @scrutinizer ignore-call */ name(), 'loaded type must have same name');
Loading history...
167
    }
168
169
    public static function provideLoadType(): iterable
170
    {
171
        yield 'PostInput' => ['PostInput'];
172
        yield 'PostPartialInput' => ['PostPartialInput'];
173
        yield 'Post' => ['Post'];
174
        yield 'PostID' => ['PostID'];
175
        yield 'PostFilter' => ['PostFilter'];
176
        yield 'PostFilterGroupJoin' => ['PostFilterGroupJoin'];
177
        yield 'PostSorting' => ['PostSorting'];
178
        yield 'PostStatus' => ['PostStatus'];
179
        yield 'PostFilterGroupCondition' => ['PostFilterGroupCondition'];
180
        yield 'JoinOnPost' => ['JoinOnPost'];
181
    }
182
183
    public function testLoadUnknownType(): void
184
    {
185
        $type = $this->types->loadType('unknown-type-name', 'GraphQLTests\Doctrine\Blog\Model');
186
        self::assertNull($type, 'should return null if type is not found to be chainable');
187
    }
188
189
    public function testLoadTypeInUnknownNamespace(): void
190
    {
191
        $type = $this->types->loadType('Post', 'Unknown\Model');
192
        self::assertNull($type, 'should return null if namespace is not found to be chainable');
193
    }
194
}
195