Passed
Branch master (273046)
by Ch
03:49 queued 01:31
created

SimpleTraversable::valid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
c 0
b 0
f 0
rs 10
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
namespace HakimCh\Http\Tests;
3
4
use Exception;
5
use HakimCh\Http\Router;
6
use HakimCh\Http\RouterParser;
7
use Iterator;
8
use PHPUnit_Framework_TestCase;
9
use stdClass;
10
11
class RouterDebug extends Router
12
{
13
    public function getNamedRoutes()
14
    {
15
        return $this->namedRoutes;
16
    }
17
18
    public function getBasePath()
19
    {
20
        return $this->basePath;
21
    }
22
23
    public function setServer($server)
24
    {
25
        $this->server = $server;
26
    }
27
}
28
29
class SimpleTraversable implements Iterator
30
{
31
    protected $_position = 0;
32
33
    protected $_data = array(
34
        array('GET', '/foo', 'foo_action', null),
35
        array('POST', '/bar', 'bar_action', 'second_route')
36
    );
37
38
    public function current()
39
    {
40
        return $this->_data[$this->_position];
41
    }
42
    public function key()
43
    {
44
        return $this->_position;
45
    }
46
    public function next()
47
    {
48
        ++$this->_position;
49
    }
50
    public function rewind()
51
    {
52
        $this->_position = 0;
53
    }
54
    public function valid()
55
    {
56
        return isset($this->_data[$this->_position]);
57
    }
58
}
59
60
/**
61
 * Generated by PHPUnit_SkeletonGenerator 1.2.1 on 2013-07-14 at 17:47:46.
62
 */
63
class AltoRouterTest extends PHPUnit_Framework_TestCase
64
{
65
    /**
66
     * @var RouterDebug
67
     */
68
    protected $router;
69
70
    /**
71
     * Sets up the fixture, for example, opens a network connection.
72
     * This method is called before a test is executed.
73
     */
74
    protected function setUp()
0 ignored issues
show
Coding Style introduced by
setUp uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
75
    {
76
        $parser = new RouterParser();
77
        $this->router = new RouterDebug($parser, [], '', $_SERVER);
78
    }
79
80
    /**
81
     * @covers Router::getRoutes
82
     */
83
    public function testGetRoutes()
84
    {
85
        $method = 'POST';
86
        $route = '/[:controller]/[:action]';
87
        $target = function () {
88
        };
89
90
        $this->assertInternalType('array', $this->router->getRoutes());
91
        $this->router->map($method, $route, $target);
92
        $this->assertEquals(array(array($method, $route, $target, null)), $this->router->getRoutes());
93
    }
94
95
    /**
96
     * @covers Router::setRoutes
97
     */
98
    public function testAddRoutes()
99
    {
100
        $method = 'POST';
101
        $route = '/[:controller]/[:action]';
102
        $target = function () {
103
        };
104
        
105
        $this->router->setRoutes(array(
106
            array($method, $route, $target),
107
            array($method, $route, $target, 'second_route')
108
        ));
109
        
110
        $routes = $this->router->getRoutes();
111
        
112
        $this->assertEquals(array($method, $route, $target, null), $routes[0]);
113
        $this->assertEquals(array($method, $route, $target, 'second_route'), $routes[1]);
114
    }
115
116
    /**
117
     * @covers Router::setRoutes
118
     */
119
    public function testAddRoutesAcceptsTraverable()
120
    {
121
        $traversable = new SimpleTraversable();
122
        $this->router->setRoutes($traversable);
123
        
124
        $traversable->rewind();
125
        
126
        $first = $traversable->current();
127
        $traversable->next();
128
        $second = $traversable->current();
129
        
130
        $routes = $this->router->getRoutes();
131
        
132
        $this->assertEquals($first, $routes[0]);
133
        $this->assertEquals($second, $routes[1]);
134
    }
135
136
    /**
137
     * @covers Router::setRoutes
138
     * @expectedException Exception
139
     */
140
    public function testAddRoutesThrowsExceptionOnInvalidArgument()
141
    {
142
        $this->router->setRoutes(new stdClass);
0 ignored issues
show
Bug introduced by
new stdClass() of type stdClass is incompatible with the type Traversable|array expected by parameter $routes of HakimCh\Http\Router::setRoutes(). ( Ignorable by Annotation )

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

142
        $this->router->setRoutes(/** @scrutinizer ignore-type */ new stdClass);
Loading history...
143
    }
144
145
    /**
146
     * @covers AltoRouter::setBasePath
147
     */
148
    public function testSetBasePath()
149
    {
150
        $this->router->setBasePath('/some/path');
151
        $this->assertEquals('/some/path', $this->router->getBasePath());
152
        
153
        $this->router->setBasePath('/some/path');
154
        $this->assertEquals('/some/path', $this->router->getBasePath());
155
    }
156
157
    /**
158
     * @covers Router::map
159
     */
160
    public function testMap()
161
    {
162
        $method = 'POST';
163
        $route = '/[:controller]/[:action]';
164
        $target = function () {
165
        };
166
        
167
        $this->router->map($method, $route, $target);
168
        
169
        $routes = $this->router->getRoutes();
170
        
171
        $this->assertEquals(array($method, $route, $target, null), $routes[0]);
172
    }
173
174
    /**
175
     * @covers Router::map
176
     */
177
    public function testMapWithName()
178
    {
179
        $method = 'POST';
180
        $route = '/[:controller]/[:action]';
181
        $target = function () {
182
        };
183
        $name = 'myroute';
184
        
185
        $this->router->map($method, $route, $target, $name);
186
        
187
        $routes = $this->router->getRoutes();
188
        $this->assertEquals(array($method, $route, $target, $name), $routes[0]);
189
        
190
        $named_routes = $this->router->getNamedRoutes();
191
        $this->assertEquals($route, $named_routes[$name]);
192
        
193
        try {
194
            $this->router->map($method, $route, $target, $name);
195
            $this->fail('Should not be able to add existing named route');
196
        } catch (Exception $e) {
197
            $this->assertEquals("Can not redeclare route '{$name}'", $e->getMessage());
198
        }
199
    }
200
201
202
    /**
203
     * @covers Router::generate
204
     */
205 View Code Duplication
    public function testGenerate()
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...
206
    {
207
        $params = array(
208
            'controller' => 'test',
209
            'action' => 'someaction'
210
        );
211
        
212
        $this->router->map('GET', '/[:controller]/[:action]', function () {
213
        }, 'foo_route');
214
        
215
        $this->assertEquals(
216
        
217
            '/test/someaction',
218
            $this->router->generate('foo_route', $params)
219
        
220
        );
221
        
222
        $params = array(
223
            'controller' => 'test',
224
            'action' => 'someaction',
225
            'type' => 'json'
226
        );
227
        
228
        $this->assertEquals(
229
        
230
            '/test/someaction',
231
            $this->router->generate('foo_route', $params)
232
        
233
        );
234
    }
235
236 View Code Duplication
    public function testGenerateWithOptionalUrlParts()
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...
237
    {
238
        $this->router->map('GET', '/[:controller]/[:action].[:type]?', function () {
239
        }, 'bar_route');
240
        
241
        $params = array(
242
            'controller' => 'test',
243
            'action' => 'someaction'
244
        );
245
        
246
        $this->assertEquals(
247
        
248
            '/test/someaction',
249
            $this->router->generate('bar_route', $params)
250
        
251
        );
252
        
253
        $params = array(
254
            'controller' => 'test',
255
            'action' => 'someaction',
256
            'type' => 'json'
257
        );
258
        
259
        $this->assertEquals(
260
        
261
            '/test/someaction.json',
262
            $this->router->generate('bar_route', $params)
263
        
264
        );
265
    }
266
    
267
    public function testGenerateWithNonexistingRoute()
268
    {
269
        try {
270
            $this->router->generate('nonexisting_route');
271
            $this->fail('Should trigger an exception on nonexisting named route');
272
        } catch (Exception $e) {
273
            $this->assertEquals("Route 'nonexisting_route' does not exist.", $e->getMessage());
274
        }
275
    }
276
    
277
    /**
278
     * @covers Router::match
279
     * @covers Router::compileRoute
280
     */
281
    public function testMatch()
282
    {
283
        $this->router->map('GET', '/foo/[:controller]/[:action]', 'foo_action', 'foo_route');
284
        
285
        $this->assertEquals(array(
286
            'target' => 'foo_action',
287
            'params' => array(
288
                'controller' => 'test',
289
                'action' => 'do'
290
            ),
291
            'name' => 'foo_route'
292
        ), $this->router->match('/foo/test/do', 'GET'));
293
        
294
        $this->assertFalse($this->router->match('/foo/test/do', 'POST'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/foo/test/do', 'POST') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

294
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/foo/test/do', 'POST'));
Loading history...
295
        
296
        $this->assertEquals(array(
297
            'target' => 'foo_action',
298
            'params' => array(
299
                'controller' => 'test',
300
                'action' => 'do'
301
            ),
302
            'name' => 'foo_route'
303
        ), $this->router->match('/foo/test/do?param=value', 'GET'));
304
    }
305
    
306
    public function testMatchWithFixedParamValues()
307
    {
308
        $this->router->map('POST', '/users/[i:id]/[delete|update:action]', 'usersController#doAction', 'users_do');
309
        
310
        $this->assertEquals(array(
311
            'target' => 'usersController#doAction',
312
            'params' => array(
313
                'id' => 1,
314
                'action' => 'delete'
315
            ),
316
            'name' => 'users_do'
317
        ), $this->router->match('/users/1/delete', 'POST'));
318
        
319
        $this->assertFalse($this->router->match('/users/1/delete', 'GET'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/users/1/delete', 'GET') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

319
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/users/1/delete', 'GET'));
Loading history...
320
        $this->assertFalse($this->router->match('/users/abc/delete', 'POST'));
321
        $this->assertFalse($this->router->match('/users/1/create', 'GET'));
322
    }
323
    
324 View Code Duplication
    public function testMatchWithServerVars()
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...
Coding Style introduced by
testMatchWithServerVars uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
325
    {
326
        $this->router->map('GET', '/foo/[:controller]/[:action]', 'foo_action', 'foo_route');
327
        
328
        $_SERVER['REQUEST_URI'] = '/foo/test/do';
329
        $_SERVER['REQUEST_METHOD'] = 'GET';
330
331
        $this->router->setServer($_SERVER);
332
        
333
        $this->assertEquals(array(
334
            'target' => 'foo_action',
335
            'params' => array(
336
                'controller' => 'test',
337
                'action' => 'do'
338
            ),
339
            'name' => 'foo_route'
340
        ), $this->router->match());
341
    }
342
    
343
    public function testMatchWithOptionalUrlParts()
344
    {
345
        $this->router->map('GET', '/bar/[:controller]/[:action].[:type]?', 'bar_action', 'bar_route');
346
        
347
        $this->assertEquals(array(
348
            'target' => 'bar_action',
349
            'params' => array(
350
                'controller' => 'test',
351
                'action' => 'do',
352
                'type' => 'json'
353
            ),
354
            'name' => 'bar_route'
355
        ), $this->router->match('/bar/test/do.json', 'GET'));
356
    }
357
    
358 View Code Duplication
    public function testMatchWithWildcard()
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...
359
    {
360
        $this->router->map('GET', '/a', 'foo_action', 'foo_route');
361
        $this->router->map('GET', '*', 'bar_action', 'bar_route');
362
        
363
        $this->assertEquals(array(
364
            'target' => 'bar_action',
365
            'params' => array(),
366
            'name' => 'bar_route'
367
        ), $this->router->match('/everything', 'GET'));
368
    }
369
    
370 View Code Duplication
    public function testMatchWithCustomRegexp()
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...
371
    {
372
        $this->router->map('GET', '@^/[a-z]*$', 'bar_action', 'bar_route');
373
        
374
        $this->assertEquals(array(
375
            'target' => 'bar_action',
376
            'params' => array(),
377
            'name' => 'bar_route'
378
        ), $this->router->match('/everything', 'GET'));
379
        
380
        $this->assertFalse($this->router->match('/some-other-thing', 'GET'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/some-other-thing', 'GET') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

380
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/some-other-thing', 'GET'));
Loading history...
381
    }
382
383
    public function testMatchWithUnicodeRegex()
384
    {
385
        $pattern = '/(?<path>[^';
386
        // Arabic characters
387
        $pattern .= '\x{0600}-\x{06FF}';
388
        $pattern .= '\x{FB50}-\x{FDFD}';
389
        $pattern .= '\x{FE70}-\x{FEFF}';
390
        $pattern .= '\x{0750}-\x{077F}';
391
        // Alphanumeric, /, _, - and space characters
392
        $pattern .= 'a-zA-Z0-9\/_\-\s';
393
        // 'ZERO WIDTH NON-JOINER'
394
        $pattern .= '\x{200C}';
395
        $pattern .= ']+)';
396
        
397
        $this->router->map('GET', '@' . $pattern, 'unicode_action', 'unicode_route');
398
        
399
        $this->assertEquals(array(
400
            'target' => 'unicode_action',
401
            'name' => 'unicode_route',
402
            'params' => array(
403
                'path' => '大家好'
404
            )
405
        ), $this->router->match('/大家好', 'GET'));
406
        
407
        $this->assertFalse($this->router->match('/﷽‎', 'GET'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/﷽‎', 'GET') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

407
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/﷽‎', 'GET'));
Loading history...
408
    }
409
410
    /**
411
     * @covers Router::setMatchTypes
412
     */
413
    public function testMatchWithCustomNamedRegex()
414
    {
415
        $this->router->getParser()->setMatchTypes(array('cId' => '[a-zA-Z]{2}[0-9](?:_[0-9]++)?'));
416
        $this->router->map('GET', '/bar/[cId:customId]', 'bar_action', 'bar_route');
417
        
418
        $this->assertEquals(array(
419
            'target' => 'bar_action',
420
            'params' => array(
421
                'customId' => 'AB1',
422
            ),
423
            'name' => 'bar_route'
424
        ), $this->router->match('/bar/AB1', 'GET'));
425
426
        $this->assertEquals(array(
427
            'target' => 'bar_action',
428
            'params' => array(
429
                'customId' => 'AB1_0123456789',
430
            ),
431
            'name' => 'bar_route'
432
        ), $this->router->match('/bar/AB1_0123456789', 'GET'));
433
        
434
        $this->assertFalse($this->router->match('/some-other-thing', 'GET'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/some-other-thing', 'GET') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

434
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/some-other-thing', 'GET'));
Loading history...
435
    }
436
437
    public function testMatchWithCustomNamedUnicodeRegex()
438
    {
439
        $pattern = '[^';
440
        // Arabic characters
441
        $pattern .= '\x{0600}-\x{06FF}';
442
        $pattern .= '\x{FB50}-\x{FDFD}';
443
        $pattern .= '\x{FE70}-\x{FEFF}';
444
        $pattern .= '\x{0750}-\x{077F}';
445
        $pattern .= ']+';
446
        
447
        $this->router->getParser()->setMatchTypes(array('nonArabic' => $pattern));
448
        $this->router->map('GET', '/bar/[nonArabic:string]', 'non_arabic_action', 'non_arabic_route');
449
450
        $this->assertEquals(array(
451
            'target' => 'non_arabic_action',
452
            'name'   => 'non_arabic_route',
453
            'params' => array(
454
                'string' => 'some-path'
455
            )
456
        ), $this->router->match('/bar/some-path', 'GET'));
457
        
458
        $this->assertFalse($this->router->match('/﷽‎', 'GET'));
0 ignored issues
show
Bug introduced by
It seems like $this->router->match('/﷽‎', 'GET') can also be of type array; however, parameter $condition of PHPUnit_Framework_Assert::assertFalse() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

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

458
        $this->assertFalse(/** @scrutinizer ignore-type */ $this->router->match('/﷽‎', 'GET'));
Loading history...
459
    }
460
}
461