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

api-platform / core / 15928092811

27 Jun 2025 01:58PM UTC coverage: 21.793% (-0.2%) from 22.016%
15928092811

push

github

web-flow
chore: bump phpstan version (#7239)

8 of 29 new or added lines in 14 files covered. (27.59%)

126 existing lines in 12 files now uncovered.

11375 of 52196 relevant lines covered (21.79%)

10.71 hits per line

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

0.0
/src/GraphQl/Tests/State/Processor/NormalizeProcessorTest.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\State\Processor;
15

16
use ApiPlatform\GraphQl\Serializer\SerializerContextBuilderInterface;
17
use ApiPlatform\GraphQl\State\Processor\NormalizeProcessor;
18
use ApiPlatform\Metadata\GraphQl\Mutation;
19
use ApiPlatform\Metadata\GraphQl\Query;
20
use ApiPlatform\Metadata\GraphQl\QueryCollection;
21
use ApiPlatform\Metadata\GraphQl\Subscription;
22
use ApiPlatform\State\Pagination\ArrayPaginator;
23
use ApiPlatform\State\Pagination\Pagination;
24
use ApiPlatform\State\Pagination\PartialPaginatorInterface;
25
use GraphQL\Type\Definition\ResolveInfo;
26
use PHPUnit\Framework\TestCase;
27
use Prophecy\PhpUnit\ProphecyTrait;
28
use Prophecy\Prophecy\ObjectProphecy;
29
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
30

31
class NormalizeProcessorTest extends TestCase
32
{
33
    use ProphecyTrait;
34

35
    private ObjectProphecy $resolveInfoProphecy;
36

37
    /**
38
     * {@inheritdoc}
39
     */
40
    protected function setUp(): void
41
    {
42
        $this->resolveInfoProphecy = $this->prophesize(ResolveInfo::class);
×
43
    }
44

45
    #[\PHPUnit\Framework\Attributes\DataProvider('processItems')]
46
    public function testProcess($body, $operation): void
47
    {
48
        $context = ['args' => []];
×
49
        $serializerContext = ['resource_class' => $operation->getClass()];
×
50
        $normalizer = $this->createMock(NormalizerInterface::class);
×
51
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
NEW
52
        $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, true)->willReturn($serializerContext);
×
53
        $normalizer->expects($this->once())->method('normalize')->with($body, 'graphql', $serializerContext);
×
54
        $processor = new NormalizeProcessor($normalizer, $serializerContextBuilder, new Pagination());
×
55
        $processor->process($body, $operation, [], $context);
×
56
    }
57

58
    public static function processItems(): array
59
    {
60
        return [
×
61
            [new \stdClass(), new Query(class: 'foo')],
×
62
            [new \stdClass(), new Mutation(class: 'foo', shortName: 'Foo')],
×
63
            [new \stdClass(), new Subscription(class: 'foo', shortName: 'Foo')],
×
64
        ];
×
65
    }
66

67
    #[\PHPUnit\Framework\Attributes\DataProvider('processCollection')]
68
    public function testProcessCollection($collection, $operation, $args, ?array $expectedResult, array $getFieldSelection, ?string $expectedExceptionClass = null, ?string $expectedExceptionMessage = null): void
69
    {
70
        $this->resolveInfoProphecy->getFieldSelection(1)->willReturn($getFieldSelection);
×
71
        $context = ['args' => $args, 'info' => $this->resolveInfoProphecy->reveal()];
×
72
        $serializerContext = ['resource_class' => $operation->getClass()];
×
73
        $normalizer = $this->prophesize(NormalizerInterface::class);
×
74

75
        $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
×
NEW
76
        $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, true)->willReturn($serializerContext);
×
77
        foreach ($collection as $v) {
×
78
            $normalizer->normalize($v, 'graphql', $serializerContext)->willReturn(['normalized_item'])->shouldBeCalledOnce();
×
79
        }
80

81
        if ($expectedExceptionClass) {
×
82
            $this->expectException($expectedExceptionClass);
×
83
            $this->expectExceptionMessage($expectedExceptionMessage);
×
84
        }
85

86
        $processor = new NormalizeProcessor($normalizer->reveal(), $serializerContextBuilder, new Pagination());
×
87
        $result = $processor->process(\is_callable($collection) ? $collection($this) : $collection, $operation, [], $context);
×
88
        $this->assertSame($expectedResult, $result);
×
89
    }
90

91
    public static function processCollection(): iterable
92
    {
93
        $partialPaginatorFactory = function (self $that): PartialPaginatorInterface {
×
94
            $partialPaginatorProphecy = $that->prophesize(PartialPaginatorInterface::class);
×
95
            $partialPaginatorProphecy->count()->willReturn(2);
×
96
            $partialPaginatorProphecy->valid()->willReturn(false);
×
97
            $partialPaginatorProphecy->getItemsPerPage()->willReturn(2.0);
×
98
            $partialPaginatorProphecy->rewind();
×
99

100
            return $partialPaginatorProphecy->reveal();
×
101
        };
×
102

103
        yield 'cursor - not paginator' => [[], new QueryCollection(class: 'foo'), [], null, [], \LogicException::class, 'Collection returned by the collection data provider must implement ApiPlatform\State\Pagination\PaginatorInterface or ApiPlatform\State\Pagination\PartialPaginatorInterface.'];
×
104
        yield 'cursor - empty paginator' => [new ArrayPaginator([], 0, 0), new QueryCollection(class: 'foo'), [], ['totalCount' => 0., 'edges' => [], 'pageInfo' => ['startCursor' => null, 'endCursor' => null, 'hasNextPage' => false, 'hasPreviousPage' => false]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
105
        yield 'cursor - paginator' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 0, 2), new QueryCollection(class: 'foo'), [],  ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MA=='], ['node' => ['normalized_item'], 'cursor' => 'MQ==']], 'pageInfo' => ['startCursor' => 'MA==', 'endCursor' => 'MQ==', 'hasNextPage' => true, 'hasPreviousPage' => false]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
106
        yield 'cursor - paginator with after cursor' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 1, 2), new QueryCollection(class: 'foo'), ['after' => 'MA=='], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ=='], ['node' => ['normalized_item'], 'cursor' => 'Mg==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'Mg==', 'hasNextPage' => false, 'hasPreviousPage' => true]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
107
        yield 'cursor - paginator with bad after cursor' => [new ArrayPaginator([], 0, 0), new QueryCollection(class: 'foo'), ['after' => '-'], null, ['edges' => ['cursor' => []]], \UnexpectedValueException::class, 'Cursor - is invalid'];
×
108
        yield 'cursor - paginator with empty after cursor' => [new ArrayPaginator([], 0, 0), new QueryCollection(class: 'foo'), ['after' => ''], null, ['edges' => ['cursor' => []]], \UnexpectedValueException::class, 'Empty cursor is invalid'];
×
109
        yield 'cursor - paginator with before cursor' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 1, 1), new QueryCollection(class: 'foo'), ['before' => 'Mg=='], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'MQ==', 'hasNextPage' => true, 'hasPreviousPage' => true]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
110
        yield 'cursor - paginator with bad before cursor' => [new ArrayPaginator([], 0, 0), new QueryCollection(class: 'foo'), ['before' => '-'], null, ['pageInfo' => ['endCursor' => true]], \UnexpectedValueException::class, 'Cursor - is invalid'];
×
111
        yield 'cursor - paginator with empty before cursor' => [new ArrayPaginator([], 0, 0), new QueryCollection(class: 'foo'), ['before' => ''], null, ['pageInfo' => ['endCursor' => true]], \UnexpectedValueException::class, 'Empty cursor is invalid'];
×
112
        yield 'cursor - paginator with last' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 1, 2), new QueryCollection(class: 'foo'), ['last' => 2], ['totalCount' => 3., 'edges' => [['node' => ['normalized_item'], 'cursor' => 'MQ=='], ['node' => ['normalized_item'], 'cursor' => 'Mg==']], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'Mg==', 'hasNextPage' => false, 'hasPreviousPage' => true]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
113
        yield 'cursor - partial paginator' => [$partialPaginatorFactory, new QueryCollection(class: 'foo'), [], ['totalCount' => 0., 'edges' => [], 'pageInfo' => ['startCursor' => 'MA==', 'endCursor' => 'MQ==', 'hasNextPage' => false, 'hasPreviousPage' => false]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
114
        yield 'cursor - partial paginator with after cursor' => [$partialPaginatorFactory, new QueryCollection(class: 'foo'), ['after' => 'MA=='], ['totalCount' => 0., 'edges' => [], 'pageInfo' => ['startCursor' => 'MQ==', 'endCursor' => 'Mg==', 'hasNextPage' => false, 'hasPreviousPage' => true]], ['totalCount' => true, 'edges' => ['cursor' => true], 'pageInfo' => ['startCursor' => true, 'endCursor' => true, 'hasNextPage' => true, 'hasPreviousPage' => true]]];
×
115

116
        yield 'page - not paginator, itemsPerPage requested' => [[], (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], [], ['paginationInfo' => ['itemsPerPage' => true]], \LogicException::class, 'Collection returned by the collection data provider must implement ApiPlatform\State\Pagination\PartialPaginatorInterface to return itemsPerPage field.'];
×
117
        yield 'page - not paginator, lastPage requested' => [[], (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], [], ['paginationInfo' => ['lastPage' => true]], \LogicException::class, 'Collection returned by the collection data provider must implement ApiPlatform\State\Pagination\PaginatorInterface to return lastPage field.'];
×
118
        yield 'page - not paginator, totalCount requested' => [[], (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], [], ['paginationInfo' => ['totalCount' => true]], \LogicException::class, 'Collection returned by the collection data provider must implement ApiPlatform\State\Pagination\PaginatorInterface to return totalCount field.'];
×
119
        yield 'page - not paginator, hasNextPage requested' => [[], (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], [], ['paginationInfo' => ['hasNextPage' => true]], \LogicException::class, 'Collection returned by the collection data provider must implement ApiPlatform\State\Pagination\HasNextPagePaginatorInterface to return hasNextPage field.'];
×
120
        yield 'page - empty paginator - itemsPerPage requested' => [new ArrayPaginator([], 0, 0), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [], 'paginationInfo' => ['itemsPerPage' => .0]], ['paginationInfo' => ['itemsPerPage' => true]]];
×
121
        yield 'page - empty paginator - lastPage requested' => [new ArrayPaginator([], 0, 0), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [], 'paginationInfo' => ['lastPage' => 1.0]], ['paginationInfo' => ['lastPage' => true]]];
×
122
        yield 'page - empty paginator - totalCount requested' => [new ArrayPaginator([], 0, 0), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [], 'paginationInfo' => ['totalCount' => .0]], ['paginationInfo' => ['totalCount' => true]]];
×
123
        yield 'page - empty paginator - hasNextPage requested' => [new ArrayPaginator([], 0, 0), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [], 'paginationInfo' => ['hasNextPage' => false]], ['paginationInfo' => ['hasNextPage' => true]]];
×
124
        yield 'page - paginator page 1 - itemsPerPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 0, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item'], ['normalized_item']], 'paginationInfo' => ['itemsPerPage' => 2.0]], ['paginationInfo' => ['itemsPerPage' => true]]];
×
125
        yield 'page - paginator page 1 - lastPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 0, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item'], ['normalized_item']], 'paginationInfo' => ['lastPage' => 2.0]], ['paginationInfo' => ['lastPage' => true]]];
×
126
        yield 'page - paginator page 1 - totalCount requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 0, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item'], ['normalized_item']], 'paginationInfo' => ['totalCount' => 3.0]], ['paginationInfo' => ['totalCount' => true]]];
×
127
        yield 'page - paginator page 1 - hasNextPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 0, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item'], ['normalized_item']], 'paginationInfo' => ['hasNextPage' => true]], ['paginationInfo' => ['hasNextPage' => true]]];
×
128
        yield 'page - paginator with page - itemsPerPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 2, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item']], 'paginationInfo' => ['itemsPerPage' => 2.0]], ['paginationInfo' => ['itemsPerPage' => true]]];
×
129
        yield 'page - paginator with page - lastPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 2, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item']], 'paginationInfo' => ['lastPage' => 2.0]], ['paginationInfo' => ['lastPage' => true]]];
×
130
        yield 'page - paginator with page - totalCount requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 2, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item']], 'paginationInfo' => ['totalCount' => 3.0]], ['paginationInfo' => ['totalCount' => true]]];
×
131
        yield 'page - paginator with page - hasNextPage requested' => [new ArrayPaginator([(object) ['test' => 'a'], (object) ['test' => 'b'], (object) ['test' => 'c']], 2, 2), (new QueryCollection(class: 'foo'))->withPaginationType('page'), [], ['collection' => [['normalized_item']], 'paginationInfo' => ['hasNextPage' => false]], ['paginationInfo' => ['hasNextPage' => true]]];
×
132
    }
133
}
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

© 2025 Coveralls, Inc