Passed
Push — master ( 604c83...a44f99 )
by Ingo
06:41
created

DirectorTest   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 1007
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1007
rs 7.9312
c 0
b 0
f 0
wmc 46

37 Methods

Rating   Name   Duplication   Size   Complexity  
A setUpRoutes() 0 4 1
A getExtraRoutes() 0 10 1
A setUp() 0 11 1
A testFileExists() 0 19 1
B testAbsoluteURL() 0 133 2
A testIsAbsolute() 0 16 2
A testMakeRelative() 0 8 1
B providerMakeRelative() 0 110 1
A testIsRelativeUrl() 0 11 1
A testIsSiteUrl() 0 11 1
A testHostPortParts() 0 5 1
A testAlternativeBaseURL() 0 60 1
A testIsAbsoluteUrl() 0 16 1
A providerTestTestRequestCarriesGlobals() 0 12 3
A testForceSSLAlternateDomain() 0 8 1
A testTestIgnoresHashes() 0 17 1
A testForceSSLOnSubPagesPattern() 0 8 1
B testResetGlobalsAfterTestRequest() 0 35 1
A testRouteParams() 0 14 1
A testPromisedForceSSL() 0 20 1
A expectExceptionRedirect() 0 3 1
A testPromisedForceWWW() 0 19 1
A testForceSSLOnTopLevelPagePattern() 0 8 1
A testIsCli() 0 3 1
A testTestRequestCarriesGlobals() 0 14 1
B testGlobalMiddleware() 0 29 1
A testRouteSpecificMiddleware() 0 51 1
A testForceWWW() 0 7 1
A runTest() 0 16 4
A testForceSSLAlternateDomainWithPort() 0 8 1
B testIsHttps() 0 86 3
A testForceSSLandForceWWW() 0 21 1
A testUnmatchedRequestReturns404() 0 4 1
B testMockRequest() 0 24 1
B testRequestFilterInDirectorTest() 0 32 1
A testForceSSLWithPatternDoesNotMatchOtherPages() 0 15 1
A testForceSSLProtectsEntireSite() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like DirectorTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DirectorTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace SilverStripe\Control\Tests;
3
4
use SilverStripe\Control\Cookie_Backend;
5
use SilverStripe\Control\Director;
6
use SilverStripe\Control\HTTPRequest;
7
use SilverStripe\Control\HTTPResponse;
8
use SilverStripe\Control\HTTPResponse_Exception;
9
use SilverStripe\Control\Middleware\CanonicalURLMiddleware;
10
use SilverStripe\Control\Middleware\RequestHandlerMiddlewareAdapter;
11
use SilverStripe\Control\Middleware\TrustedProxyMiddleware;
12
use SilverStripe\Control\RequestProcessor;
13
use SilverStripe\Control\Tests\DirectorTest\TestController;
14
use SilverStripe\Core\Config\Config;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Core\Kernel;
17
use SilverStripe\Dev\SapphireTest;
18
19
/**
20
 * @todo test Director::alternateBaseFolder()
21
 */
22
class DirectorTest extends SapphireTest
23
{
24
    protected static $extra_controllers = [
25
        TestController::class,
26
    ];
27
28
    protected function setUp()
29
    {
30
        parent::setUp();
31
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/');
32
33
        // Ensure redirects enabled on all environments and global state doesn't affect the tests
34
        CanonicalURLMiddleware::singleton()
35
            ->setForceSSLDomain(null)
36
            ->setForceSSLPatterns([])
37
            ->setEnabledEnvs(true);
38
        $this->expectedRedirect = null;
39
    }
40
41
    protected function getExtraRoutes()
42
    {
43
        $rules = parent::getExtraRoutes();
44
45
        $rules['DirectorTestRule/$Action/$ID/$OtherID'] = TestController::class;
46
        $rules['en-nz/$Action/$ID/$OtherID'] = [
47
            'Controller' => TestController::class,
48
            'Locale' => 'en_NZ',
49
        ];
50
        return $rules;
51
    }
52
53
    protected function setUpRoutes()
54
    {
55
        // Don't merge with any existing rules
56
        Director::config()->set('rules', $this->getExtraRoutes());
57
    }
58
59
    public function testFileExists()
60
    {
61
        $tempFileName = 'DirectorTest_testFileExists.tmp';
62
        $tempFilePath = TEMP_PATH . DIRECTORY_SEPARATOR . $tempFileName;
63
64
        // create temp file
65
        file_put_contents($tempFilePath, '');
66
67
        $this->assertTrue(
68
            Director::fileExists($tempFilePath),
69
            'File exist check with absolute path'
70
        );
71
72
        $this->assertTrue(
73
            Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'),
74
            'File exist check with query params ignored'
75
        );
76
77
        unlink($tempFilePath);
78
    }
79
80
    public function testAbsoluteURL()
81
    {
82
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/mysite/');
83
        $_SERVER['REQUEST_URI'] = "http://www.mysite.com:9090/mysite/sub-page/";
84
85
        //test empty / local urls
86
        foreach (array('', './', '.') as $url) {
87
            $this->assertEquals(
88
                "http://www.mysite.com:9090/mysite/",
89
                Director::absoluteURL($url, Director::BASE)
90
            );
91
            $this->assertEquals(
92
                "http://www.mysite.com:9090/",
93
                Director::absoluteURL($url, Director::ROOT)
94
            );
95
            $this->assertEquals(
96
                "http://www.mysite.com:9090/mysite/sub-page/",
97
                Director::absoluteURL($url, Director::REQUEST)
98
            );
99
        }
100
101
        // Test site root url
102
        $this->assertEquals("http://www.mysite.com:9090/", Director::absoluteURL('/'));
103
104
        // Test Director::BASE
105
        $this->assertEquals(
106
            'http://www.mysite.com:9090/',
107
            Director::absoluteURL('http://www.mysite.com:9090/', Director::BASE)
108
        );
109
        $this->assertEquals(
110
            'http://www.mytest.com',
111
            Director::absoluteURL('http://www.mytest.com', Director::BASE)
112
        );
113
        $this->assertEquals(
114
            "http://www.mysite.com:9090/test",
115
            Director::absoluteURL("http://www.mysite.com:9090/test", Director::BASE)
116
        );
117
        $this->assertEquals(
118
            "http://www.mysite.com:9090/root",
119
            Director::absoluteURL("/root", Director::BASE)
120
        );
121
        $this->assertEquals(
122
            "http://www.mysite.com:9090/root/url",
123
            Director::absoluteURL("/root/url", Director::BASE)
124
        );
125
126
        // Test Director::ROOT
127
        $this->assertEquals(
128
            'http://www.mysite.com:9090/',
129
            Director::absoluteURL('http://www.mysite.com:9090/', Director::ROOT)
130
        );
131
        $this->assertEquals(
132
            'http://www.mytest.com',
133
            Director::absoluteURL('http://www.mytest.com', Director::ROOT)
134
        );
135
        $this->assertEquals(
136
            "http://www.mysite.com:9090/test",
137
            Director::absoluteURL("http://www.mysite.com:9090/test", Director::ROOT)
138
        );
139
        $this->assertEquals(
140
            "http://www.mysite.com:9090/root",
141
            Director::absoluteURL("/root", Director::ROOT)
142
        );
143
        $this->assertEquals(
144
            "http://www.mysite.com:9090/root/url",
145
            Director::absoluteURL("/root/url", Director::ROOT)
146
        );
147
148
        // Test Director::REQUEST
149
        $this->assertEquals(
150
            'http://www.mysite.com:9090/',
151
            Director::absoluteURL('http://www.mysite.com:9090/', Director::REQUEST)
152
        );
153
        $this->assertEquals(
154
            'http://www.mytest.com',
155
            Director::absoluteURL('http://www.mytest.com', Director::REQUEST)
156
        );
157
        $this->assertEquals(
158
            "http://www.mysite.com:9090/test",
159
            Director::absoluteURL("http://www.mysite.com:9090/test", Director::REQUEST)
160
        );
161
        $this->assertEquals(
162
            "http://www.mysite.com:9090/root",
163
            Director::absoluteURL("/root", Director::REQUEST)
164
        );
165
        $this->assertEquals(
166
            "http://www.mysite.com:9090/root/url",
167
            Director::absoluteURL("/root/url", Director::REQUEST)
168
        );
169
170
        // Test evaluating relative urls relative to base (default)
171
        $this->assertEquals(
172
            "http://www.mysite.com:9090/mysite/test",
173
            Director::absoluteURL("test")
174
        );
175
        $this->assertEquals(
176
            "http://www.mysite.com:9090/mysite/test/url",
177
            Director::absoluteURL("test/url")
178
        );
179
        $this->assertEquals(
180
            "http://www.mysite.com:9090/mysite/test",
181
            Director::absoluteURL("test", Director::BASE)
182
        );
183
        $this->assertEquals(
184
            "http://www.mysite.com:9090/mysite/test/url",
185
            Director::absoluteURL("test/url", Director::BASE)
186
        );
187
188
        // Test evaluting relative urls relative to root
189
        $this->assertEquals(
190
            "http://www.mysite.com:9090/test",
191
            Director::absoluteURL("test", Director::ROOT)
192
        );
193
        $this->assertEquals(
194
            "http://www.mysite.com:9090/test/url",
195
            Director::absoluteURL("test/url", Director::ROOT)
196
        );
197
198
        // Test relative to requested page
199
        $this->assertEquals(
200
            "http://www.mysite.com:9090/mysite/sub-page/test",
201
            Director::absoluteURL("test", Director::REQUEST)
202
        );
203
        $this->assertEquals(
204
            "http://www.mysite.com:9090/mysite/sub-page/test/url",
205
            Director::absoluteURL("test/url", Director::REQUEST)
206
        );
207
208
        // Test that javascript links are not left intact
209
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('javascript:alert("attack")'));
210
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('javascript:alert("attack")'));
211
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('alert("attack")'));
212
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('alert("attack")'));
213
    }
214
215
    public function testAlternativeBaseURL()
216
    {
217
        // relative base URLs - you should end them in a /
218
        Director::config()->set('alternate_base_url', '/relativebase/');
219
        $_SERVER['HTTP_HOST'] = 'www.somesite.com';
220
        $_SERVER['REQUEST_URI'] = "/relativebase/sub-page/";
221
222
        $this->assertEquals('/relativebase/', Director::baseURL());
223
        $this->assertEquals('http://www.somesite.com/relativebase/', Director::absoluteBaseURL());
224
        $this->assertEquals(
225
            'http://www.somesite.com/relativebase/subfolder/test',
226
            Director::absoluteURL('subfolder/test')
227
        );
228
229
        // absolute base URLS with subdirectory - You should end them in a /
230
        Director::config()->set('alternate_base_url', 'http://www.example.org/relativebase/');
231
        $_SERVER['REQUEST_URI'] = "http://www.example.org/relativebase/sub-page/";
232
        $this->assertEquals('/relativebase/', Director::baseURL()); // Non-absolute url
233
        $this->assertEquals('http://www.example.org/relativebase/', Director::absoluteBaseURL());
234
        $this->assertEquals(
235
            'http://www.example.org/relativebase/sub-page/',
236
            Director::absoluteURL('', Director::REQUEST)
237
        );
238
        $this->assertEquals(
239
            'http://www.example.org/relativebase/',
240
            Director::absoluteURL('', Director::BASE)
241
        );
242
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
243
        $this->assertEquals(
244
            'http://www.example.org/relativebase/sub-page/subfolder/test',
245
            Director::absoluteURL('subfolder/test', Director::REQUEST)
246
        );
247
        $this->assertEquals(
248
            'http://www.example.org/subfolder/test',
249
            Director::absoluteURL('subfolder/test', Director::ROOT)
250
        );
251
        $this->assertEquals(
252
            'http://www.example.org/relativebase/subfolder/test',
253
            Director::absoluteURL('subfolder/test', Director::BASE)
254
        );
255
256
        // absolute base URLs - you should end them in a /
257
        Director::config()->set('alternate_base_url', 'http://www.example.org/');
258
        $_SERVER['REQUEST_URI'] = "http://www.example.org/sub-page/";
259
        $this->assertEquals('/', Director::baseURL()); // Non-absolute url
260
        $this->assertEquals('http://www.example.org/', Director::absoluteBaseURL());
261
        $this->assertEquals('http://www.example.org/sub-page/', Director::absoluteURL('', Director::REQUEST));
262
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::BASE));
263
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
264
        $this->assertEquals(
265
            'http://www.example.org/sub-page/subfolder/test',
266
            Director::absoluteURL('subfolder/test', Director::REQUEST)
267
        );
268
        $this->assertEquals(
269
            'http://www.example.org/subfolder/test',
270
            Director::absoluteURL('subfolder/test', Director::ROOT)
271
        );
272
        $this->assertEquals(
273
            'http://www.example.org/subfolder/test',
274
            Director::absoluteURL('subfolder/test', Director::BASE)
275
        );
276
    }
277
278
    /**
279
     * Tests that {@link Director::is_absolute()} works under different environment types
280
     */
281
    public function testIsAbsolute()
282
    {
283
        $expected = array (
284
            'C:/something' => true,
285
            'd:\\'         => true,
286
            'e/'           => false,
287
            's:/directory' => true,
288
            '/var/www'     => true,
289
            '\\Something'  => true,
290
            'something/c:' => false,
291
            'folder'       => false,
292
            'a/c:/'        => false
293
        );
294
295
        foreach ($expected as $path => $result) {
296
            $this->assertEquals(Director::is_absolute($path), $result, "Test result for $path");
297
        }
298
    }
299
300
    public function testIsAbsoluteUrl()
301
    {
302
        $this->assertTrue(Director::is_absolute_url('http://test.com/testpage'));
303
        $this->assertTrue(Director::is_absolute_url('ftp://test.com'));
304
        $this->assertFalse(Director::is_absolute_url('test.com/testpage'));
305
        $this->assertFalse(Director::is_absolute_url('/relative'));
306
        $this->assertFalse(Director::is_absolute_url('relative'));
307
        $this->assertFalse(Director::is_absolute_url("/relative/?url=http://foo.com"));
308
        $this->assertFalse(Director::is_absolute_url("/relative/#http://foo.com"));
309
        $this->assertTrue(Director::is_absolute_url("https://test.com/?url=http://foo.com"));
310
        $this->assertTrue(Director::is_absolute_url("trickparseurl:http://test.com"));
311
        $this->assertTrue(Director::is_absolute_url('//test.com'));
312
        $this->assertTrue(Director::is_absolute_url('/////test.com'));
313
        $this->assertTrue(Director::is_absolute_url('  ///test.com'));
314
        $this->assertTrue(Director::is_absolute_url('http:test.com'));
315
        $this->assertTrue(Director::is_absolute_url('//http://test.com'));
316
    }
317
318
    public function testHostPortParts()
319
    {
320
        $this->assertEquals('www.mysite.com:9090', Director::host());
321
        $this->assertEquals('www.mysite.com', Director::hostName());
322
        $this->assertEquals('9090', Director::port());
323
    }
324
325
    public function testIsRelativeUrl()
326
    {
327
        $this->assertFalse(Director::is_relative_url('http://test.com'));
328
        $this->assertFalse(Director::is_relative_url('https://test.com'));
329
        $this->assertFalse(Director::is_relative_url('   https://test.com/testpage   '));
330
        $this->assertTrue(Director::is_relative_url('test.com/testpage'));
331
        $this->assertFalse(Director::is_relative_url('ftp://test.com'));
332
        $this->assertTrue(Director::is_relative_url('/relative'));
333
        $this->assertTrue(Director::is_relative_url('relative'));
334
        $this->assertTrue(Director::is_relative_url('/relative/?url=http://test.com'));
335
        $this->assertTrue(Director::is_relative_url('/relative/#=http://test.com'));
336
    }
337
338
    /**
339
     * @return array
340
     */
341
    public function providerMakeRelative()
342
    {
343
        return [
344
            // Resilience to slash position
345
            [
346
                'http://www.mysite.com:9090/base/folder',
347
                'http://www.mysite.com:9090/base/folder',
348
                ''
349
            ],
350
            [
351
                'http://www.mysite.com:9090/base/folder',
352
                'http://www.mysite.com:9090/base/folder/',
353
                ''
354
            ],
355
            [
356
                'http://www.mysite.com:9090/base/folder/',
357
                'http://www.mysite.com:9090/base/folder',
358
                ''
359
            ],
360
            [
361
                'http://www.mysite.com:9090/',
362
                'http://www.mysite.com:9090/',
363
                ''
364
            ],
365
            [
366
                'http://www.mysite.com:9090/',
367
                'http://www.mysite.com',
368
                ''
369
            ],
370
            [
371
                'http://www.mysite.com',
372
                'http://www.mysite.com:9090/',
373
                ''
374
            ],
375
            [
376
                'http://www.mysite.com:9090/base/folder',
377
                'http://www.mysite.com:9090/base/folder/page',
378
                'page'
379
            ],
380
            [
381
                'http://www.mysite.com:9090/',
382
                'http://www.mysite.com:9090/page/',
383
                'page/'
384
            ],
385
            // Parsing protocol safely
386
            [
387
                'http://www.mysite.com:9090/base/folder',
388
                'https://www.mysite.com:9090/base/folder',
389
                ''
390
            ],
391
            [
392
                'https://www.mysite.com:9090/base/folder',
393
                'http://www.mysite.com:9090/base/folder/testpage',
394
                'testpage'
395
            ],
396
            [
397
                'http://www.mysite.com:9090/base/folder',
398
                '//www.mysite.com:9090/base/folder/testpage',
399
                'testpage'
400
            ],
401
            // Dirty input
402
            [
403
                'http://www.mysite.com:9090/base/folder',
404
                '    https://www.mysite.com:9090/base/folder/testpage    ',
405
                'testpage'
406
            ],
407
            [
408
                'http://www.mysite.com:9090/base/folder',
409
                '//www.mysite.com:9090/base//folder/testpage//subpage',
410
                'testpage/subpage'
411
            ],
412
            // Non-http protocol isn't modified
413
            [
414
                'http://www.mysite.com:9090/base/folder',
415
                'ftp://test.com',
416
                'ftp://test.com'
417
            ],
418
            // Alternate hostnames are redirected
419
            [
420
                'https://www.mysite.com:9090/base/folder',
421
                'http://mysite.com:9090/base/folder/testpage',
422
                'testpage'
423
            ],
424
            [
425
                'http://www.otherdomain.com/base/folder',
426
                '//www.mysite.com:9090/base/folder/testpage',
427
                'testpage'
428
            ],
429
            // Base folder is found
430
            [
431
                'http://www.mysite.com:9090/base/folder',
432
                BASE_PATH . '/some/file.txt',
433
                'some/file.txt',
434
            ],
435
            // public folder is found
436
            [
437
                'http://www.mysite.com:9090/base/folder',
438
                PUBLIC_PATH . '/some/file.txt',
439
                'some/file.txt',
440
            ],
441
            // querystring is protected
442
            [
443
                'http://www.mysite.com:9090/base/folder',
444
                '//www.mysite.com:9090/base//folder/testpage//subpage?args=hello',
445
                'testpage/subpage?args=hello'
446
            ],
447
            [
448
                'http://www.mysite.com:9090/base/folder',
449
                '//www.mysite.com:9090/base//folder/?args=hello',
450
                '?args=hello'
451
            ],
452
        ];
453
    }
454
455
    /**
456
     * @dataProvider providerMakeRelative
457
     * @param string $baseURL Site base URL
458
     * @param string $requestURL Request URL
459
     * @param string $relativeURL Expected relative URL
460
     */
461
    public function testMakeRelative($baseURL, $requestURL, $relativeURL)
462
    {
463
        Director::config()->set('alternate_base_url', $baseURL);
464
        $actualRelative = Director::makeRelative($requestURL);
465
        $this->assertEquals(
466
            $relativeURL,
467
            $actualRelative,
468
            "Expected relativeURL of {$requestURL} to be {$relativeURL}"
469
        );
470
    }
471
472
    /**
473
     * Mostly tested by {@link testIsRelativeUrl()},
474
     * just adding the host name matching aspect here.
475
     */
476
    public function testIsSiteUrl()
477
    {
478
        $this->assertFalse(Director::is_site_url("http://test.com"));
479
        $this->assertTrue(Director::is_site_url(Director::absoluteBaseURL()));
480
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . Director::absoluteBaseURL()));
481
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL())));
482
        $this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL()));
483
        $this->assertFalse(Director::is_site_url('http://google.com\@test.com'));
484
        $this->assertFalse(Director::is_site_url('http://google.com/@test.com'));
485
        $this->assertFalse(Director::is_site_url('http://google.com:pass\@test.com'));
486
        $this->assertFalse(Director::is_site_url('http://google.com:pass/@test.com'));
487
    }
488
489
    public function testResetGlobalsAfterTestRequest()
490
    {
491
        $_GET = array('somekey' => 'getvalue');
492
        $_POST = array('somekey' => 'postvalue');
493
        $_COOKIE = array('somekey' => 'cookievalue');
494
495
        $cookies = Injector::inst()->createWithArgs(
496
            Cookie_Backend::class,
497
            array(array('somekey' => 'sometestcookievalue'))
498
        );
499
500
        Director::test(
501
            'errorpage?somekey=sometestgetvalue',
502
            array('somekey' => 'sometestpostvalue'),
503
            null,
504
            null,
505
            null,
506
            null,
507
            $cookies
508
        );
509
510
        $this->assertEquals(
511
            'getvalue',
512
            $_GET['somekey'],
513
            '$_GET reset to original value after Director::test()'
514
        );
515
        $this->assertEquals(
516
            'postvalue',
517
            $_POST['somekey'],
518
            '$_POST reset to original value after Director::test()'
519
        );
520
        $this->assertEquals(
521
            'cookievalue',
522
            $_COOKIE['somekey'],
523
            '$_COOKIE reset to original value after Director::test()'
524
        );
525
    }
526
527
    public function providerTestTestRequestCarriesGlobals()
528
    {
529
        $tests = [];
530
        $fixture = [ 'somekey' => 'sometestvalue' ];
531
        foreach (array('get', 'post') as $method) {
532
            foreach (array('return%sValue', 'returnRequestValue', 'returnCookieValue') as $testfunction) {
533
                $url = 'TestController/' . sprintf($testfunction, ucfirst($method))
534
                    . '?' . http_build_query($fixture);
535
                $tests[] = [$url, $fixture, $method];
536
            }
537
        }
538
        return $tests;
539
    }
540
541
    /**
542
     * @dataProvider providerTestTestRequestCarriesGlobals
543
     * @param $url
544
     * @param $fixture
545
     * @param $method
546
     */
547
    public function testTestRequestCarriesGlobals($url, $fixture, $method)
548
    {
549
        $getresponse = Director::test(
550
            $url,
551
            $fixture,
552
            null,
553
            strtoupper($method),
554
            null,
555
            null,
556
            Injector::inst()->createWithArgs(Cookie_Backend::class, array($fixture))
557
        );
558
559
        $this->assertInstanceOf(HTTPResponse::class, $getresponse, 'Director::test() returns HTTPResponse');
560
        $this->assertEquals($fixture['somekey'], $getresponse->getBody(), "Director::test({$url}, {$method})");
561
    }
562
563
    /**
564
     * Tests that additional parameters specified in the routing table are
565
     * saved in the request
566
     */
567
    public function testRouteParams()
568
    {
569
        /** @var HTTPRequest $request */
570
        Director::test('en-nz/myaction/myid/myotherid', null, null, null, null, null, null, $request);
571
572
        $this->assertEquals(
573
            array(
574
                'Controller' => TestController::class,
575
                'Action' => 'myaction',
576
                'ID' => 'myid',
577
                'OtherID' => 'myotherid',
578
                'Locale' => 'en_NZ'
579
            ),
580
            $request->params()
581
        );
582
    }
583
584
    public function testForceWWW()
585
    {
586
        $this->expectExceptionRedirect('http://www.mysite.com:9090/some-url');
587
        Director::mockRequest(function ($request) {
588
            Injector::inst()->registerService($request, HTTPRequest::class);
589
            Director::forceWWW();
590
        }, 'http://mysite.com:9090/some-url');
591
    }
592
593
    public function testPromisedForceWWW()
594
    {
595
        Director::forceWWW();
596
597
        // Flag is set but not redirected yet
598
        $middleware = CanonicalURLMiddleware::singleton();
599
        $this->assertTrue($middleware->getForceWWW());
600
601
        // Middleware forces the redirection eventually
602
        /** @var HTTPResponse $response */
603
        $response = Director::mockRequest(function ($request) use ($middleware) {
604
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

604
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
605
                return null;
606
            });
607
        }, 'http://mysite.com:9090/some-url');
608
609
        // Middleware returns non-exception redirect
610
        $this->assertEquals('http://www.mysite.com:9090/some-url', $response->getHeader('Location'));
611
        $this->assertEquals(301, $response->getStatusCode());
612
    }
613
614
    public function testForceSSLProtectsEntireSite()
615
    {
616
        $this->expectExceptionRedirect('https://www.mysite.com:9090/some-url');
617
        Director::mockRequest(function ($request) {
618
            Injector::inst()->registerService($request, HTTPRequest::class);
619
            Director::forceSSL();
620
        }, 'http://www.mysite.com:9090/some-url');
621
    }
622
623
    public function testPromisedForceSSL()
624
    {
625
            Director::forceSSL();
626
627
        // Flag is set but not redirected yet
628
        $middleware = CanonicalURLMiddleware::singleton();
629
        $this->assertTrue($middleware->getForceSSL());
630
631
        // Middleware forces the redirection eventually
632
        /** @var HTTPResponse $response */
633
        $response = Director::mockRequest(function ($request) use ($middleware) {
634
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

634
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
635
                return null;
636
            });
637
        }, 'http://www.mysite.com:9090/some-url');
638
639
        // Middleware returns non-exception redirect
640
        $this->assertInstanceOf(HTTPResponse::class, $response);
641
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
642
        $this->assertEquals(301, $response->getStatusCode());
643
    }
644
645
    public function testForceSSLOnTopLevelPagePattern()
646
    {
647
        // Expect admin to trigger redirect
648
        $this->expectExceptionRedirect('https://www.mysite.com:9090/admin');
649
        Director::mockRequest(function (HTTPRequest $request) {
650
            Injector::inst()->registerService($request, HTTPRequest::class);
651
            Director::forceSSL(array('/^admin/'));
652
        }, 'http://www.mysite.com:9090/admin');
653
    }
654
655
    public function testForceSSLOnSubPagesPattern()
656
    {
657
        // Expect to redirect to security login page
658
        $this->expectExceptionRedirect('https://www.mysite.com:9090/Security/login');
659
        Director::mockRequest(function (HTTPRequest $request) {
660
            Injector::inst()->registerService($request, HTTPRequest::class);
661
            Director::forceSSL(array('/^Security/'));
662
        }, 'http://www.mysite.com:9090/Security/login');
663
    }
664
665
    public function testForceSSLWithPatternDoesNotMatchOtherPages()
666
    {
667
        // Not on same url should not trigger redirect
668
        $response = Director::mockRequest(function (HTTPRequest $request) {
669
            Injector::inst()->registerService($request, HTTPRequest::class);
670
            Director::forceSSL(array('/^admin/'));
671
        }, 'http://www.mysite.com:9090/normal-page');
672
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
673
674
        // nested url should not triger redirect either
675
        $response = Director::mockRequest(function (HTTPRequest $request) {
676
            Injector::inst()->registerService($request, HTTPRequest::class);
677
            Director::forceSSL(array('/^admin/', '/^Security/'));
678
        }, 'http://www.mysite.com:9090/just-another-page/sub-url');
679
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
680
    }
681
682
    public function testForceSSLAlternateDomain()
683
    {
684
        // Ensure that forceSSL throws the appropriate exception
685
        $this->expectExceptionRedirect('https://secure.mysite.com/admin');
686
        Director::mockRequest(function (HTTPRequest $request) {
687
            Injector::inst()->registerService($request, HTTPRequest::class);
688
            return Director::forceSSL(array('/^admin/'), 'secure.mysite.com');
0 ignored issues
show
Bug introduced by
Are you sure the usage of SilverStripe\Control\Dir...), 'secure.mysite.com') targeting SilverStripe\Control\Director::forceSSL() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
689
        }, 'http://www.mysite.com:9090/admin');
690
    }
691
692
    public function testForceSSLAlternateDomainWithPort()
693
    {
694
        // Ensure that forceSSL throws the appropriate exception
695
        $this->expectExceptionRedirect('https://secure.mysite.com:81/admin');
696
        Director::mockRequest(function (HTTPRequest $request) {
697
            Injector::inst()->registerService($request, HTTPRequest::class);
698
            return Director::forceSSL(array('/^admin/'), 'secure.mysite.com:81');
0 ignored issues
show
Bug introduced by
Are you sure the usage of SilverStripe\Control\Dir...'secure.mysite.com:81') targeting SilverStripe\Control\Director::forceSSL() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
699
        }, 'http://www.mysite.com:9090/admin');
700
    }
701
702
    /**
703
     * Test that combined forceWWW and forceSSL combine safely
704
     */
705
    public function testForceSSLandForceWWW()
706
    {
707
        Director::forceWWW();
708
        Director::forceSSL();
709
710
        // Flag is set but not redirected yet
711
        $middleware = CanonicalURLMiddleware::singleton();
712
        $this->assertTrue($middleware->getForceWWW());
713
        $this->assertTrue($middleware->getForceSSL());
714
715
        // Middleware forces the redirection eventually
716
        /** @var HTTPResponse $response */
717
        $response = Director::mockRequest(function ($request) use ($middleware) {
718
            return $middleware->process($request, function ($request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

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

718
            return $middleware->process($request, function (/** @scrutinizer ignore-unused */ $request) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
719
                return null;
720
            });
721
        }, 'http://mysite.com:9090/some-url');
722
723
        // Middleware returns non-exception redirect
724
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
725
        $this->assertEquals(301, $response->getStatusCode());
726
    }
727
728
    /**
729
     * Set url to redirect to
730
     *
731
     * @var string
732
     */
733
    protected $expectedRedirect = null;
734
735
    /**
736
     * Expects this test to throw a HTTPResponse_Exception with the given redirect
737
     *
738
     * @param string $url
739
     */
740
    protected function expectExceptionRedirect($url)
741
    {
742
        $this->expectedRedirect = $url;
743
    }
744
745
    protected function runTest()
746
    {
747
        try {
748
            $result = parent::runTest();
749
            if ($this->expectedRedirect) {
750
                $this->fail("Expected to redirect to {$this->expectedRedirect} but no redirect found");
751
            }
752
            return $result;
753
        } catch (HTTPResponse_Exception $exception) {
754
            // Check URL
755
            if ($this->expectedRedirect) {
756
                $url = $exception->getResponse()->getHeader('Location');
757
                $this->assertEquals($this->expectedRedirect, $url, "Expected to redirect to {$this->expectedRedirect}");
758
                return null;
759
            } else {
760
                throw $exception;
761
            }
762
        }
763
    }
764
765
    public function testUnmatchedRequestReturns404()
766
    {
767
        // Remove non-tested rules
768
        $this->assertEquals(404, Director::test('no-route')->getStatusCode());
769
    }
770
771
    public function testIsHttps()
772
    {
773
        // Trust all IPs for this test
774
        /** @var TrustedProxyMiddleware $trustedProxyMiddleware */
775
        $trustedProxyMiddleware
776
            = Injector::inst()->get(TrustedProxyMiddleware::class);
777
        $trustedProxyMiddleware->setTrustedProxyIPs('*');
778
779
        // Clear alternate_base_url for this test
780
        Director::config()->remove('alternate_base_url');
781
782
        // nothing available
783
        $headers = array(
784
            'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
785
        );
786
        foreach ($headers as $header) {
787
            if (isset($_SERVER[$header])) {
788
                unset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']);
789
            }
790
        }
791
792
        $this->assertEquals(
793
            'no',
794
            Director::test('TestController/returnIsSSL')->getBody()
795
        );
796
797
        $this->assertEquals(
798
            'yes',
799
            Director::test(
800
                'TestController/returnIsSSL',
801
                null,
802
                null,
803
                null,
804
                null,
805
                [ 'X-Forwarded-Protocol' => 'https' ]
806
            )->getBody()
807
        );
808
809
        $this->assertEquals(
810
            'no',
811
            Director::test(
812
                'TestController/returnIsSSL',
813
                null,
814
                null,
815
                null,
816
                null,
817
                [ 'X-Forwarded-Protocol' => 'http' ]
818
            )->getBody()
819
        );
820
821
        $this->assertEquals(
822
            'no',
823
            Director::test(
824
                'TestController/returnIsSSL',
825
                null,
826
                null,
827
                null,
828
                null,
829
                [ 'X-Forwarded-Protocol' => 'ftp' ]
830
            )->getBody()
831
        );
832
833
        // https via HTTPS
834
        $_SERVER['HTTPS'] = 'true';
835
        $this->assertEquals(
836
            'yes',
837
            Director::test('TestController/returnIsSSL')->getBody()
838
        );
839
840
        $_SERVER['HTTPS'] = '1';
841
        $this->assertEquals(
842
            'yes',
843
            Director::test('TestController/returnIsSSL')->getBody()
844
        );
845
846
        $_SERVER['HTTPS'] = 'off';
847
        $this->assertEquals(
848
            'no',
849
            Director::test('TestController/returnIsSSL')->getBody()
850
        );
851
852
        // https via SSL
853
        $_SERVER['SSL'] = '';
854
        $this->assertEquals(
855
            'yes',
856
            Director::test('TestController/returnIsSSL')->getBody()
857
        );
858
    }
859
860
    public function testTestIgnoresHashes()
861
    {
862
        //test that hashes are ignored
863
        $url = "TestController/returnGetValue?somekey=key";
864
        $hash = "#test";
865
        /** @var HTTPRequest $request */
866
        $response = Director::test($url . $hash, null, null, null, null, null, null, $request);
867
        $this->assertFalse($response->isError());
868
        $this->assertEquals('key', $response->getBody());
869
        $this->assertEquals($request->getURL(true), $url);
870
871
        //test encoded hashes are accepted
872
        $url = "TestController/returnGetValue?somekey=test%23key";
873
        $response = Director::test($url, null, null, null, null, null, null, $request);
874
        $this->assertFalse($response->isError());
875
        $this->assertEquals('test#key', $response->getBody());
876
        $this->assertEquals($request->getURL(true), $url);
877
    }
878
879
    public function testRequestFilterInDirectorTest()
880
    {
881
        $filter = new DirectorTest\TestRequestFilter;
882
883
        $processor = new RequestProcessor(array($filter));
0 ignored issues
show
Deprecated Code introduced by
The class SilverStripe\Control\RequestProcessor has been deprecated: 4.0..5.0 Use HTTPMiddleware directly instead. ( Ignorable by Annotation )

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

883
        $processor = /** @scrutinizer ignore-deprecated */ new RequestProcessor(array($filter));
Loading history...
884
885
        Injector::inst()->registerService($processor, RequestProcessor::class);
886
        $response = Director::test('some-dummy-url');
887
        $this->assertEquals(404, $response->getStatusCode());
888
889
        $this->assertEquals(1, $filter->preCalls);
890
        $this->assertEquals(1, $filter->postCalls);
891
892
        $filter->failPost = true;
893
894
        $response = Director::test('some-dummy-url');
895
        $this->assertEquals(500, $response->getStatusCode());
896
        $this->assertEquals(_t(Director::class . '.REQUEST_ABORTED', 'Request aborted'), $response->getBody());
897
898
        $this->assertEquals(2, $filter->preCalls);
899
        $this->assertEquals(2, $filter->postCalls);
900
901
        $filter->failPre = true;
902
903
        $response = Director::test('some-dummy-url');
904
        $this->assertEquals(400, $response->getStatusCode());
905
        $this->assertEquals(_t(Director::class . '.INVALID_REQUEST', 'Invalid request'), $response->getBody());
906
907
        $this->assertEquals(3, $filter->preCalls);
908
909
        // preCall 'true' will trigger an exception and prevent post call execution
910
        $this->assertEquals(2, $filter->postCalls);
911
    }
912
913
    public function testGlobalMiddleware()
914
    {
915
        $middleware = new DirectorTest\TestMiddleware;
916
        Director::singleton()->setMiddlewares([$middleware]);
917
918
        $response = Director::test('some-dummy-url');
919
        $this->assertEquals(404, $response->getStatusCode());
920
921
        // Both triggered
922
        $this->assertEquals(1, $middleware->preCalls);
923
        $this->assertEquals(1, $middleware->postCalls);
924
925
        $middleware->failPost = true;
926
927
        $response = Director::test('some-dummy-url');
928
        $this->assertEquals(500, $response->getStatusCode());
929
930
        // Both triggered
931
        $this->assertEquals(2, $middleware->preCalls);
932
        $this->assertEquals(2, $middleware->postCalls);
933
934
        $middleware->failPre = true;
935
936
        $response = Director::test('some-dummy-url');
937
        $this->assertEquals(400, $response->getStatusCode());
938
939
        // Pre triggered, post not
940
        $this->assertEquals(3, $middleware->preCalls);
941
        $this->assertEquals(2, $middleware->postCalls);
942
    }
943
944
    public function testRouteSpecificMiddleware()
945
    {
946
        // Inject adapter in place of controller
947
        $specificMiddleware = new DirectorTest\TestMiddleware;
948
        Injector::inst()->registerService($specificMiddleware, 'SpecificMiddleware');
949
950
        // Register adapter as factory for creating this controller
951
        Config::modify()->merge(
952
            Injector::class,
953
            'ControllerWithMiddleware',
954
            [
955
                'class' => RequestHandlerMiddlewareAdapter::class,
956
                'constructor' => [
957
                    '%$' . TestController::class
958
                ],
959
                'properties' => [
960
                    'Middlewares' => [
961
                        '%$SpecificMiddleware',
962
                    ],
963
                ],
964
            ]
965
        );
966
967
        // Global middleware
968
        $middleware = new DirectorTest\TestMiddleware;
969
        Director::singleton()->setMiddlewares([ $middleware ]);
970
971
        // URL rules, one of which has a specific middleware
972
        Config::modify()->set(
973
            Director::class,
974
            'rules',
975
            [
976
                'url-one' => TestController::class,
977
                'url-two' => [
978
                    'Controller' => 'ControllerWithMiddleware',
979
                ],
980
            ]
981
        );
982
983
        // URL without a route-specific middleware
984
        Director::test('url-one');
985
986
        // Only the global middleware triggered
987
        $this->assertEquals(1, $middleware->preCalls);
988
        $this->assertEquals(0, $specificMiddleware->postCalls);
989
990
        Director::test('url-two');
991
992
        // Both triggered on the url with the specific middleware applied
993
        $this->assertEquals(2, $middleware->preCalls);
994
        $this->assertEquals(1, $specificMiddleware->postCalls);
995
    }
996
997
    /**
998
     * If using phpdbg it returns itself instead of "cli" from php_sapi_name()
999
     */
1000
    public function testIsCli()
1001
    {
1002
        $this->assertTrue(Director::is_cli(), 'is_cli should be true for PHP CLI and phpdbg');
1003
    }
1004
1005
    public function testMockRequest()
1006
    {
1007
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/some-subdir/');
1008
1009
        // Can handle root-relative $url
1010
        Director::mockRequest(function (HTTPRequest $request) {
1011
            $this->assertEquals('some-page/nested', $request->getURL());
1012
            $this->assertEquals(1, $request->getVar('query'));
1013
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1014
        }, '/some-subdir/some-page/nested?query=1');
1015
1016
        // Can handle absolute $url
1017
        Director::mockRequest(function (HTTPRequest $request) {
1018
            $this->assertEquals('some-page/nested', $request->getURL());
1019
            $this->assertEquals(1, $request->getVar('query'));
1020
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1021
        }, 'http://www.mysite.com:9090/some-subdir/some-page/nested?query=1');
1022
1023
        // Can handle relative $url
1024
        Director::mockRequest(function (HTTPRequest $request) {
1025
            $this->assertEquals('some-page/nested', $request->getURL());
1026
            $this->assertEquals(1, $request->getVar('query'));
1027
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1028
        }, 'some-page/nested?query=1');
1029
    }
1030
}
1031