Completed
Pull Request — master (#92)
by Vitor
02:32
created

shouldCreateRoutesWithTheCorrectHttpSchemes()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 2
eloc 16
nc 2
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 SwaggerRouteLoaderTest 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
    protected function setUp()
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 loadingMultipleDocumentWillPreventRouteKeyCollisions()
94
    {
95
        $this->decriptionMock
96
            ->expects($this->any())
97
            ->method('getPaths')
98
            ->willReturn([
99
                new Path('/a', [new Operation('', '/a', 'get')]),
100
            ]);
101
102
        $routes1 = $this->loader->load(self::DOCUMENT_PATH);
103
        $routes2 = $this->loader->load(self::DOCUMENT_PATH . '2');
104
        $this->assertSame(count($routes1), count(array_diff_key($routes1->all(), $routes2->all())));
105
    }
106
107
    /**
108
     * @test
109
     * @expectedException \RuntimeException
110
     */
111 View Code Duplication
    public function cannotTryToLoadSameDocumentMoreThanOnce()
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...
112
    {
113
        $this->decriptionMock
114
            ->expects($this->any())
115
            ->method('getPaths')
116
            ->willReturn([]);
117
118
        $this->loader->load(self::DOCUMENT_PATH);
119
        $this->loader->load(self::DOCUMENT_PATH);
120
    }
121
122
    /**
123
     * @test
124
     */
125 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...
126
    {
127
        $this->decriptionMock
128
            ->expects($this->any())
129
            ->method('getPaths')
130
            ->willReturn([]);
131
132
        $routes = $this->loader->load(self::DOCUMENT_PATH);
133
        $this->assertInstanceOf('Symfony\Component\Routing\RouteCollection', $routes);
134
    }
135
136
    /**
137
     * @test
138
     */
139 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...
140
    {
141
        $this->decriptionMock
142
            ->expects($this->any())
143
            ->method('getPaths')
144
            ->willReturn([
145
                new Path('/a', [new Operation(uniqid(), '/a', 'get'), new Operation(uniqid(), '/a', 'post')]),
146
                new Path('/b', [new Operation(uniqid(), '/b', 'get')]),
147
            ]);
148
149
        $routes = $this->loader->load(self::DOCUMENT_PATH);
150
151
        $this->assertCount(3, $routes);
152
    }
153
154
    /**
155
     * @test
156
     */
157
    public function shouldCreateRoutesWithTheCorrectHttpSchemes()
158
    {
159
        $this->decriptionMock
160
            ->expects($this->any())
161
            ->method('getPaths')
162
            ->willReturn([
163
                new Path('/a', [
164
                    new Operation(uniqid(), '/a', 'get'),
165
                    new Operation(uniqid(), '/a', 'post')
166
                ]),
167
            ]);
168
169
        $this->decriptionMock
170
            ->expects($this->any())
171
            ->method('getSchemes')
172
            ->willReturn(['https', 'http']);
173
174
        $routes = $this->loader->load(self::DOCUMENT_PATH);
175
176
        $this->assertCount(2, $routes);
177
178
        foreach ($routes as $route) {
179
            $this->assertEquals(['https', 'http'], $route->getSchemes());
180
        }
181
    }
182
183
    /**
184
     * @test
185
     */
186 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...
187
    {
188
        $this->decriptionMock
189
            ->expects($this->any())
190
            ->method('getPaths')
191
            ->willReturn([
192
                new Path('/a', [new Operation(uniqid(), '/a', 'get')]),
193
                new Path('/a/b', [new Operation(uniqid(), '/a/b', 'get')]),
194
                new Path('/a/b/c', [new Operation(uniqid(), '/a/b/c', 'get')]),
195
            ]);
196
197
198
        $routes = $this->loader->load(self::DOCUMENT_PATH);
199
200
        $this->assertCount(3, $routes);
201
    }
202
203
    /**
204
     * @test
205
     */
206
    public function canUseOperationIdAsControllerKey()
207
    {
208
        $expected = 'my.controller.key:methodName';
209
210
        $this->decriptionMock
211
            ->expects($this->any())
212
            ->method('getPaths')
213
            ->willReturn([
214
                new Path('/a', [
215
                    new Operation('/a:get', '/a', 'get'),
216
                    new Operation($expected, '/a', 'post')
217
                ]),
218
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
219
            ]);
220
221
        $routes = $this->loader->load(self::DOCUMENT_PATH);
222
223
        $actual = $routes->get('swagger.path.a.methodName');
224
        $this->assertNotNull($actual);
225
        $this->assertSame($expected, $actual->getDefault('_controller'));
226
    }
227
228
    /**
229
     * @test
230
     */
231
    public function canUseXRouterMethodToOverrideMethod()
232
    {
233
        $extensions = ['router-controller-method' => 'myMethodName'];
234
235
        $this->decriptionMock
236
            ->expects($this->any())
237
            ->method('getPaths')
238
            ->willReturn([
239
                new Path('/a', [
240
                    new Operation('/a:get', '/a', 'get'),
241
                    new Operation('/a:post', '/a', 'post', [], null, [], $extensions)
242
                ]),
243
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
244
            ]);
245
246
        $routes = $this->loader->load(self::DOCUMENT_PATH);
247
248
        $actual = $routes->get('swagger.path.a.myMethodName');
249
        $this->assertNotNull($actual);
250
    }
251
252
    /**
253
     * @test
254
     */
255
    public function canUseXRouterControllerForDiKeyInOperation()
256
    {
257
        $diKey      = 'my.x_router.controller';
258
        $expected   = "$diKey:post";
259
        $extensions = ['router-controller' => $diKey];
260
        $this->decriptionMock
261
            ->expects($this->any())
262
            ->method('getPaths')
263
            ->willReturn([
264
                new Path('/a', [
265
                    new Operation('/a:get', '/a', 'get'),
266
                    new Operation('/a:post', '/a', 'post', [], null, [], $extensions)
267
                ]),
268
                new Path('/b', [new Operation('/b:get', '/b', 'get')]),
269
            ]);
270
271
        $routes = $this->loader->load(self::DOCUMENT_PATH);
272
273
        $actual = $routes->get('swagger.path.a.post');
274
        $this->assertNotNull($actual);
275
        $this->assertSame($expected, $actual->getDefault('_controller'));
276
    }
277
278
    /**
279
     * @test
280
     */
281 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...
282
    {
283
        $diKey    = 'my.x_router.controller';
284
        $expected = "$diKey:post";
285
        $this->decriptionMock
286
            ->expects($this->any())
287
            ->method('getPaths')
288
            ->willReturn([new Path('/a', [new Operation('/a:post', '/a', 'post')])]);
289
290
        $this->decriptionMock
291
            ->expects($this->atLeast(1))
292
            ->method('getExtension')
293
            ->willReturnCallback(function (string $name) use ($diKey) {
294
                return $name == 'router-controller' ? $diKey : null;
295
            });
296
297
        $routes = $this->loader->load(self::DOCUMENT_PATH);
298
299
        $actual = $routes->get('swagger.path.a.post');
300
        $this->assertNotNull($actual);
301
        $this->assertSame($expected, $actual->getDefault('_controller'));
302
    }
303
304
    /**
305
     * @test
306
     */
307 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...
308
    {
309
        $router   = 'my.x_router';
310
        $expected = "$router.a:post";
311
        $this->decriptionMock
312
            ->expects($this->any())
313
            ->method('getPaths')
314
            ->willReturn([new Path('/a', [new Operation('/a:post', '/a', 'post')])]);
315
316
        $this->decriptionMock
317
            ->expects($this->atLeast(1))
318
            ->method('getExtension')
319
            ->willReturnCallback(function (string $name) use ($router) {
320
                return $name == 'router' ? $router : null;
321
            });
322
323
        $routes = $this->loader->load(self::DOCUMENT_PATH);
324
325
        $actual = $routes->get('swagger.path.a.post');
326
        $this->assertNotNull($actual);
327
        $this->assertSame($expected, $actual->getDefault('_controller'));
328
    }
329
330
    /**
331
     * @test
332
     */
333
    public function routeCollectionWillIncludeSeparateRoutesForSubPathMethodCombinations()
334
    {
335
        $this->decriptionMock
336
            ->expects($this->any())
337
            ->method('getPaths')
338
            ->willReturn([
339
                new Path('/a', [
340
                    new Operation('/a:get', '/a', 'get'),
341
                ]),
342
                new Path('/a/b', [
343
                    new Operation('/a/b:get', '/a/b', 'get'),
344
                    new Operation('/a/b:post', '/a/b', 'post')
345
                ]),
346
                new Path('/a/b/c', [new Operation('/a/b/c:get', '/a/b/c', 'get')]),
347
            ]);
348
349
        $routes = $this->loader->load(self::DOCUMENT_PATH);
350
351
        $this->assertCount(4, $routes);
352
    }
353
354
    /**
355
     * @test
356
     */
357
    public function routeCollectionWillContainPathFromDescription()
358
    {
359
        $paths = [
360
            new Path('/a', [new Operation('/a:get', '/a', 'get'),]),
361
            new Path('/a/b', [new Operation('/a/b:get', '/a/b', 'get'),]),
362
            new Path('/a/b/c', [new Operation('/a/b/c:get', '/a/b/c', 'get')]),
363
            new Path('/d/f/g', [new Operation('/d/f/g:get', '/d/f/g', 'get')]),
364
            new Path('/1/2/3', [new Operation('/1/2/3:get', '/1/2/3', 'get')]),
365
            new Path('/foo/{bar}/{blah}', [new Operation('/foo/{bar}/{blah}:get', '/foo/{bar}/{blah}', 'get')]),
366
            new Path('/z', [new Operation('/z:get', '/z', 'get'),]),
367
        ];
368
369
        $this->decriptionMock
370
            ->expects($this->any())
371
            ->method('getPaths')
372
            ->willReturn($paths);
373
374
        $routes = $this->loader->load(self::DOCUMENT_PATH);
375
376
        $descriptionPaths = array_map(function (Path $path) {
377
            return $path->getPath();
378
        }, $paths);
379
        sort($descriptionPaths);
380
381
        $routePaths = array_map(function (Route $route) {
382
            return $route->getPath();
383
        }, $routes->getIterator()->getArrayCopy());
384
385
        sort($routePaths);
386
        $this->assertSame($descriptionPaths, $routePaths);
387
    }
388
389
    /**
390
     * @test
391
     */
392
    public function willAddRequirementsForIntegerPathParams()
393
    {
394
        $parameter = new Parameter(
395
            'foo',
396
            true,
397
            new ScalarSchema((object)['type' => Schema::TYPE_INT]),
398
            Parameter::IN_PATH
399
        );
400
401
        $this->decriptionMock
402
            ->expects($this->any())
403
            ->method('getPaths')
404
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
405
406
        $routes = $this->loader->load(self::DOCUMENT_PATH);
407
        $actual = $routes->get('swagger.path.a.get');
408
        $this->assertNotNull($actual);
409
        $requirements = $actual->getRequirements();
410
        $this->assertNotNull($requirements);
411
412
        $this->assertSame($requirements['foo'], '\d+');
413
    }
414
415
    /**
416
     * @test
417
     */
418
    public function willAddRequirementsForStringPatternParams()
419
    {
420
        $expected        = '\d{2}hello';
421
        $parameter = new Parameter(
422
            'aString',
423
            true,
424
            new ScalarSchema((object)[
425
                'type' => Schema::TYPE_STRING,
426
                'pattern' => $expected
427
            ]),
428
            Parameter::IN_PATH
429
        );
430
431
        $this->decriptionMock
432
            ->expects($this->any())
433
            ->method('getPaths')
434
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
435
436
        $routes = $this->loader->load(self::DOCUMENT_PATH);
437
        $actual = $routes->get('swagger.path.a.get');
438
        $this->assertNotNull($actual);
439
        $requirements = $actual->getRequirements();
440
        $this->assertNotNull($requirements);
441
442
        $this->assertSame($expected, $requirements['aString']);
443
    }
444
445
    /**
446
     * @test
447
     */
448
    public function willAddRequirementsForStringEnumParams()
449
    {
450
        $enum            = ['a', 'b', 'c'];
451
        $expected        = '(a|b|c)';
452
        $parameter = new Parameter(
453
            'aString',
454
            true,
455
            new ScalarSchema((object)[
456
                'type' => Schema::TYPE_STRING,
457
                'enum' => $enum
458
            ]),
459
            Parameter::IN_PATH
460
        );
461
462
        $this->decriptionMock
463
            ->expects($this->any())
464
            ->method('getPaths')
465
            ->willReturn([new Path('/a', [new Operation('/a:get', '/a', 'get', [$parameter])])]);
466
467
        $routes = $this->loader->load(self::DOCUMENT_PATH);
468
        $actual = $routes->get('swagger.path.a.get');
469
        $this->assertNotNull($actual);
470
        $requirements = $actual->getRequirements();
471
        $this->assertNotNull($requirements);
472
473
        $this->assertSame($expected, $requirements['aString']);
474
    }
475
}
476