Issues (51)

tests/Api/ServerTest.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EcodevTests\Felix\Api;
6
7
use Ecodev\Felix\Api\Server;
8
use EcodevTests\Felix\Traits\TestWithContainer;
9
use Exception;
10
use GraphQL\Error\UserError;
11
use GraphQL\Executor\ExecutionResult;
12
use GraphQL\Type\Definition\ObjectType;
13
use GraphQL\Type\Definition\Type;
14
use GraphQL\Type\Schema;
15
use Laminas\ConfigAggregator\ArrayProvider;
16
use Laminas\ConfigAggregator\ConfigAggregator;
17
use Laminas\Diactoros\CallbackStream;
18
use Laminas\Diactoros\ServerRequest;
19
use PHPUnit\Framework\TestCase;
20
use Psr\Log\LoggerInterface;
21
22
class ServerTest extends TestCase
23
{
24
    use TestWithContainer;
25
26
    /**
27
     * @var LoggerInterface&\PHPUnit\Framework\MockObject\MockObject
28
     */
29
    private LoggerInterface $logger;
30
31
    protected function setUp(): void
32
    {
33
        $this->logger = $this->createMock(LoggerInterface::class);
34
        $aggregator = new ConfigAggregator([
35
            new ArrayProvider([
36
                'dependencies' => [
37
                    'factories' => [
38
                        LoggerInterface::class => fn () => $this->logger,
39
                    ],
40
                ],
41
            ]),
42
        ]);
43
44
        $this->createContainer($aggregator);
45
    }
46
47
    /**
48
     * @dataProvider providerExecute
49
     */
50
    public function testExecute(string $body, array $expected, string $expectedLog = ''): void
51
    {
52
        $schema = new Schema(['query' => new ObjectType([
53
            'name' => 'Query',
54
            'fields' => [
55
                'nativeException' => [
56
                    'type' => Type::boolean(),
57
                    'resolve' => fn () => throw new Exception('Fake message'),
58
                ],
59
                'felixException' => [
60
                    'type' => Type::boolean(),
61
                    'resolve' => fn () => throw new \Ecodev\Felix\Api\Exception('Fake message'),
62
                ],
63
                'notFoundException' => [
64
                    'type' => Type::boolean(),
65
                    'resolve' => fn () => throw new UserError('Entity not found for class `foo` and ID `bar`.'),
66
                ],
67
                'invalidVariables' => [
68
                    'args' => [
69
                        'myArg' => Type::boolean(),
70
                    ],
71
                    'type' => Type::boolean(),
72
                    'resolve' => fn () => true,
73
                ],
74
            ],
75
        ])]);
76
77
        if ($expectedLog) {
78
            $this->logger->expects(self::once())->method('error')->with($expectedLog);
0 ignored issues
show
The method expects() does not exist on Psr\Log\LoggerInterface. ( Ignorable by Annotation )

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

78
            $this->logger->/** @scrutinizer ignore-call */ 
79
                           expects(self::once())->method('error')->with($expectedLog);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
79
        } else {
80
            $this->logger->expects(self::never())->method('error');
81
        }
82
83
        $server = new Server($schema, false);
84
        $request = new ServerRequest(method: 'POST', body: new CallbackStream(fn () => $body));
85
        $request = $request->withHeader('content-type', 'application/json');
86
87
        $result = $server->execute($request);
88
89
        self::assertInstanceOf(ExecutionResult::class, $result);
90
        self::assertSame($expected, $result->jsonSerialize());
91
    }
92
93
    public static function providerExecute(): iterable
94
    {
95
        yield 'empty body' => [
96
            '',
97
            [
98
                'errors' => [
99
                    [
100
                        'message' => 'GraphQL Request must include at least one of those two parameters: "query" or "queryId"',
101
                    ],
102
                ],
103
            ],
104
        ];
105
106
        yield 'invalid json' => [
107
            'foo bar',
108
            [
109
                'errors' => [
110
                    [
111
                        'message' => 'GraphQL Request must include at least one of those two parameters: "query" or "queryId"',
112
                    ],
113
                ],
114
            ],
115
        ];
116
117
        yield 'empty query' => [
118
            '{"query": ""}',
119
            [
120
                'errors' => [
121
                    [
122
                        'message' => 'GraphQL Request must include at least one of those two parameters: "query" or "queryId"',
123
                    ],
124
                ],
125
            ],
126
        ];
127
128
        yield 'normal' => [
129
            '{"query": "{ __typename }"}',
130
            [
131
                'data' => [
132
                    '__typename' => 'Query',
133
                ],
134
            ],
135
        ];
136
137
        yield 'native exception' => [
138
            '{"query": "{ nativeException }"}',
139
            [
140
                'errors' => [
141
                    [
142
                        'message' => 'Internal server error',
143
                        'locations' => [
144
                            [
145
                                'line' => 1,
146
                                'column' => 3,
147
                            ],
148
                        ],
149
                        'path' => [
150
                            'nativeException',
151
                        ],
152
                    ],
153
                ],
154
                'data' => [
155
                    'nativeException' => null,
156
                ],
157
            ],
158
            <<<STRING
159
                Fake message
160
161
                GraphQL request (1:3)
162
                1: { nativeException }
163
                     ^
164
165
                STRING
166
        ];
167
168
        yield 'Felix exception shows snackbar' => [
169
            '{"query": "{ felixException }"}',
170
            [
171
                'errors' => [
172
                    [
173
                        'message' => 'Fake message',
174
                        'locations' => [
175
                            [
176
                                'line' => 1,
177
                                'column' => 3,
178
                            ],
179
                        ],
180
                        'path' => [
181
                            'felixException',
182
                        ],
183
                        'extensions' => [
184
                            'showSnack' => true,
185
                        ],
186
                    ],
187
                ],
188
                'data' => [
189
                    'felixException' => null,
190
                ],
191
            ],
192
            <<<STRING
193
                Fake message
194
195
                GraphQL request (1:3)
196
                1: { felixException }
197
                     ^
198
199
                STRING
200
        ];
201
202
        yield 'not found exception does not show snackbar but is flagged' => [
203
            '{"query": "{ notFoundException }"}',
204
            [
205
                'errors' => [
206
                    [
207
                        'message' => 'Entity not found for class `foo` and ID `bar`.',
208
                        'locations' => [
209
                            [
210
                                'line' => 1,
211
                                'column' => 3,
212
                            ],
213
                        ],
214
                        'path' => [
215
                            'notFoundException',
216
                        ],
217
                        'extensions' => [
218
                            'objectNotFound' => true,
219
                        ],
220
                    ],
221
                ],
222
                'data' => [
223
                    'notFoundException' => null,
224
                ],
225
            ],
226
            <<<STRING
227
                Entity not found for class `foo` and ID `bar`.
228
229
                GraphQL request (1:3)
230
                1: { notFoundException }
231
                     ^
232
233
                STRING
234
        ];
235
236
        yield 'invalidVariables shows snackbar' => [
237
            '{"query": "query ($v: Boolean) { invalidVariables(myArg: $v) }", "variables": {"v": 123}}',
238
            [
239
                'errors' => [
240
                    [
241
                        'message' => 'Variable "$v" got invalid value 123; Boolean cannot represent a non boolean value: 123',
242
                        'locations' => [
243
                            [
244
                                'line' => 1,
245
                                'column' => 8,
246
                            ],
247
                        ],
248
                        'extensions' => [
249
                            'showSnack' => true,
250
                        ],
251
                    ],
252
                ],
253
            ],
254
            <<<STRING
255
                Variable "\$v" got invalid value 123; Boolean cannot represent a non boolean value: 123
256
257
                GraphQL request (1:8)
258
                1: query (\$v: Boolean) { invalidVariables(myArg: \$v) }
259
                          ^
260
261
                STRING
262
        ];
263
    }
264
}
265