Issues (3)

tests/TrailingSlashControllerProviderTest.php (1 issue)

1
<?php
2
3
namespace Graze\Silex\Tests\ControllerProvider;
4
5
use Graze\Silex\ControllerProvider\TrailingSlashControllerProvider;
6
use Mockery;
7
use Pimple\ServiceProviderInterface;
8
use Psr\Log\LoggerInterface;
9
use Silex\Application;
10
use Silex\Api\ControllerProviderInterface;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\Routing\Matcher\UrlMatcher;
13
14
/**
15
 * TrailingSlashControllerProvider test cases.
16
 */
17
class TrailingSlashControllerProviderTest extends \PHPUnit_Framework_TestCase
18
{
19
    public function testShouldInitalize()
20
    {
21
        $provider = new TrailingSlashControllerProvider();
22
23
        $this->assertInstanceOf(ControllerProviderInterface::class, $provider);
24
        $this->assertInstanceOf(ServiceProviderInterface::class, $provider);
25
    }
26
27
    public function testShouldRegisterUrlMatcher()
28
    {
29
        $app = new Application();
30
31
        $app->register(new TrailingSlashControllerProvider());
32
33
        $this->assertInstanceOf(UrlMatcher::class, $app['request_matcher']);
34
    }
35
36
    public function testShouldMount()
37
    {
38
        $app = new Application();
39
40
        // `mount` should return the application.
41
        $this->assertSame(
42
            $app,
43
            $app->mount('/', new TrailingSlashControllerProvider())
44
        );
45
    }
46
47
    /**
48
     * @dataProvider requestMethodProvider
49
     *
50
     * @param string $method
51
     */
52
    public function testShouldRespondOkWithoutTrailingSlash($method)
53
    {
54
        $app = new Application();
55
56
        $app->match('/foo/', function () {
57
            return 'hunter42';
58
        })->method($method);
59
60
        $app->match('/foo/bar/', function () {
61
            return 'What\'s the question?';
62
        })->method($method);
63
64
        $app->match('/foo/bar/baz/', function () {
65
            return 'Fizz Buzz';
66
        })->method($method);
67
68
        $app->register(new TrailingSlashControllerProvider());
69
        $app->mount('/', new TrailingSlashControllerProvider());
70
71
        $request = Request::create('/foo', $method);
72
        $response = $app->handle($request);
73
74
        $this->assertEquals(200, $response->getStatusCode());
75
76
        $request = Request::create('/foo/bar', $method);
77
        $response = $app->handle($request);
78
79
        $this->assertEquals(200, $response->getStatusCode());
80
81
        $request = Request::create('/foo/bar/baz', $method);
82
        $response = $app->handle($request);
83
84
        $this->assertEquals(200, $response->getStatusCode());
85
    }
86
87
    /**
88
     * This just shows that the mount order for the controller provider doesn't
89
     * matter when all routes are defined with a trailing slash.
90
     *
91
     * @dataProvider requestMethodProvider
92
     *
93
     * @param string $method
94
     */
95
    public function testShouldRespondOkWithoutTrailingSlashWhenMountedFirst($method)
96
    {
97
        $app = new Application();
98
99
        $app->register(new TrailingSlashControllerProvider());
100
        $app->mount('/', new TrailingSlashControllerProvider());
101
102
        $app->match('/foo/', function () {
103
            return 'hunter42';
104
        })->method($method);
105
106
        $app->match('/foo/bar/', function () {
107
            return 'What\'s the question?';
108
        })->method($method);
109
110
        $app->match('/foo/bar/baz/', function () {
111
            return 'Fizz Buzz';
112
        })->method($method);
113
114
        $request = Request::create('/foo', $method);
115
        $response = $app->handle($request);
116
117
        $this->assertEquals(200, $response->getStatusCode());
118
119
        $request = Request::create('/foo/bar', $method);
120
        $response = $app->handle($request);
121
122
        $this->assertEquals(200, $response->getStatusCode());
123
124
        $request = Request::create('/foo/bar/baz', $method);
125
        $response = $app->handle($request);
126
127
        $this->assertEquals(200, $response->getStatusCode());
128
    }
129
130
    /**
131
     * This just shows that the controller provider is compatiable with other
132
     * controller providers.
133
     *
134
     * @dataProvider requestMethodProvider
135
     *
136
     * @param string $method
137
     */
138
    public function testShouldRespondOkWithoutTrailingSlashWithMountedControllers($method)
139
    {
140
        $app = new Application();
141
142
        $app->register(new TrailingSlashControllerProvider());
143
        $app->mount('/', new TrailingSlashControllerProvider());
144
145
        $controller = $app['controllers_factory'];
146
147
        $controller->match('/foo/', function () {
148
            return 'hunter42';
149
        })->method($method);
150
151
        $controller->match('/foo/bar/', function () {
152
            return 'hunter42';
153
        })->method($method);
154
155
        $controller->match('/foo/bar/baz/', function () {
156
            return 'hunter42';
157
        })->method($method);
158
159
        $provider = Mockery::mock(ControllerProviderInterface::class);
160
        $provider->shouldReceive('connect')->andReturn($controller);
161
162
        $app->mount('/', $provider);
163
164
        $request = Request::create('/foo', $method);
165
        $response = $app->handle($request);
166
167
        $this->assertEquals(200, $response->getStatusCode());
168
169
        $request = Request::create('/foo/bar', $method);
170
        $response = $app->handle($request);
171
172
        $this->assertEquals(200, $response->getStatusCode());
173
174
        $request = Request::create('/foo/bar/baz', $method);
175
        $response = $app->handle($request);
176
177
        $this->assertEquals(200, $response->getStatusCode());
178
    }
179
180
    /**
181
     * @return array
182
     */
183
    public function requestMethodProvider()
184
    {
185
        return [
186
            ['GET'],
187
            ['POST'],
188
            ['PUT'],
189
            ['PATCH'],
190
            ['DELETE'],
191
            ['PURGE'],
192
            ['OPTIONS'],
193
            ['TRACE'],
194
            ['CONNECT'],
195
        ];
196
    }
197
198
    public function testShouldRespondOkToHeadWithoutTrailingSlash()
199
    {
200
        $app = new Application();
201
202
        $app->get('/foo/', function () {
203
            return 'hunter42';
204
        });
205
206
        $app->get('/foo/bar/', function () {
207
            return 'What\'s the question?';
208
        });
209
210
        $app->get('/foo/bar/baz/', function () {
211
            return 'Fizz Buzz';
212
        });
213
214
        $app->register(new TrailingSlashControllerProvider());
215
        $app->mount('/', new TrailingSlashControllerProvider());
216
217
        $request = Request::create('/foo', 'HEAD');
218
        $response = $app->handle($request);
219
220
        $this->assertEquals(200, $response->getStatusCode());
221
222
        $request = Request::create('/foo/bar', 'HEAD');
223
        $response = $app->handle($request);
224
225
        $this->assertEquals(200, $response->getStatusCode());
226
227
        $request = Request::create('/foo/bar/baz', 'HEAD');
228
        $response = $app->handle($request);
229
230
        $this->assertEquals(200, $response->getStatusCode());
231
    }
232
233
    /**
234
     * This is just to show when defining routes that the trailing slash is
235
     * required when the controller provider is mounted before any other routes.
236
     *
237
     * @dataProvider requestMethodProvider
238
     *
239
     * @param string $method
240
     */
241
    public function testWillRespondWithNotFoundForRouteWithNoTrailingSlashWhenMountedFirst($method)
242
    {
243
        $app = new Application();
244
245
        $app->register(new TrailingSlashControllerProvider());
246
        $app->mount('/', new TrailingSlashControllerProvider());
247
248
        $app->match('/foo', function () {
249
            return 'hunter42';
250
        })->method($method);
251
252
        $app->match('/foo/bar', function () {
253
            return 'hunter42';
254
        })->method($method);
255
256
        $request = Request::create('/foo', $method);
257
        $response = $app->handle($request);
258
259
        $this->assertEquals(404, $response->getStatusCode());
260
261
        $request = Request::create('/foo/bar', $method);
262
        $response = $app->handle($request);
263
264
        $this->assertEquals(404, $response->getStatusCode());
265
    }
266
267
    /**
268
     * This shows that by default it will return 404 when the routes are defined with trailing slashes
269
     *
270
     * @dataProvider requestMethodProvider
271
     *
272
     * @param string $method
273
     */
274
    public function testWillRespondWillNotFoundForRouteWithTrailingSlashWhenNotMounted($method)
275
    {
276
        $app = new Application();
277
278
        $app->match('/foo/', function () {
279
            return 'hunter42';
280
        })->method($method);
281
282
        $app->match('/foo/bar/', function () {
283
            return 'hunter42';
284
        })->method($method);
285
286
        $app->register(new TrailingSlashControllerProvider());
287
288
        $request = Request::create('/foo', $method);
289
        $response = $app->handle($request);
290
291
        $this->assertEquals(404, $response->getStatusCode());
292
293
        $request = Request::create('/foo/bar', $method);
294
        $response = $app->handle($request);
295
296
        $this->assertEquals(404, $response->getStatusCode());
297
    }
298
299
    /**
300
     * This is just to show when defining routes with no trailing slash before
301
     * mounting the controller provider they should respond as expected.
302
     *
303
     * @dataProvider requestMethodProvider
304
     *
305
     * @param string $method
306
     */
307
    public function testWillRespondWithOkForRouteWithNoTrailingSlashWhenMountedLast($method)
308
    {
309
        $app = new Application();
310
311
        $app->match('/foo', function () {
312
            return 'hunter42';
313
        })->method($method);
314
315
        $app->match('/foo/bar', function () {
316
            return 'hunter42';
317
        })->method($method);
318
319
        $app->register(new TrailingSlashControllerProvider());
320
        $app->mount('/', new TrailingSlashControllerProvider());
321
322
        $request = Request::create('/foo', $method);
323
        $response = $app->handle($request);
324
325
        $this->assertEquals(200, $response->getStatusCode());
326
327
        $request = Request::create('/foo/bar', $method);
328
        $response = $app->handle($request);
329
330
        $this->assertEquals(200, $response->getStatusCode());
331
    }
332
333
    /**
334
     * Test the case in which a request should have both query
335
     * string params and body params
336
     */
337
    public function testWillHandleQueryAndBodySeparately()
338
    {
339
        $app = new Application();
340
341
        $app->match('/foo/', function (Request $request) {
342
            $response = [
343
                'query' => $request->query->all(),
344
                'request' => $request->request->all()
345
            ];
346
            return json_encode($response, true);
0 ignored issues
show
true of type true is incompatible with the type integer expected by parameter $options of json_encode(). ( Ignorable by Annotation )

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

346
            return json_encode($response, /** @scrutinizer ignore-type */ true);
Loading history...
347
        })->method('POST');
348
349
        $app->register(new TrailingSlashControllerProvider());
350
        $app->mount('/', new TrailingSlashControllerProvider());
351
352
        $request = Request::create('/foo?q=1', 'POST', ['r' => 2]);
353
        $response = $app->handle($request);
354
        $this->assertEquals(200, $response->getStatusCode());
355
        $body = json_decode($response->getContent(), true);
356
        $this->assertRequestQuery($body);
357
358
        $request = Request::create('/foo/?q=1', 'POST', ['r' => 2]);
359
        $response = $app->handle($request);
360
        $this->assertEquals(200, $response->getStatusCode());
361
        $body = json_decode($response->getContent(), true);
362
        $this->assertRequestQuery($body);
363
    }
364
365
    /**
366
     * @param array $body
367
     */
368
    private function assertRequestQuery(array $body)
369
    {
370
        $this->assertArrayHasKey('query', $body);
371
        $this->assertArrayHasKey('request', $body);
372
373
        $this->assertArrayHasKey('q', $body['query']);
374
        $this->assertEquals(1, $body['query']['q']);
375
376
        $this->assertArrayHasKey('r', $body['request']);
377
        $this->assertEquals(2, $body['request']['r']);
378
    }
379
380
    public function testLogging()
381
    {
382
        $app = new Application();
383
384
        $logger = Mockery::mock(LoggerInterface::class)->makePartial()->shouldIgnoreMissing();
385
        $app['logger'] = $logger;
386
387
        $app->match('/foo/', function () {
388
            return 'hunter42';
389
        })->method('GET');
390
391
        $logger->shouldReceive('debug')
392
               ->with('Appending a trailing slash for the request to `/foo`.')
393
               ->once();
394
        $logger->shouldReceive('debug')
395
               ->with('Overriding the default Silex url matcher to Symfony\Component\Routing\Matcher\UrlMatcher.')
396
               ->once();
397
398
        $app->register(new TrailingSlashControllerProvider());
399
        $app->mount('/', new TrailingSlashControllerProvider());
400
401
        $request = Request::create('/foo', 'GET');
402
        $response = $app->handle($request);
403
        $this->assertEquals(200, $response->getStatusCode());
404
    }
405
}
406