• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 16414261225

21 Jul 2025 10:14AM UTC coverage: 21.806% (-0.2%) from 22.039%
16414261225

Pull #7307

github

web-flow
Merge 92df6bf50 into d3b4b7b40
Pull Request #7307: Use class-string param type for Metadata::getClass

0 of 191 new or added lines in 19 files covered. (0.0%)

44 existing lines in 2 files now uncovered.

11408 of 52317 relevant lines covered (21.81%)

11.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/GraphQl/Tests/Type/FieldsBuilderTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\GraphQl\Tests\Type;
15

16
use ApiPlatform\GraphQl\Resolver\Factory\ResolverFactoryInterface;
17
use ApiPlatform\GraphQl\Tests\Fixtures\Enum\GenderTypeEnum;
18
use ApiPlatform\GraphQl\Tests\Fixtures\Serializer\NameConverter\CustomConverter;
19
use ApiPlatform\GraphQl\Type\ContextAwareTypeBuilderInterface;
20
use ApiPlatform\GraphQl\Type\FieldsBuilder;
21
use ApiPlatform\GraphQl\Type\TypeConverterInterface;
22
use ApiPlatform\GraphQl\Type\TypesContainerInterface;
23
use ApiPlatform\Metadata\ApiProperty;
24
use ApiPlatform\Metadata\ApiResource;
25
use ApiPlatform\Metadata\FilterInterface;
26
use ApiPlatform\Metadata\GraphQl\Mutation;
27
use ApiPlatform\Metadata\GraphQl\Operation;
28
use ApiPlatform\Metadata\GraphQl\Query;
29
use ApiPlatform\Metadata\GraphQl\QueryCollection;
30
use ApiPlatform\Metadata\GraphQl\Subscription;
31
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
32
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
33
use ApiPlatform\Metadata\Property\PropertyNameCollection;
34
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
35
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
36
use ApiPlatform\Metadata\ResourceClassResolverInterface;
37
use ApiPlatform\State\Pagination\Pagination;
38
use GraphQL\Type\Definition\InputObjectType;
39
use GraphQL\Type\Definition\InterfaceType;
40
use GraphQL\Type\Definition\ListOfType;
41
use GraphQL\Type\Definition\NonNull;
42
use GraphQL\Type\Definition\ObjectType;
43
use GraphQL\Type\Definition\Type as GraphQLType;
44
use PHPUnit\Framework\TestCase;
45
use Prophecy\Argument;
46
use Prophecy\PhpUnit\ProphecyTrait;
47
use Prophecy\Prophecy\ObjectProphecy;
48
use Psr\Container\ContainerInterface;
49
use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
50
use Symfony\Component\TypeInfo\Type;
51

52
/**
53
 * @author Alan Poulain <contact@alanpoulain.eu>
54
 */
55
class FieldsBuilderTest extends TestCase
56
{
57
    use ProphecyTrait;
58

59
    private ObjectProphecy $propertyNameCollectionFactoryProphecy;
60
    private ObjectProphecy $propertyMetadataFactoryProphecy;
61
    private ObjectProphecy $resourceMetadataCollectionFactoryProphecy;
62
    private ObjectProphecy $typesContainerProphecy;
63
    private ObjectProphecy $typeBuilderProphecy;
64
    private ObjectProphecy $typeConverterProphecy;
65
    private ObjectProphecy $itemResolverFactoryProphecy;
66
    private ObjectProphecy $filterLocatorProphecy;
67
    private ObjectProphecy $resourceClassResolverProphecy;
68
    private FieldsBuilder $fieldsBuilder;
69

70
    /**
71
     * {@inheritdoc}
72
     */
73
    protected function setUp(): void
74
    {
75
        $this->propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
76
        $this->propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
77
        $this->resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
×
78
        $this->typesContainerProphecy = $this->prophesize(TypesContainerInterface::class);
×
79
        $this->typeBuilderProphecy = $this->prophesize(ContextAwareTypeBuilderInterface::class);
×
80
        $this->typeConverterProphecy = $this->prophesize(TypeConverterInterface::class);
×
81
        $this->itemResolverFactoryProphecy = $this->prophesize(ResolverFactoryInterface::class);
×
82
        $this->filterLocatorProphecy = $this->prophesize(ContainerInterface::class);
×
83
        $this->resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
84
        $this->fieldsBuilder = $this->buildFieldsBuilder();
×
85
    }
86

87
    private function buildFieldsBuilder(?AdvancedNameConverterInterface $advancedNameConverter = null): FieldsBuilder
88
    {
89
        return new FieldsBuilder($this->propertyNameCollectionFactoryProphecy->reveal(), $this->propertyMetadataFactoryProphecy->reveal(), $this->resourceMetadataCollectionFactoryProphecy->reveal(), $this->resourceClassResolverProphecy->reveal(), $this->typesContainerProphecy->reveal(), $this->typeBuilderProphecy->reveal(), $this->typeConverterProphecy->reveal(), $this->itemResolverFactoryProphecy->reveal(), $this->filterLocatorProphecy->reveal(), new Pagination(), $advancedNameConverter ?? new CustomConverter(), '__');
×
90
    }
91

92
    public function testGetNodeQueryFields(): void
93
    {
94
        $nodeInterfaceType = $this->prophesize(InterfaceType::class)->reveal();
×
95
        $this->typeBuilderProphecy->getNodeInterface()->shouldBeCalled()->willReturn($nodeInterfaceType);
×
96

97
        $itemResolver = function (): void {
×
98
        };
×
99
        $this->itemResolverFactoryProphecy->__invoke()->shouldBeCalled()->willReturn($itemResolver);
×
100

101
        $nodeQueryFields = $this->fieldsBuilder->getNodeQueryFields();
×
102
        $this->assertArrayHasKey('type', $nodeQueryFields);
×
103
        $this->assertArrayHasKey('args', $nodeQueryFields);
×
104
        $this->assertArrayHasKey('resolve', $nodeQueryFields);
×
105

106
        $this->assertSame($nodeInterfaceType, $nodeQueryFields['type']);
×
107
        $this->assertArrayHasKey('id', $nodeQueryFields['args']);
×
108
        $this->assertArrayHasKey('type', $nodeQueryFields['args']['id']);
×
109
        $this->assertInstanceOf(NonNull::class, $nodeQueryFields['args']['id']['type']);
×
110
        /** @var NonNull $idType */
111
        $idType = $nodeQueryFields['args']['id']['type'];
×
112
        $this->assertSame(GraphQLType::id(), $idType->getWrappedType());
×
113
        $this->assertSame($itemResolver, $nodeQueryFields['resolve']);
×
114
    }
115

116
    #[\PHPUnit\Framework\Attributes\DataProvider('itemQueryFieldsProvider')]
117
    public function testGetItemQueryFields(string $resourceClass, Operation $operation, array $configuration, ?GraphQLType $graphqlType, ?callable $resolver, array $expectedQueryFields): void
118
    {
119
        $this->resourceClassResolverProphecy->isResourceClass($resourceClass)->willReturn(true);
×
120
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
×
121
        $this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string());
×
122
        $this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($resolver);
×
123

124
        $queryFields = $this->fieldsBuilder->getItemQueryFields($resourceClass, $operation, $configuration);
×
125

126
        $this->assertEquals($expectedQueryFields, $queryFields);
×
127
    }
128

129
    public static function itemQueryFieldsProvider(): array
130
    {
131
        return [
×
NEW
132
            'no resource field configuration' => [\stdClass::class, (new Query())->withClass(\stdClass::class)->withName('action'), [], null, null, []],
×
NEW
133
            'nested item query' => [\stdClass::class, (new Query())->withNested(true)->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), [], new ObjectType(['name' => 'item', 'fields' => []]), function (): void {
×
134
            }, []],
×
NEW
135
            'nominal standard type case with deprecation reason and description' => [\stdClass::class, (new Query())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withDeprecationReason('not useful')->withDescription('Custom description.'), [], GraphQLType::string(), null,
×
136
                [
×
137
                    'actionShortName' => [
×
138
                        'type' => GraphQLType::string(),
×
139
                        'description' => 'Custom description.',
×
140
                        'args' => [
×
141
                            'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())],
×
142
                        ],
×
143
                        'resolve' => null,
×
144
                        'deprecationReason' => 'not useful',
×
145
                    ],
×
146
                ],
×
147
            ],
×
NEW
148
            'nominal item case' => [\stdClass::class, (new Query())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), [], $graphqlType = new ObjectType(['name' => 'item', 'fields' => []]), $resolver = function (): void {
×
149
            },
×
150
                [
×
151
                    'actionShortName' => [
×
152
                        'type' => $graphqlType,
×
153
                        'description' => null,
×
154
                        'args' => [
×
155
                            'id' => ['type' => GraphQLType::nonNull(GraphQLType::id())],
×
156
                        ],
×
157
                        'resolve' => $resolver,
×
158
                        'deprecationReason' => null,
×
159
                    ],
×
160
                ],
×
161
            ],
×
162
            'empty overridden args and add fields' => [
×
NEW
163
                \stdClass::class, (new Query())->withClass(\stdClass::class)->withShortName('ShortName'), ['args' => [], 'name' => 'customActionName'], GraphQLType::string(), null,
×
164
                [
×
165
                    'shortName' => [
×
166
                        'type' => GraphQLType::string(),
×
167
                        'description' => null,
×
168
                        'args' => [],
×
169
                        'resolve' => null,
×
170
                        'deprecationReason' => null,
×
171
                        'name' => 'customActionName',
×
172
                    ],
×
173
                ],
×
174
            ],
×
175
            'override args with custom ones' => [
×
NEW
176
                \stdClass::class, (new Query())->withClass(\stdClass::class)->withShortName('ShortName'), ['args' => ['customArg' => ['type' => 'a type']]], GraphQLType::string(), null,
×
177
                [
×
178
                    'shortName' => [
×
179
                        'type' => GraphQLType::string(),
×
180
                        'description' => null,
×
181
                        'args' => [
×
182
                            'customArg' => [
×
183
                                'type' => GraphQLType::string(),
×
184
                            ],
×
185
                        ],
×
186
                        'resolve' => null,
×
187
                        'deprecationReason' => null,
×
188
                    ],
×
189
                ],
×
190
            ],
×
191
        ];
×
192
    }
193

194
    #[\PHPUnit\Framework\Attributes\DataProvider('collectionQueryFieldsProvider')]
195
    public function testGetCollectionQueryFields(string $resourceClass, Operation $operation, array $configuration, ?GraphQLType $graphqlType, ?callable $resolver, array $expectedQueryFields): void
196
    {
197
        $this->resourceClassResolverProphecy->isResourceClass($resourceClass)->willReturn(true);
×
198
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
×
199
        $this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string());
×
200
        $this->typeBuilderProphecy->getPaginatedCollectionType($graphqlType, $operation)->willReturn($graphqlType);
×
201
        $this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($resolver);
×
202
        $this->filterLocatorProphecy->has('my_filter')->willReturn(true);
×
203
        $filterProphecy = $this->prophesize(FilterInterface::class);
×
204
        $filterProphecy->getDescription($resourceClass)->willReturn([
×
205
            'boolField' => ['type' => 'bool', 'required' => true],
×
206
            'boolField[]' => ['type' => 'bool', 'required' => false],
×
207
            'parent.child[related.nested]' => ['type' => 'bool', 'required' => false],
×
208
            'dateField[before]' => ['type' => \DateTimeInterface::class, 'required' => false],
×
209
        ]);
×
210
        $this->filterLocatorProphecy->get('my_filter')->willReturn($filterProphecy->reveal());
×
211
        $this->typesContainerProphecy->has('ShortNameFilter_dateField')->willReturn(false);
×
212
        $this->typesContainerProphecy->has('ShortNameFilter_parent__child')->willReturn(false);
×
213
        $this->typesContainerProphecy->set('ShortNameFilter_dateField', Argument::type(ListOfType::class));
×
214
        $this->typesContainerProphecy->set('ShortNameFilter_parent__child', Argument::type(ListOfType::class));
×
215

216
        $queryFields = $this->fieldsBuilder->getCollectionQueryFields($resourceClass, $operation, $configuration);
×
217

218
        $this->assertEquals($expectedQueryFields, $queryFields);
×
219
    }
220

221
    public static function collectionQueryFieldsProvider(): array
222
    {
223
        return [
×
NEW
224
            'no resource field configuration' => [\stdClass::class, (new QueryCollection())->withClass(\stdClass::class)->withName('action'), [], null, null, []],
×
NEW
225
            'nested collection query' => [\stdClass::class, (new QueryCollection())->withNested(true)->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), [], GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), function (): void {
×
226
            }, []],
×
NEW
227
            'nominal collection case with deprecation reason and description' => [\stdClass::class, (new QueryCollection())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withDeprecationReason('not useful')->withDescription('Custom description.'), [], $graphqlType = GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), $resolver = function (): void {
×
228
            },
×
229
                [
×
230
                    'actionShortNames' => [
×
231
                        'type' => $graphqlType,
×
232
                        'description' => 'Custom description.',
×
233
                        'args' => [
×
234
                            'first' => [
×
235
                                'type' => GraphQLType::int(),
×
236
                                'description' => 'Returns the first n elements from the list.',
×
237
                            ],
×
238
                            'last' => [
×
239
                                'type' => GraphQLType::int(),
×
240
                                'description' => 'Returns the last n elements from the list.',
×
241
                            ],
×
242
                            'before' => [
×
243
                                'type' => GraphQLType::string(),
×
244
                                'description' => 'Returns the elements in the list that come before the specified cursor.',
×
245
                            ],
×
246
                            'after' => [
×
247
                                'type' => GraphQLType::string(),
×
248
                                'description' => 'Returns the elements in the list that come after the specified cursor.',
×
249
                            ],
×
250
                        ],
×
251
                        'resolve' => $resolver,
×
252
                        'deprecationReason' => 'not useful',
×
253
                    ],
×
254
                ],
×
255
            ],
×
NEW
256
            'collection with filters' => [\stdClass::class, (new QueryCollection())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withFilters(['my_filter']), [], $graphqlType = GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), $resolver = function (): void {
×
257
            },
×
258
                [
×
259
                    'actionShortNames' => [
×
260
                        'type' => $graphqlType,
×
261
                        'description' => null,
×
262
                        'args' => [
×
263
                            'first' => [
×
264
                                'type' => GraphQLType::int(),
×
265
                                'description' => 'Returns the first n elements from the list.',
×
266
                            ],
×
267
                            'last' => [
×
268
                                'type' => GraphQLType::int(),
×
269
                                'description' => 'Returns the last n elements from the list.',
×
270
                            ],
×
271
                            'before' => [
×
272
                                'type' => GraphQLType::string(),
×
273
                                'description' => 'Returns the elements in the list that come before the specified cursor.',
×
274
                            ],
×
275
                            'after' => [
×
276
                                'type' => GraphQLType::string(),
×
277
                                'description' => 'Returns the elements in the list that come after the specified cursor.',
×
278
                            ],
×
279
                            'boolField' => GraphQLType::nonNull($graphqlType),
×
280
                            'boolField_list' => GraphQLType::listOf($graphqlType),
×
281
                            'parent__child' => GraphQLType::listOf(new InputObjectType(['name' => 'ShortNameFilter_parent__child', 'fields' => ['related__nested' => $graphqlType]])),
×
282
                            'dateField' => GraphQLType::listOf(new InputObjectType(['name' => 'ShortNameFilter_dateField', 'fields' => ['before' => $graphqlType]])),
×
283
                        ],
×
284
                        'resolve' => $resolver,
×
285
                        'deprecationReason' => null,
×
286
                    ],
×
287
                ],
×
288
            ],
×
289
            'collection empty overridden args and add fields' => [
×
NEW
290
                \stdClass::class, (new QueryCollection())->withArgs([])->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), ['args' => [], 'name' => 'customActionName'], $graphqlType = GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), $resolver = function (): void {
×
291
                },
×
292
                [
×
293
                    'actionShortNames' => [
×
294
                        'type' => $graphqlType,
×
295
                        'description' => null,
×
296
                        'args' => [],
×
297
                        'resolve' => $resolver,
×
298
                        'deprecationReason' => null,
×
299
                        'name' => 'customActionName',
×
300
                    ],
×
301
                ],
×
302
            ],
×
303
            'collection override args with custom ones' => [
×
NEW
304
                \stdClass::class, (new QueryCollection())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), ['args' => ['customArg' => ['type' => 'a type']]], $graphqlType = GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), $resolver = function (): void {
×
305
                },
×
306
                [
×
307
                    'actionShortNames' => [
×
308
                        'type' => $graphqlType,
×
309
                        'description' => null,
×
310
                        'args' => [
×
311
                            'customArg' => [
×
312
                                'type' => GraphQLType::string(),
×
313
                            ],
×
314
                        ],
×
315
                        'resolve' => $resolver,
×
316
                        'deprecationReason' => null,
×
317
                    ],
×
318
                ],
×
319
            ],
×
NEW
320
            'collection with page-based pagination enabled' => [\stdClass::class, (new QueryCollection())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withPaginationType('page')->withFilters(['my_filter']), [], $graphqlType = GraphQLType::listOf(new ObjectType(['name' => 'collection', 'fields' => []])), $resolver = function (): void {
×
321
            },
×
322
                [
×
323
                    'actionShortNames' => [
×
324
                        'type' => $graphqlType,
×
325
                        'description' => null,
×
326
                        'args' => [
×
327
                            'page' => [
×
328
                                'type' => GraphQLType::int(),
×
329
                                'description' => 'Returns the current page.',
×
330
                            ],
×
331
                            'boolField' => GraphQLType::nonNull($graphqlType),
×
332
                            'boolField_list' => GraphQLType::listOf($graphqlType),
×
333
                            'parent__child' => GraphQLType::listOf(new InputObjectType(['name' => 'ShortNameFilter_parent__child', 'fields' => ['related__nested' => $graphqlType]])),
×
334
                            'dateField' => GraphQLType::listOf(new InputObjectType(['name' => 'ShortNameFilter_dateField', 'fields' => ['before' => $graphqlType]])),
×
335
                        ],
×
336
                        'resolve' => $resolver,
×
337
                        'deprecationReason' => null,
×
338
                    ],
×
339
                ],
×
340
            ],
×
341
        ];
×
342
    }
343

344
    #[\PHPUnit\Framework\Attributes\DataProvider('mutationFieldsProvider')]
345
    public function testGetMutationFields(string $resourceClass, Operation $operation, GraphQLType $graphqlType, GraphQLType $inputGraphqlType, ?callable $mutationResolver, array $expectedMutationFields): void
346
    {
347
        $this->resourceClassResolverProphecy->isResourceClass($resourceClass)->willReturn(true);
×
348
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
×
349
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), true, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($inputGraphqlType);
×
350
        $this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($mutationResolver);
×
351

352
        $mutationFields = $this->fieldsBuilder->getMutationFields($resourceClass, $operation);
×
353

354
        $this->assertSame($expectedMutationFields, $mutationFields);
×
355
    }
356

357
    public static function mutationFieldsProvider(): array
358
    {
359
        return [
×
NEW
360
            'nominal case with deprecation reason' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withDeprecationReason('not useful'), $graphqlType = new ObjectType(['name' => 'mutation', 'fields' => []]), $inputGraphqlType = new ObjectType(['name' => 'input', 'fields' => []]), $mutationResolver = function (): void {
×
361
            },
×
362
                [
×
363
                    'actionShortName' => [
×
364
                        'type' => $graphqlType,
×
365
                        'description' => 'Actions a ShortName.',
×
366
                        'args' => [
×
367
                            'input' => [
×
368
                                'type' => $inputGraphqlType,
×
369
                                'description' => null,
×
370
                                'args' => [],
×
371
                                'resolve' => null,
×
372
                                'deprecationReason' => 'not useful',
×
373
                            ],
×
374
                        ],
×
375
                        'resolve' => $mutationResolver,
×
376
                        'deprecationReason' => 'not useful',
×
377
                    ],
×
378
                ],
×
379
            ],
×
NEW
380
            'custom description' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withDescription('Custom description.'), $graphqlType = new ObjectType(['name' => 'mutation', 'fields' => []]), $inputGraphqlType = new ObjectType(['name' => 'input', 'fields' => []]), $mutationResolver = function (): void {
×
381
            },
×
382
                [
×
383
                    'actionShortName' => [
×
384
                        'type' => $graphqlType,
×
385
                        'description' => 'Custom description.',
×
386
                        'args' => [
×
387
                            'input' => [
×
388
                                'type' => $inputGraphqlType,
×
389
                                'description' => null,
×
390
                                'args' => [],
×
391
                                'resolve' => null,
×
392
                                'deprecationReason' => null,
×
393
                            ],
×
394
                        ],
×
395
                        'resolve' => $mutationResolver,
×
396
                        'deprecationReason' => null,
×
397
                    ],
×
398
                ],
×
399
            ],
×
400
        ];
×
401
    }
402

403
    #[\PHPUnit\Framework\Attributes\DataProvider('subscriptionFieldsProvider')]
404
    public function testGetSubscriptionFields(string $resourceClass, Operation $operation, GraphQLType $graphqlType, GraphQLType $inputGraphqlType, ?callable $subscriptionResolver, array $expectedSubscriptionFields): void
405
    {
406
        $this->resourceClassResolverProphecy->isResourceClass($resourceClass)->willReturn(true);
×
407
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), false, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($graphqlType);
×
408
        $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), true, Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), $resourceClass, $resourceClass, null, 0)->willReturn($inputGraphqlType);
×
409
        $this->resourceMetadataCollectionFactoryProphecy->create($resourceClass)->willReturn(new ResourceMetadataCollection($resourceClass, [(new ApiResource())->withGraphQlOperations([$operation->getName() => $operation])]));
×
410
        $this->itemResolverFactoryProphecy->__invoke($resourceClass, $resourceClass, $operation, Argument::any())->willReturn($subscriptionResolver);
×
411

412
        $subscriptionFields = $this->fieldsBuilder->getSubscriptionFields($resourceClass, $operation);
×
413

414
        $this->assertSame($expectedSubscriptionFields, $subscriptionFields);
×
415
    }
416

417
    public static function subscriptionFieldsProvider(): array
418
    {
419
        return [
×
NEW
420
            'mercure not enabled' => [\stdClass::class, (new Subscription())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName'), new ObjectType(['name' => 'subscription', 'fields' => []]), new ObjectType(['name' => 'input', 'fields' => []]), null, [],
×
421
            ],
×
NEW
422
            'nominal case with deprecation reason' => [\stdClass::class, (new Subscription())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withMercure(true)->withDeprecationReason('not useful'), $graphqlType = new ObjectType(['name' => 'subscription', 'fields' => []]), $inputGraphqlType = new ObjectType(['name' => 'input', 'fields' => []]), $subscriptionResolver = function (): void {
×
423
            },
×
424
                [
×
425
                    'actionShortNameSubscribe' => [
×
426
                        'type' => $graphqlType,
×
427
                        'description' => 'Subscribes to the action event of a ShortName.',
×
428
                        'args' => [
×
429
                            'input' => [
×
430
                                'type' => $inputGraphqlType,
×
431
                                'description' => null,
×
432
                                'args' => [],
×
433
                                'resolve' => null,
×
434
                                'deprecationReason' => 'not useful',
×
435
                            ],
×
436
                        ],
×
437
                        'resolve' => $subscriptionResolver,
×
438
                        'deprecationReason' => 'not useful',
×
439
                    ],
×
440
                ],
×
441
            ],
×
NEW
442
            'custom description' => [\stdClass::class, (new Subscription())->withClass(\stdClass::class)->withName('action')->withShortName('ShortName')->withMercure(true)->withDescription('Custom description.'), $graphqlType = new ObjectType(['name' => 'subscription', 'fields' => []]), $inputGraphqlType = new ObjectType(['name' => 'input', 'fields' => []]), $subscriptionResolver = function (): void {
×
443
            },
×
444
                [
×
445
                    'actionShortNameSubscribe' => [
×
446
                        'type' => $graphqlType,
×
447
                        'description' => 'Custom description.',
×
448
                        'args' => [
×
449
                            'input' => [
×
450
                                'type' => $inputGraphqlType,
×
451
                                'description' => null,
×
452
                                'args' => [],
×
453
                                'resolve' => null,
×
454
                                'deprecationReason' => null,
×
455
                            ],
×
456
                        ],
×
457
                        'resolve' => $subscriptionResolver,
×
458
                        'deprecationReason' => null,
×
459
                    ],
×
460
                ],
×
461
            ],
×
462
        ];
×
463
    }
464

465
    #[\PHPUnit\Framework\Attributes\DataProvider('resourceObjectTypeFieldsProvider')]
466
    public function testGetResourceObjectTypeFields(string $resourceClass, Operation $operation, array $properties, bool $input, int $depth, ?array $ioMetadata, array $expectedResourceObjectTypeFields, ?callable $advancedNameConverterFactory = null): void
467
    {
468
        $this->resourceClassResolverProphecy->isResourceClass($resourceClass)->willReturn(true);
×
469
        $this->resourceClassResolverProphecy->isResourceClass('nestedResourceClass')->willReturn(true);
×
470
        $this->resourceClassResolverProphecy->isResourceClass('nestedResourceNoQueryClass')->willReturn(true);
×
471
        $this->resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(false);
×
472
        $this->propertyNameCollectionFactoryProphecy->create($resourceClass)->willReturn(new PropertyNameCollection(array_keys($properties)));
×
473
        foreach ($properties as $propertyName => $propertyMetadata) {
×
474
            $this->propertyMetadataFactoryProphecy->create($resourceClass, $propertyName, ['normalization_groups' => null, 'denormalization_groups' => null])->willReturn($propertyMetadata);
×
475
            $this->typeConverterProphecy->convertPhpType(Type::null(), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), '', $resourceClass, $propertyName, $depth + 1)->willReturn(null);
×
476
            $this->typeConverterProphecy->convertPhpType(Type::callable(), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), '', $resourceClass, $propertyName, $depth + 1)->willReturn('NotRegisteredType');
×
477
            $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), '', $resourceClass, $propertyName, $depth + 1)->willReturn(GraphQLType::string());
×
478
            $this->typeConverterProphecy->convertPhpType(Type::list(Type::string()), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), '', $resourceClass, $propertyName, $depth + 1)->willReturn(GraphQLType::nonNull(GraphQLType::listOf(GraphQLType::nonNull(GraphQLType::string()))));
×
479

480
            if ('propertyObject' === $propertyName) {
×
481
                $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), 'objectClass', $resourceClass, $propertyName, $depth + 1)->willReturn(new ObjectType(['name' => 'objectType', 'fields' => []]));
×
482
                $this->itemResolverFactoryProphecy->__invoke('objectClass', $resourceClass, $operation, Argument::any())->willReturn(static function (): void {
×
483
                });
×
484
            }
485
            if ('propertyNestedResource' === $propertyName) {
×
486
                $nestedResourceQueryOperation = new Query();
×
487
                $this->resourceMetadataCollectionFactoryProphecy->create('nestedResourceClass')->willReturn(new ResourceMetadataCollection('nestedResourceClass', [(new ApiResource())->withGraphQlOperations(['item_query' => $nestedResourceQueryOperation])]));
×
488
                $this->typeConverterProphecy->convertPhpType(Argument::type(Type::class), Argument::type('bool'), Argument::that(static fn (Operation $arg): bool => $arg->getName() === $operation->getName()), 'nestedResourceClass', $resourceClass, $propertyName, $depth + 1)->willReturn(new ObjectType(['name' => 'objectType', 'fields' => []]));
×
489
                $this->itemResolverFactoryProphecy->__invoke('nestedResourceClass', $resourceClass, $nestedResourceQueryOperation, Argument::any())->willReturn(static function (): void {
×
490
                });
×
491
            }
492
        }
493
        $this->typesContainerProphecy->has('NotRegisteredType')->willReturn(false);
×
494
        $this->typesContainerProphecy->all()->willReturn([]);
×
495

496
        $fieldsBuilder = $this->fieldsBuilder;
×
497
        if ($advancedNameConverterFactory) {
×
498
            $fieldsBuilder = $this->buildFieldsBuilder($advancedNameConverterFactory($this));
×
499
        }
500
        $resourceObjectTypeFields = $fieldsBuilder->getResourceObjectTypeFields($resourceClass, $operation, $input, $depth, $ioMetadata);
×
501

502
        $this->assertEquals($expectedResourceObjectTypeFields, $resourceObjectTypeFields);
×
503
    }
504

505
    public static function resourceObjectTypeFieldsProvider(): iterable
506
    {
507
        $advancedNameConverterFactory = function (self $that): AdvancedNameConverterInterface {
×
508
            $advancedNameConverterProphecy = $that->prophesize(AdvancedNameConverterInterface::class);
×
NEW
509
            $advancedNameConverterProphecy->normalize('field', \stdClass::class)->willReturn('normalizedField');
×
510

511
            return $advancedNameConverterProphecy->reveal();
×
512
        };
×
513

NEW
514
        yield 'query' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
515
            [
×
516
                'property' => new ApiProperty(),
×
517
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(true)->withWritable(false),
×
518
                'propertyNotReadable' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(false),
×
519
                'nameConverted' => (new ApiProperty())->withNativeType(Type::string())->withReadable(true)->withWritable(false),
×
520
            ],
×
521
            false, 0, null,
×
522
            [
×
523
                'id' => [
×
524
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
525
                ],
×
526
                'propertyBool' => [
×
527
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
528
                    'description' => null,
×
529
                    'args' => [],
×
530
                    'resolve' => null,
×
531
                    'deprecationReason' => null,
×
532
                ],
×
533
                'name_converted' => [
×
534
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
535
                    'description' => null,
×
536
                    'args' => [],
×
537
                    'resolve' => null,
×
538
                    'deprecationReason' => null,
×
539
                ],
×
540
            ],
×
541
        ];
×
NEW
542
        yield 'query with advanced name converter' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
543
            [
×
544
                'field' => (new ApiProperty())->withNativeType(Type::string())->withReadable(true)->withWritable(false),
×
545
            ],
×
546
            false, 0, null,
×
547
            [
×
548
                'id' => [
×
549
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
550
                ],
×
551
                'normalizedField' => [
×
552
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
553
                    'description' => null,
×
554
                    'args' => [],
×
555
                    'resolve' => null,
×
556
                    'deprecationReason' => null,
×
557
                ],
×
558
            ],
×
559
            $advancedNameConverterFactory,
×
560
        ];
×
NEW
561
        yield 'query input' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
562
            [
×
563
                'property' => new ApiProperty(),
×
564
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
565
                'nonWritableProperty' => (new ApiProperty())->withNativeType(Type::string())->withReadable(false)->withWritable(false),
×
566
            ],
×
567
            true, 0, null,
×
568
            [
×
569
                'id' => [
×
570
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
571
                ],
×
572
                'propertyBool' => [
×
573
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
574
                    'description' => null,
×
575
                    'args' => [],
×
576
                    'resolve' => null,
×
577
                    'deprecationReason' => null,
×
578
                ],
×
579
            ],
×
580
        ];
×
NEW
581
        yield 'query with simple non-null string array property' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
582
            [
×
583
                'property' => (new ApiProperty())->withNativeType(Type::list(Type::string()))->withReadable(true)->withWritable(false),
×
584
            ],
×
585
            false, 0, null,
×
586
            [
×
587
                'id' => [
×
588
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
589
                ],
×
590
                'property' => [
×
591
                    'type' => GraphQLType::nonNull(GraphQLType::listOf(GraphQLType::nonNull(GraphQLType::string()))),
×
592
                    'description' => null,
×
593
                    'args' => [],
×
594
                    'resolve' => null,
×
595
                    'deprecationReason' => null,
×
596
                ],
×
597
            ],
×
598
        ];
×
NEW
599
        yield 'query with nested resources' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
600
            [
×
601
                'propertyNestedResource' => (new ApiProperty())->withNativeType(Type::object('nestedResourceClass'))->withReadable(true)->withWritable(true),
×
602
            ],
×
603
            false, 0, null,
×
604
            [
×
605
                'id' => [
×
606
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
607
                ],
×
608
                'propertyNestedResource' => [
×
609
                    'type' => GraphQLType::nonNull(new ObjectType(['name' => 'objectType', 'fields' => []])),
×
610
                    'description' => null,
×
611
                    'args' => [],
×
612
                    'resolve' => static function (): void {
×
613
                    },
×
614
                    'deprecationReason' => null,
×
615
                ],
×
616
            ],
×
617
        ];
×
NEW
618
        yield 'mutation non input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('mutation'),
×
619
            [
×
620
                'property' => new ApiProperty(),
×
621
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
622
                'propertyReadable' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(true)->withWritable(true),
×
623
                'propertyObject' => (new ApiProperty())->withNativeType(Type::object('objectClass'))->withReadable(true)->withWritable(true),
×
624
            ],
×
625
            false, 0, null,
×
626
            [
×
627
                'id' => [
×
628
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
629
                ],
×
630
                'propertyReadable' => [
×
631
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
632
                    'description' => null,
×
633
                    'args' => [],
×
634
                    'resolve' => null,
×
635
                    'deprecationReason' => null,
×
636
                ],
×
637
                'propertyObject' => [
×
638
                    'type' => GraphQLType::nonNull(new ObjectType(['name' => 'objectType', 'fields' => []])),
×
639
                    'description' => null,
×
640
                    'args' => [],
×
641
                    'resolve' => static function (): void {
×
642
                    },
×
643
                    'deprecationReason' => null,
×
644
                ],
×
645
            ],
×
646
        ];
×
NEW
647
        yield 'mutation input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('mutation'),
×
648
            [
×
649
                'property' => new ApiProperty(),
×
650
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withDescription('propertyBool description')->withReadable(false)->withWritable(true)->withDeprecationReason('not useful'),
×
651
                'propertySubresource' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
652
                'nonWritableProperty' => (new ApiProperty())->withNativeType(Type::string())->withReadable(false)->withWritable(false),
×
653
                'id' => (new ApiProperty())->withNativeType(Type::int())->withReadable(false)->withWritable(true),
×
654
            ],
×
655
            true, 0, null,
×
656
            [
×
657
                'id' => [
×
658
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
659
                ],
×
660
                'propertyBool' => [
×
661
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
662
                    'description' => 'propertyBool description',
×
663
                    'args' => [],
×
664
                    'resolve' => null,
×
665
                    'deprecationReason' => 'not useful',
×
666
                ],
×
667
                'propertySubresource' => [
×
668
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
669
                    'description' => null,
×
670
                    'args' => [],
×
671
                    'resolve' => null,
×
672
                    'deprecationReason' => null,
×
673
                ],
×
674
                '_id' => [
×
675
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
676
                    'description' => null,
×
677
                    'args' => [],
×
678
                    'resolve' => null,
×
679
                    'deprecationReason' => null,
×
680
                ],
×
681
                'clientMutationId' => GraphQLType::string(),
×
682
            ],
×
683
        ];
×
NEW
684
        yield 'custom mutation' => [\stdClass::class, (new Mutation())->withResolver('resolver')->withName('mutation'),
×
685
            [
×
686
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withDescription('propertyBool description')->withReadable(false)->withWritable(true),
×
687
            ],
×
688
            true, 0, null,
×
689
            [
×
690
                'propertyBool' => [
×
691
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
692
                    'description' => 'propertyBool description',
×
693
                    'args' => [],
×
694
                    'resolve' => null,
×
695
                    'deprecationReason' => null,
×
696
                ],
×
697
                'clientMutationId' => GraphQLType::string(),
×
698
            ],
×
699
        ];
×
NEW
700
        yield 'mutation nested input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('mutation'),
×
701
            [
×
702
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
703
            ],
×
704
            true, 1, null,
×
705
            [
×
706
                'id' => [
×
707
                    'type' => GraphQLType::id(),
×
708
                ],
×
709
                'propertyBool' => [
×
710
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
711
                    'description' => null,
×
712
                    'args' => [],
×
713
                    'resolve' => null,
×
714
                    'deprecationReason' => null,
×
715
                ],
×
716
                'clientMutationId' => GraphQLType::string(),
×
717
            ],
×
718
        ];
×
NEW
719
        yield 'delete mutation input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('delete'),
×
720
            [
×
721
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
722
            ],
×
723
            true, 0, null,
×
724
            [
×
725
                'id' => [
×
726
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
727
                ],
×
728
                'clientMutationId' => GraphQLType::string(),
×
729
            ],
×
730
        ];
×
NEW
731
        yield 'create mutation input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('create'),
×
732
            [
×
733
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
734
            ],
×
735
            true, 0, null,
×
736
            [
×
737
                'propertyBool' => [
×
738
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
739
                    'description' => null,
×
740
                    'args' => [],
×
741
                    'resolve' => null,
×
742
                    'deprecationReason' => null,
×
743
                ],
×
744
                'clientMutationId' => GraphQLType::string(),
×
745
            ],
×
746
        ];
×
NEW
747
        yield 'update mutation input' => [\stdClass::class, (new Mutation())->withClass(\stdClass::class)->withName('update'),
×
748
            [
×
749
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
750
            ],
×
751
            true, 0, null,
×
752
            [
×
753
                'id' => [
×
754
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
755
                ],
×
756
                'propertyBool' => [
×
757
                    'type' => GraphQLType::string(),
×
758
                    'description' => null,
×
759
                    'args' => [],
×
760
                    'resolve' => null,
×
761
                    'deprecationReason' => null,
×
762
                ],
×
763
                'clientMutationId' => GraphQLType::string(),
×
764
            ],
×
765
        ];
×
NEW
766
        yield 'subscription non input' => [\stdClass::class, (new Subscription())->withClass(\stdClass::class),
×
767
            [
×
768
                'property' => new ApiProperty(),
×
769
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
770
                'propertyReadable' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(true)->withWritable(true),
×
771
            ],
×
772
            false, 0, null,
×
773
            [
×
774
                'id' => [
×
775
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
776
                ],
×
777
                'propertyReadable' => [
×
778
                    'type' => GraphQLType::nonNull(GraphQLType::string()),
×
779
                    'description' => null,
×
780
                    'args' => [],
×
781
                    'resolve' => null,
×
782
                    'deprecationReason' => null,
×
783
                ],
×
784
            ],
×
785
        ];
×
NEW
786
        yield 'subscription input' => [\stdClass::class, (new Subscription())->withClass(\stdClass::class),
×
787
            [
×
788
                'property' => new ApiProperty(),
×
789
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withDescription('propertyBool description')->withReadable(false)->withWritable(true)->withDeprecationReason('not useful'),
×
790
                'propertySubresource' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
791
                'id' => (new ApiProperty())->withNativeType(Type::int())->withReadable(false)->withWritable(true),
×
792
            ],
×
793
            true, 0, null,
×
794
            [
×
795
                'id' => [
×
796
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
797
                ],
×
798
                'clientSubscriptionId' => GraphQLType::string(),
×
799
            ],
×
800
        ];
×
NEW
801
        yield 'null io metadata non input' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
802
            [
×
803
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
804
            ],
×
805
            false, 0, ['class' => null], [],
×
806
        ];
×
NEW
807
        yield 'null io metadata input' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
808
            [
×
809
                'propertyBool' => (new ApiProperty())->withNativeType(Type::bool())->withReadable(false)->withWritable(true),
×
810
            ],
×
811
            true, 0, ['class' => null],
×
812
            [
×
813
                'clientMutationId' => GraphQLType::string(),
×
814
            ],
×
815
        ];
×
NEW
816
        yield 'invalid types' => [\stdClass::class, (new Query())->withClass(\stdClass::class),
×
817
            [
×
818
                'propertyInvalidType' => (new ApiProperty())->withNativeType(Type::null())->withReadable(true)->withWritable(false),
×
819
                'propertyNotRegisteredType' => (new ApiProperty())->withNativeType(Type::callable())->withReadable(true)->withWritable(false),
×
820
            ],
×
821
            false, 0, null,
×
822
            [
×
823
                'id' => [
×
824
                    'type' => GraphQLType::nonNull(GraphQLType::id()),
×
825
                ],
×
826
            ],
×
827
        ];
×
828
    }
829

830
    public function testGetEnumFields(): void
831
    {
832
        $enumClass = GenderTypeEnum::class;
×
833

834
        $this->propertyMetadataFactoryProphecy->create($enumClass, GenderTypeEnum::MALE->name)->willReturn(new ApiProperty(
×
835
            description: 'Description of MALE case',
×
836
        ));
×
837
        $this->propertyMetadataFactoryProphecy->create($enumClass, GenderTypeEnum::FEMALE->name)->willReturn(new ApiProperty(
×
838
            description: 'Description of FEMALE case',
×
839
        ));
×
840

841
        $enumFields = $this->fieldsBuilder->getEnumFields($enumClass);
×
842

843
        $this->assertSame([
×
844
            GenderTypeEnum::MALE->name => ['value' => GenderTypeEnum::MALE->value, 'description' => 'Description of MALE case'],
×
845
            GenderTypeEnum::FEMALE->name => ['value' => GenderTypeEnum::FEMALE->value, 'description' => 'Description of FEMALE case'],
×
846
        ], $enumFields);
×
847
    }
848

849
    #[\PHPUnit\Framework\Attributes\DataProvider('resolveResourceArgsProvider')]
850
    public function testResolveResourceArgs(array $args, array $expectedResolvedArgs, ?string $expectedExceptionMessage = null): void
851
    {
852
        if (null !== $expectedExceptionMessage) {
×
853
            $this->expectExceptionMessage($expectedExceptionMessage);
×
854
        }
855

856
        $this->typeConverterProphecy->resolveType(Argument::type('string'))->willReturn(GraphQLType::string());
×
857

858
        $operation = (new Query())->withName('operation')->withShortName('shortName');
×
859
        $args = $this->fieldsBuilder->resolveResourceArgs($args, $operation);
×
860

861
        $this->assertSame($expectedResolvedArgs, $args);
×
862
    }
863

864
    public static function resolveResourceArgsProvider(): array
865
    {
866
        return [
×
867
            [[], []],
×
868
            [['customArg' => []], [], 'The argument "customArg" of the custom operation "operation" in shortName needs a "type" option.'],
×
869
            [['customArg' => ['type' => 'a type']], ['customArg' => ['type' => GraphQLType::string()]]],
×
870
        ];
×
871
    }
872
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc