Completed
Push — master ( fb244a...6e9b12 )
by John
05:17
created

EventListener/Request/RequestProcessorTest.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the KleijnWeb\SwaggerBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace KleijnWeb\SwaggerBundle\Tests\EventListener\Request;
10
11
use KleijnWeb\PhpApi\Descriptions\Description\Description;
12
use KleijnWeb\PhpApi\Descriptions\Description\Operation;
13
use KleijnWeb\PhpApi\Descriptions\Description\Parameter;
14
use KleijnWeb\PhpApi\Descriptions\Description\Path;
15
use KleijnWeb\PhpApi\Descriptions\Description\Repository;
16
use KleijnWeb\PhpApi\Descriptions\Description\Schema\ObjectSchema;
17
use KleijnWeb\PhpApi\Descriptions\Description\Schema\Schema;
18
use KleijnWeb\PhpApi\Descriptions\Description\Schema\Validator\SchemaValidator;
19
use KleijnWeb\PhpApi\Descriptions\Description\Schema\Validator\ValidationResult;
20
use KleijnWeb\PhpApi\Descriptions\Request\RequestParameterAssembler;
21
use KleijnWeb\SwaggerBundle\Hydrator\ObjectHydrator;
22
use KleijnWeb\PhpApi\RoutingBundle\Routing\RequestMeta;
23
use KleijnWeb\SwaggerBundle\EventListener\Request\RequestProcessor;
24
use KleijnWeb\SwaggerBundle\Exception\MalformedContentException;
25
use KleijnWeb\SwaggerBundle\Exception\ValidationException;
26
use PHPUnit\Framework\TestCase;
27
use Symfony\Component\HttpFoundation\ParameterBag;
28
use Symfony\Component\HttpFoundation\Request;
29
30
/**
31
 * @author John Kleijn <[email protected]>
32
 */
33
class RequestProcessorTest extends TestCase
34
{
35
    /**
36
     * @var  \PHPUnit_Framework_MockObject_MockObject
37
     */
38
    private $repositoryMock;
39
40
    /**
41
     * @var  \PHPUnit_Framework_MockObject_MockObject
42
     */
43
    private $validatorMock;
44
45
    /**
46
     * @var  \PHPUnit_Framework_MockObject_MockObject
47
     */
48
    private $hydratorMock;
49
50
    /**
51
     * @var  \PHPUnit_Framework_MockObject_MockObject
52
     */
53
    private $parametersAssemblerMock;
54
55
    /**
56
     * Create mocks
57
     */
58
    protected function setUp()
59
    {
60
        /** @var Repository $repository */
61
        $this->repositoryMock = $repository = $this
62
            ->getMockBuilder(Repository::class)
63
            ->disableOriginalConstructor()
64
            ->getMock();
65
66
        /** @var Repository $repository */
67
        $this->validatorMock = $validator = $this
68
            ->getMockBuilder(SchemaValidator::class)
69
            ->disableOriginalConstructor()
70
            ->getMock();
71
72
        /** @var RequestParameterAssembler $hydrator */
73
        $this->parametersAssemblerMock = $parametersAssembler = $this
74
            ->getMockBuilder(RequestParameterAssembler::class)
75
            ->disableOriginalConstructor()
76
            ->getMock();
77
78
        /** @var ObjectHydrator $hydrator */
79
        $this->hydratorMock = $hydrator = $this
80
            ->getMockBuilder(ObjectHydrator::class)
81
            ->disableOriginalConstructor()
82
            ->getMock();
83
    }
84
85
    /**
86
     * @test
87
     */
88
    public function willThrowExceptionIfRequestDoesNotHaveDocumentUri()
89
    {
90
        $processor = $this->createProcessor();
91
92
        $this->expectException(\UnexpectedValueException::class);
93
        $processor->process(new Request());
94
    }
95
96
    /**
97
     * @test
98
     */
99 View Code Duplication
    public function willThrowExceptionWhenContentIsNotJson()
100
    {
101
        $processor = $this->createProcessor();
102
103
        $this->expectException(MalformedContentException::class);
104
105
        $processor->process(
106
            $this->createRequest(
107
                [
108
                    RequestMeta::ATTRIBUTE_URI  => '/uri',
109
                    RequestMeta::ATTRIBUTE_PATH => '/path',
110
                ],
111
                'not json'
112
            )
113
        );
114
    }
115
116
    /**
117
     * @test
118
     */
119 View Code Duplication
    public function willAssembleParameters()
120
    {
121
        $processor = $this->createProcessor();
122
        $this->parametersAssemblerMock->expects($this->once())->method('assemble');
123
124
        $processor->process(
125
            $this->createRequest(
126
                [
127
                    RequestMeta::ATTRIBUTE_URI  => '/uri',
128
                    RequestMeta::ATTRIBUTE_PATH => '/path',
129
                ]
130
            )
131
        );
132
    }
133
134
    /**
135
     * @test
136
     */
137
    public function willUpdateAttributes()
138
    {
139
        $processor         = $this->createProcessor();
140
        $coercedAttributes = (object)[
141
            'foo' => 'bar',
142
        ];
143
144
        $this->parametersAssemblerMock->expects($this->once())->method('assemble')->willReturn($coercedAttributes);
145
146
        $request = $this->createRequest(
147
            [
148
                RequestMeta::ATTRIBUTE_URI  => '/uri',
149
                RequestMeta::ATTRIBUTE_PATH => '/path',
150
            ]
151
        );
152
153
        $processor->process($request);
154
155
        $this->assertTrue($request->attributes->has(RequestMeta::ATTRIBUTE_URI));
156
        $this->assertTrue($request->attributes->has(RequestMeta::ATTRIBUTE_PATH));
157
        $this->assertTrue($request->attributes->has('foo'));
158
        $this->assertSame('bar', $request->attributes->get('foo'));
159
    }
160
161
    /**
162
     * @test
163
     */
164
    public function canDecodeJsonBody()
165
    {
166
        $body = (object)['foo' => 'bar'];
167
168
        $processor = $this->createProcessor();
169
170
        $this->parametersAssemblerMock
171
            ->expects($this->once())
172
            ->method('assemble')
173
            ->willReturnCallback(
174
                function (
175
                    Operation $operation,
176
                    array $query,
177
                    array $attributes,
178
                    array $headers,
179
                    \stdClass $body
180
                ) {
181
                    return (object)['theBody' => $body];
182
                }
183
            );
184
185
        $request = $this->createRequest(
186
            [
187
                RequestMeta::ATTRIBUTE_URI  => '/uri',
188
                RequestMeta::ATTRIBUTE_PATH => '/path',
189
            ],
190
            json_encode($body)
191
        );
192
193
        $processor->process($request);
194
195
        $this->assertEquals($body, $request->attributes->get('theBody'));
196
    }
197
198
    /**
199
     * @test
200
     */
201
    public function canHydrateJsonBody()
202
    {
203
        $body = (object)['theBody' => 'bar'];
204
205
        $processor = $this->createProcessor(true, true);
206
207
        $parameter       = new Parameter('theBody', true, new ObjectSchema((object)[]), Parameter::IN_BODY);
208
        $descriptionMock = $this->getMockBuilder(Description::class)->disableOriginalConstructor()->getMock();
209
        $descriptionMock->expects($this->once())->method('getRequestBodyParameter')->willReturn($parameter);
210
211
        $this->repositoryMock->expects($this->once())->method('get')->willReturn($descriptionMock);
212
213
        $this->parametersAssemblerMock
214
            ->expects($this->once())
215
            ->method('assemble')
216
            ->willReturnCallback(
217
                function (
218
                    Operation $operation,
219
                    array $query,
220
                    array $attributes,
221
                    array $headers,
222
                    \stdClass $body
223
                ) {
224
                    return (object)['theBody' => $body];
225
                }
226
            );
227
228
        $dto = new \ArrayObject;
229
230
        $this->hydratorMock
231
            ->expects($this->once())
232
            ->method('hydrate')
233
            ->with($body, $this->isInstanceOf(Schema::class))
234
            ->willReturnCallback(
235
                function () use ($dto) {
236
                    return $dto;
237
                }
238
            );
239
240
        $request = $this->createRequest(
241
            [
242
                RequestMeta::ATTRIBUTE_URI  => '/uri',
243
                RequestMeta::ATTRIBUTE_PATH => '/path',
244
            ],
245
            json_encode($body)
246
        );
247
248
        $processor->process($request);
249
250
        $this->assertSame($dto, $request->attributes->get('theBody'));
251
    }
252
253
    /**
254
     * @test
255
     */
256
    public function willSetRequestMetaAttribute()
257
    {
258
        $processor         = $this->createProcessor();
259
        $coercedAttributes = (object)[
260
            'foo' => 'bar',
261
        ];
262
263
        $this->parametersAssemblerMock->expects($this->once())->method('assemble')->willReturn($coercedAttributes);
264
265
        $request = $this->createRequest(
266
            [
267
                RequestMeta::ATTRIBUTE_URI  => '/uri',
268
                RequestMeta::ATTRIBUTE_PATH => '/path',
269
            ]
270
        );
271
272
        $processor->process($request);
273
274
        $this->assertTrue($request->attributes->has(RequestMeta::ATTRIBUTE));
275
    }
276
277
    /**
278
     * @test
279
     */
280 View Code Duplication
    public function willThrowExceptionIfRequestIsNotValid()
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281
    {
282
        $processor = $this->createProcessor(false, false);
283
284
        $this->expectException(ValidationException::class);
285
286
        $processor->process(
287
            $this->createRequest(
288
                [
289
                    RequestMeta::ATTRIBUTE_URI  => '/uri',
290
                    RequestMeta::ATTRIBUTE_PATH => '/path',
291
                ]
292
            )
293
        );
294
    }
295
296
    /**
297
     * @test
298
     */
299
    public function willThrowExceptionIfRequestBodyIsNotValid()
300
    {
301
        $this->hydratorMock->expects($this->never())
302
            ->method('hydrate');
303
304
        $processor = $this->createProcessor(true, false);
305
        $this->expectException(ValidationException::class);
306
307
        $processor->process(
308
            $this->createRequest(
309
                [
310
                    RequestMeta::ATTRIBUTE_URI  => '/uri',
311
                    RequestMeta::ATTRIBUTE_PATH => '/path',
312
                ],
313
                json_encode([])
314
            )
315
        );
316
    }
317
318
    /**
319
     * @test
320
     */
321
    public function willPassNullToValidatorWhenOperationAndRequestHaveNoParams()
322
    {
323
        $processor = $this->createProcessor();
324
325
        $descriptionMock = $this->getMockBuilder(Description::class)->disableOriginalConstructor()->getMock();
326
        $pathMock        = $this->getMockBuilder(Path::class)->disableOriginalConstructor()->getMock();
327
        $descriptionMock->expects($this->once())->method('getPath')->willReturn($pathMock);
328
        $operationMock = $this->getMockBuilder(Operation::class)->disableOriginalConstructor()->getMock();
329
        $pathMock->expects($this->once())->method('getOperation')->willReturn($operationMock);
330
        $operationMock->expects($this->once())->method('hasParameters')->willReturn(false);
331
332
        $this->repositoryMock->expects($this->once())->method('get')->willReturn($descriptionMock);
333
        $this->parametersAssemblerMock->expects($this->once())->method('assemble')->willReturn((object)[]);
334
335
        $this->validatorMock->expects($this->once())->method('validate')->with(
336
            $this->isInstanceOf(Schema::class),
337
            null
338
        );
339
340
        $processor->process(
341
            $this->createRequest(
342
                [
343
                    RequestMeta::ATTRIBUTE_URI  => '/uri',
344
                    RequestMeta::ATTRIBUTE_PATH => '/path',
345
                ]
346
            )
347
        );
348
    }
349
350
    /**
351
     * @param bool      $useHydrator
352
     * @param null|bool $forcedValidationResult
353
     *
354
     * @return RequestProcessor
355
     */
356
    private function createProcessor(bool $useHydrator = false, $forcedValidationResult = true): RequestProcessor
357
    {
358
        if (null !== $forcedValidationResult) {
359
            $this->validatorMock
360
                ->expects($this->any())
361
                ->method('validate')
362
                ->willReturn(new ValidationResult($forcedValidationResult));
363
        }
364
365
        /** @var Repository $repository */
366
        $repository = $this->repositoryMock;
367
        /** @var SchemaValidator $validator */
368
        $validator = $this->validatorMock;
369
        /** @var RequestParameterAssembler $parametersAssembler */
370
        $parametersAssembler = $this->parametersAssemblerMock;
371
        /** @var ObjectHydrator $hydrator */
372
        $hydrator = $this->hydratorMock;
373
374
        return new RequestProcessor(
375
            $repository,
376
            $validator,
377
            $parametersAssembler,
378
            ($useHydrator ? $hydrator : null)
379
        );
380
    }
381
382
    /**
383
     * @param array  $attributes
384
     *
385
     * @param string $content
386
     *
387
     * @return Request
388
     */
389
    private function createRequest(array $attributes, string $content = ''): Request
390
    {
391
        return new class($attributes, $content) extends Request
392
        {
393
            /**
394
             * @param array $attributes
395
             * @param array $content
396
             */
397
            public function __construct(array $attributes, $content)
398
            {
399
                parent::__construct();
400
                $this->attributes = new ParameterBag($attributes);
401
                $this->content    = $content;
0 ignored issues
show
Documentation Bug introduced by
It seems like $content of type array is incompatible with the declared type string|resource|false|null of property $content.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
402
            }
403
        };
404
    }
405
}
406