Completed
Push — master ( f730e7...231cf5 )
by John
03:07
created

canUseXRouterControllerForDiKeyInOperation()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 1
eloc 17
nc 1
nop 0
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\Routing;
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\ScalarSchema;
17
use KleijnWeb\PhpApi\Descriptions\Description\Schema\Schema;
18
use KleijnWeb\SwaggerBundle\Routing\OpenApiRouteLoader;
19
use Symfony\Component\Routing\Route;
20
21
/**
22
 * @author John Kleijn <[email protected]>
23
 */
24
class OpenApiRouteLoaderTest extends \PHPUnit_Framework_TestCase
25
{
26
    const DOCUMENT_PATH = '/totally/non-existent/path';
27
28
    /**
29
     * @var \PHPUnit_Framework_MockObject_MockObject
30
     */
31
    private $repositoryMock;
32
33
    /**
34
     * @var \PHPUnit_Framework_MockObject_MockObject
35
     */
36
    private $decriptionMock;
37
38
    /**
39
     * @var OpenApiRouteLoader
40
     */
41
    private $loader;
42
43
    /**
44
     * Create mocks
45
     */
46 View Code Duplication
    protected function setUp()
0 ignored issues
show
Duplication introduced by
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...
47
    {
48
        $this->decriptionMock = $this
49
            ->getMockBuilder(Description::class)
50
            ->disableOriginalConstructor()
51
            ->getMock();
52
53
        /** @var Repository $repository */
54
        $this->repositoryMock = $repository = $this
55
            ->getMockBuilder(Repository::class)
56
            ->disableOriginalConstructor()
57
            ->getMock();
58
59
        $this->repositoryMock
60
            ->expects($this->any())
61
            ->method('get')
62
            ->willReturn($this->decriptionMock);
63
64
        $this->loader = new OpenApiRouteLoader($repository);
65
    }
66
67
    /**
68
     * @test
69
     */
70
    public function supportSwaggerAsRouteTypeOnly()
71
    {
72
        $this->assertFalse($this->loader->supports('/a/b/c'));
73
        $this->assertTrue($this->loader->supports('/a/b/c', 'swagger'));
74
    }
75
76
    /**
77
     * @test
78
     */
79 View Code Duplication
    public function canLoadMultipleDocuments()
0 ignored issues
show
Duplication introduced by
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...
80
    {
81
        $this->decriptionMock
82
            ->expects($this->any())
83
            ->method('getPaths')
84
            ->willReturn([]);
85
86
        $this->loader->load(self::DOCUMENT_PATH);
87
        $this->loader->load(self::DOCUMENT_PATH.'2');
88
    }
89
90
    /**
91
     * @test
92
     */
93
    public function loadingMultipleDocumentsWillPreventRouteKeyCollisions()
94
    {
95
        $this->decriptionMock
96
            ->expects($this->any())
97
            ->method('getPaths')
98
            ->willReturn(
99
                [
100
                    new Path('/a', [new Operation('', '/a', 'get')]),
101
                ]
102
            );
103
104
        $routes1 = $this->loader->load(self::DOCUMENT_PATH);
105
        $routes2 = $this->loader->load(self::DOCUMENT_PATH.'2');
106
        $this->assertSame(count($routes1), count(array_diff_key($routes1->all(), $routes2->all())));
107
    }
108
109
    /**
110
     * @test
111
     * @expectedException \RuntimeException
112
     */
113 View Code Duplication
    public function cannotLoadSameDocumentMoreThanOnce()
0 ignored issues
show
Duplication introduced by
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...
114
    {
115
        $this->decriptionMock
116
            ->expects($this->any())
117
            ->method('getPaths')
118
            ->willReturn([]);
119
120
        $this->loader->load(self::DOCUMENT_PATH);
121
        $this->loader->load(self::DOCUMENT_PATH);
122
    }
123
124
    /**
125
     * @test
126
     */
127 View Code Duplication
    public function willReturnRouteCollection()
0 ignored issues
show
Duplication introduced by
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...
128
    {
129
        $this->decriptionMock
130
            ->expects($this->any())
131
            ->method('getPaths')
132
            ->willReturn([]);
133
134
        $routes = $this->loader->load(self::DOCUMENT_PATH);
135
        $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $routes);
136
    }
137
138
    /**
139
     * @test
140
     */
141 View Code Duplication
    public function routeCollectionWillContainOneRouteForEveryPathAndMethod()
0 ignored issues
show
Duplication introduced by
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...
142
    {
143
        $this->decriptionMock
144
            ->expects($this->any())
145
            ->method('getPaths')
146
            ->willReturn(
147
                [
148
                    new Path('/a', [new Operation(uniqid(), '/a', 'get'), new Operation(uniqid(), '/a', 'post')]),
149
                    new Path('/b', [new Operation(uniqid(), '/b', 'get')]),
150
                ]
151
            );
152
153
        $routes = $this->loader->load(self::DOCUMENT_PATH);
154
155
        $this->assertCount(3, $routes);
156
    }
157
158
    /**
159
     * @test
160
     */
161
    public function shouldCreateRoutesWithTheCorrectHttpSchemes()
162
    {
163
        $this->decriptionMock
164
            ->expects($this->any())
165
            ->method('getPaths')
166
            ->willReturn([
167
                new Path('/a', [
168
                    new Operation(uniqid(), '/a', 'get'),
169
                    new Operation(uniqid(), '/a', 'post'),
170
                ]),
171
            ]);
172
173
        $this->decriptionMock
174
            ->expects($this->any())
175
            ->method('getSchemes')
176
            ->willReturn(['https', 'http']);
177
178
        $routes = $this->loader->load(self::DOCUMENT_PATH);
179
180
        $this->assertCount(2, $routes);
181
182
        foreach ($routes as $route) {
183
            $this->assertEquals(['https', 'http'], $route->getSchemes());
184
        }
185
    }
186
187
    /**
188
     * @test
189
     */
190 View Code Duplication
    public function routeCollectionWillIncludeSeparateRoutesForSubPaths()
0 ignored issues
show
Duplication introduced by
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...
191
    {
192
        $this->decriptionMock
193
            ->expects($this->any())
194
            ->method('getPaths')
195
            ->willReturn(
196
                [
197
                    new Path('/a', [new Operation(uniqid(), '/a', 'get')]),
198
                    new Path('/a/b', [new Operation(uniqid(), '/a/b', 'get')]),
199
                    new Path('/a/b/c', [new Operation(uniqid(), '/a/b/c', 'get')]),
200
                ]
201
            );
202
203
204
        $routes = $this->loader->load(self::DOCUMENT_PATH);
205
206
        $this->assertCount(3, $routes);
207
    }
208
209
    /**
210
     * @test
211
     */
212
    public function canUseOperationIdAsControllerKey()
213
    {
214
        $expected = 'my.controller.key:methodName';
215
216
        $this->decriptionMock
217
            ->expects($this->any())
218
            ->method('getPaths')
219
            ->willReturn([
220
                new Path(
221
                    '/a',
222
                    [new Operation('/a:get', '/a', 'get'), new Operation($expected, '/a', 'post'),]
223
                ),
224
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
225
            ]);
226
227
        $routes = $this->loader->load(self::DOCUMENT_PATH);
228
229
        $actual = $routes->get('swagger.path.a.methodName');
230
        $this->assertNotNull($actual);
231
        $this->assertSame($expected, $actual->getDefault('_controller'));
232
    }
233
234
    /**
235
     * @test
236
     */
237
    public function canUseXRouterMethodToOverrideMethod()
238
    {
239
        $extensions = ['router-controller-method' => 'myMethodName'];
240
241
        $this->decriptionMock
242
            ->expects($this->any())
243
            ->method('getPaths')
244
            ->willReturn([
245
                new Path(
246
                    '/a',
247
                    [
248
                        new Operation('/a:get', '/a', 'get'),
249
                        new Operation('/a:post', '/a', 'post', [], null, [], $extensions),
250
                    ]
251
                ),
252
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
253
            ]);
254
255
        $routes = $this->loader->load(self::DOCUMENT_PATH);
256
257
        $actual = $routes->get('swagger.path.a.myMethodName');
258
        $this->assertNotNull($actual);
259
    }
260
261
    /**
262
     * @test
263
     */
264
    public function canUseXRouterControllerForDiKeyInOperation()
265
    {
266
        $diKey      = 'my.x_router.controller';
267
        $expected   = "$diKey:post";
268
        $extensions = ['router-controller' => $diKey];
269
        $this->decriptionMock
270
            ->expects($this->any())
271
            ->method('getPaths')
272
            ->willReturn([
273
                new Path(
274
                    '/a',
275
                    [
276
                        new Operation('/a:get', '/a', 'get'),
277
                        new Operation('/a:post', '/a', 'post', [], null, [], $extensions),
278
                    ]
279
                ),
280
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
281
            ]);
282
283
        $routes = $this->loader->load(self::DOCUMENT_PATH);
284
285
        $actual = $routes->get('swagger.path.a.post');
286
        $this->assertNotNull($actual);
287
        $this->assertSame($expected, $actual->getDefault('_controller'));
288
    }
289
290
    /**
291
     * @test
292
     */
293 View Code Duplication
    public function canUseXRouterControllerForDiKeyInPath()
0 ignored issues
show
Duplication introduced by
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...
294
    {
295
        $diKey    = 'my.x_router.controller';
296
        $expected = "$diKey:post";
297
        $this->decriptionMock
298
            ->expects($this->any())
299
            ->method('getPaths')
300
            ->willReturn([new Path('/a', [new Operation('/a:post', '/a', 'post')])]);
301
302
        $this->decriptionMock
303
            ->expects($this->atLeast(1))
304
            ->method('getExtension')
305
            ->willReturnCallback(
306
                function (string $name) use ($diKey) {
307
                    return $name == 'router-controller' ? $diKey : null;
308
                }
309
            );
310
311
        $routes = $this->loader->load(self::DOCUMENT_PATH);
312
313
        $actual = $routes->get('swagger.path.a.post');
314
        $this->assertNotNull($actual);
315
        $this->assertSame($expected, $actual->getDefault('_controller'));
316
    }
317
318
    /**
319
     * @test
320
     */
321 View Code Duplication
    public function canUseXRouterForDiKeyInPath()
0 ignored issues
show
Duplication introduced by
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...
322
    {
323
        $router   = 'my.x_router';
324
        $expected = "$router.a:post";
325
        $this->decriptionMock
326
            ->expects($this->any())
327
            ->method('getPaths')
328
            ->willReturn([new Path('/a', [new Operation('/a:post', '/a', 'post')])]);
329
330
        $this->decriptionMock
331
            ->expects($this->atLeast(1))
332
            ->method('getExtension')
333
            ->willReturnCallback(
334
                function (string $name) use ($router) {
335
                    return $name == 'router' ? $router : null;
336
                }
337
            );
338
339
        $routes = $this->loader->load(self::DOCUMENT_PATH);
340
341
        $actual = $routes->get('swagger.path.a.post');
342
        $this->assertNotNull($actual);
343
        $this->assertSame($expected, $actual->getDefault('_controller'));
344
    }
345
346
    /**
347
     * @test
348
     */
349
    public function routeCollectionWillIncludeSeparateRoutesForSubPathMethodCombinations()
350
    {
351
        $this->decriptionMock
352
            ->expects($this->any())
353
            ->method('getPaths')
354
            ->willReturn([
355
                new Path(
356
                    '/a',
357
                    [new Operation('/a:get', '/a', 'get')]
358
                ),
359
                new Path(
360
                    '/a/b',
361
                    [new Operation('/a/b:get', '/a/b', 'get'), new Operation('/a/b:post', '/a/b', 'post')]
362
                ),
363
                new Path('/a/b/c', [new Operation('/a/b/c:get', '/a/b/c', 'get')]),
364
            ]);
365
366
        $routes = $this->loader->load(self::DOCUMENT_PATH);
367
368
        $this->assertCount(4, $routes);
369
    }
370
371
    /**
372
     * @test
373
     */
374
    public function routeCollectionWillContainPathFromDescription()
375
    {
376
        $paths = [
377
            new Path('/a', [new Operation('/a:get', '/a', 'get'),]),
378
            new Path('/a/b', [new Operation('/a/b:get', '/a/b', 'get'),]),
379
            new Path('/a/b/c', [new Operation('/a/b/c:get', '/a/b/c', 'get')]),
380
            new Path('/d/f/g', [new Operation('/d/f/g:get', '/d/f/g', 'get')]),
381
            new Path('/1/2/3', [new Operation('/1/2/3:get', '/1/2/3', 'get')]),
382
            new Path('/foo/{bar}/{blah}', [new Operation('/foo/{bar}/{blah}:get', '/foo/{bar}/{blah}', 'get')]),
383
            new Path('/z', [new Operation('/z:get', '/z', 'get'),]),
384
        ];
385
386
        $this->decriptionMock
387
            ->expects($this->any())
388
            ->method('getPaths')
389
            ->willReturn($paths);
390
391
        $routes = $this->loader->load(self::DOCUMENT_PATH);
392
393
        $descriptionPaths = array_map(
394
            function (Path $path) {
395
                return $path->getPath();
396
            },
397
            $paths
398
        );
399
        sort($descriptionPaths);
400
401
        $routePaths = array_map(
402
            function (Route $route) {
403
                return $route->getPath();
404
            },
405
            $routes->getIterator()->getArrayCopy()
406
        );
407
408
        sort($routePaths);
409
        $this->assertSame($descriptionPaths, $routePaths);
410
    }
411
412
    /**
413
     * @test
414
     */
415
    public function willAddRequirementsForIntegerPathParams()
416
    {
417
        $parameter = new Parameter(
418
            'foo',
419
            true,
420
            new ScalarSchema((object)['type' => Schema::TYPE_INT]),
421
            Parameter::IN_PATH
422
        );
423
424
        $this->decriptionMock
425
            ->expects($this->any())
426
            ->method('getPaths')
427
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
428
429
        $routes = $this->loader->load(self::DOCUMENT_PATH);
430
        $actual = $routes->get('swagger.path.a.get');
431
        $this->assertNotNull($actual);
432
        $requirements = $actual->getRequirements();
433
        $this->assertNotNull($requirements);
434
435
        $this->assertSame($requirements['foo'], '\d+');
436
    }
437
438
    /**
439
     * @test
440
     */
441
    public function willAddRequirementsForStringPatternParams()
442
    {
443
        $expected  = '\d{2}hello';
444
        $parameter = new Parameter(
445
            'aString',
446
            true,
447
            new ScalarSchema(
448
                (object)[
449
                    'type'    => Schema::TYPE_STRING,
450
                    'pattern' => $expected,
451
                ]
452
            ),
453
            Parameter::IN_PATH
454
        );
455
456
        $this->decriptionMock
457
            ->expects($this->any())
458
            ->method('getPaths')
459
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
460
461
        $routes = $this->loader->load(self::DOCUMENT_PATH);
462
        $actual = $routes->get('swagger.path.a.get');
463
        $this->assertNotNull($actual);
464
        $requirements = $actual->getRequirements();
465
        $this->assertNotNull($requirements);
466
467
        $this->assertSame($expected, $requirements['aString']);
468
    }
469
470
    /**
471
     * @test
472
     */
473
    public function willAddRequirementsForStringEnumParams()
474
    {
475
        $enum      = ['a', 'b', 'c'];
476
        $expected  = '(a|b|c)';
477
        $parameter = new Parameter(
478
            'aString',
479
            true,
480
            new ScalarSchema(
481
                (object)[
482
                    'type' => Schema::TYPE_STRING,
483
                    'enum' => $enum,
484
                ]
485
            ),
486
            Parameter::IN_PATH
487
        );
488
489
        $this->decriptionMock
490
            ->expects($this->any())
491
            ->method('getPaths')
492
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
493
494
        $routes = $this->loader->load(self::DOCUMENT_PATH);
495
        $actual = $routes->get('swagger.path.a.get');
496
        $this->assertNotNull($actual);
497
        $requirements = $actual->getRequirements();
498
        $this->assertNotNull($requirements);
499
500
        $this->assertSame($expected, $requirements['aString']);
501
    }
502
}
503