Passed
Push — master ( ddbf8b...086098 )
by Robbie
11:17
created

DirectorTest::testQueryIsEnvironment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 41
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 26
nc 2
nop 0
dl 0
loc 41
rs 9.504
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Tests;
4
5
use SilverStripe\Control\Cookie_Backend;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Control\HTTPResponse_Exception;
10
use SilverStripe\Control\Middleware\CanonicalURLMiddleware;
11
use SilverStripe\Control\Middleware\RequestHandlerMiddlewareAdapter;
12
use SilverStripe\Control\Middleware\TrustedProxyMiddleware;
13
use SilverStripe\Control\RequestProcessor;
14
use SilverStripe\Control\Tests\DirectorTest\TestController;
15
use SilverStripe\Core\Config\Config;
16
use SilverStripe\Core\Environment;
17
use SilverStripe\Core\Injector\Injector;
18
use SilverStripe\Core\Kernel;
19
use SilverStripe\Dev\SapphireTest;
20
21
/**
22
 * @todo test Director::alternateBaseFolder()
23
 */
24
class DirectorTest extends SapphireTest
25
{
26
    protected static $extra_controllers = [
27
        TestController::class,
28
    ];
29
30
    private $originalEnvType;
31
32
    protected function setUp()
33
    {
34
        parent::setUp();
35
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/');
36
37
        $this->originalEnvType = Environment::getEnv('SS_ENVIRONMENT_TYPE');
38
39
        // Ensure redirects enabled on all environments and global state doesn't affect the tests
40
        CanonicalURLMiddleware::singleton()
41
            ->setForceSSLDomain(null)
42
            ->setForceSSLPatterns([])
43
            ->setEnabledEnvs(true);
44
        $this->expectedRedirect = null;
45
    }
46
47
    protected function tearDown(...$args)
48
    {
49
        Environment::setEnv('SS_ENVIRONMENT_TYPE', $this->originalEnvType);
50
        parent::tearDown(...$args);
0 ignored issues
show
Unused Code introduced by
The call to SilverStripe\Dev\SapphireTest::tearDown() has too many arguments starting with $args. ( Ignorable by Annotation )

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

50
        parent::/** @scrutinizer ignore-call */ 
51
                tearDown(...$args);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

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

664
            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...
665
                return null;
666
            });
667
        }, 'http://mysite.com:9090/some-url');
668
669
        // Middleware returns non-exception redirect
670
        $this->assertEquals('http://www.mysite.com:9090/some-url', $response->getHeader('Location'));
671
        $this->assertEquals(301, $response->getStatusCode());
672
    }
673
674
    public function testForceSSLProtectsEntireSite()
675
    {
676
        $this->expectExceptionRedirect('https://www.mysite.com:9090/some-url');
677
        Director::mockRequest(function ($request) {
678
            Injector::inst()->registerService($request, HTTPRequest::class);
679
            Director::forceSSL();
680
        }, 'http://www.mysite.com:9090/some-url');
681
    }
682
683
    public function testPromisedForceSSL()
684
    {
685
            Director::forceSSL();
686
687
        // Flag is set but not redirected yet
688
        $middleware = CanonicalURLMiddleware::singleton();
689
        $this->assertTrue($middleware->getForceSSL());
690
691
        // Middleware forces the redirection eventually
692
        /** @var HTTPResponse $response */
693
        $response = Director::mockRequest(function ($request) use ($middleware) {
694
            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

694
            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...
695
                return null;
696
            });
697
        }, 'http://www.mysite.com:9090/some-url');
698
699
        // Middleware returns non-exception redirect
700
        $this->assertInstanceOf(HTTPResponse::class, $response);
701
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
702
        $this->assertEquals(301, $response->getStatusCode());
703
    }
704
705
    public function testForceSSLOnTopLevelPagePattern()
706
    {
707
        // Expect admin to trigger redirect
708
        $this->expectExceptionRedirect('https://www.mysite.com:9090/admin');
709
        Director::mockRequest(function (HTTPRequest $request) {
710
            Injector::inst()->registerService($request, HTTPRequest::class);
711
            Director::forceSSL(array('/^admin/'));
712
        }, 'http://www.mysite.com:9090/admin');
713
    }
714
715
    public function testForceSSLOnSubPagesPattern()
716
    {
717
        // Expect to redirect to security login page
718
        $this->expectExceptionRedirect('https://www.mysite.com:9090/Security/login');
719
        Director::mockRequest(function (HTTPRequest $request) {
720
            Injector::inst()->registerService($request, HTTPRequest::class);
721
            Director::forceSSL(array('/^Security/'));
722
        }, 'http://www.mysite.com:9090/Security/login');
723
    }
724
725
    public function testForceSSLWithPatternDoesNotMatchOtherPages()
726
    {
727
        // Not on same url should not trigger redirect
728
        $response = Director::mockRequest(function (HTTPRequest $request) {
729
            Injector::inst()->registerService($request, HTTPRequest::class);
730
            Director::forceSSL(array('/^admin/'));
731
        }, 'http://www.mysite.com:9090/normal-page');
732
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
733
734
        // nested url should not triger redirect either
735
        $response = Director::mockRequest(function (HTTPRequest $request) {
736
            Injector::inst()->registerService($request, HTTPRequest::class);
737
            Director::forceSSL(array('/^admin/', '/^Security/'));
738
        }, 'http://www.mysite.com:9090/just-another-page/sub-url');
739
        $this->assertNull($response, 'Non-matching patterns do not trigger redirect');
740
    }
741
742
    public function testForceSSLAlternateDomain()
743
    {
744
        // Ensure that forceSSL throws the appropriate exception
745
        $this->expectExceptionRedirect('https://secure.mysite.com/admin');
746
        Director::mockRequest(function (HTTPRequest $request) {
747
            Injector::inst()->registerService($request, HTTPRequest::class);
748
            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...
749
        }, 'http://www.mysite.com:9090/admin');
750
    }
751
752
    public function testForceSSLAlternateDomainWithPort()
753
    {
754
        // Ensure that forceSSL throws the appropriate exception
755
        $this->expectExceptionRedirect('https://secure.mysite.com:81/admin');
756
        Director::mockRequest(function (HTTPRequest $request) {
757
            Injector::inst()->registerService($request, HTTPRequest::class);
758
            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...
759
        }, 'http://www.mysite.com:9090/admin');
760
    }
761
762
    /**
763
     * Test that combined forceWWW and forceSSL combine safely
764
     */
765
    public function testForceSSLandForceWWW()
766
    {
767
        Director::forceWWW();
768
        Director::forceSSL();
769
770
        // Flag is set but not redirected yet
771
        $middleware = CanonicalURLMiddleware::singleton();
772
        $this->assertTrue($middleware->getForceWWW());
773
        $this->assertTrue($middleware->getForceSSL());
774
775
        // Middleware forces the redirection eventually
776
        /** @var HTTPResponse $response */
777
        $response = Director::mockRequest(function ($request) use ($middleware) {
778
            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

778
            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...
779
                return null;
780
            });
781
        }, 'http://mysite.com:9090/some-url');
782
783
        // Middleware returns non-exception redirect
784
        $this->assertEquals('https://www.mysite.com:9090/some-url', $response->getHeader('Location'));
785
        $this->assertEquals(301, $response->getStatusCode());
786
    }
787
788
    /**
789
     * Set url to redirect to
790
     *
791
     * @var string
792
     */
793
    protected $expectedRedirect = null;
794
795
    /**
796
     * Expects this test to throw a HTTPResponse_Exception with the given redirect
797
     *
798
     * @param string $url
799
     */
800
    protected function expectExceptionRedirect($url)
801
    {
802
        $this->expectedRedirect = $url;
803
    }
804
805
    protected function runTest()
806
    {
807
        try {
808
            $result = parent::runTest();
809
            if ($this->expectedRedirect) {
810
                $this->fail("Expected to redirect to {$this->expectedRedirect} but no redirect found");
811
            }
812
            return $result;
813
        } catch (HTTPResponse_Exception $exception) {
814
            // Check URL
815
            if ($this->expectedRedirect) {
816
                $url = $exception->getResponse()->getHeader('Location');
817
                $this->assertEquals($this->expectedRedirect, $url, "Expected to redirect to {$this->expectedRedirect}");
818
                return null;
819
            } else {
820
                throw $exception;
821
            }
822
        }
823
    }
824
825
    public function testUnmatchedRequestReturns404()
826
    {
827
        // Remove non-tested rules
828
        $this->assertEquals(404, Director::test('no-route')->getStatusCode());
829
    }
830
831
    public function testIsHttps()
832
    {
833
        // Trust all IPs for this test
834
        /** @var TrustedProxyMiddleware $trustedProxyMiddleware */
835
        $trustedProxyMiddleware
836
            = Injector::inst()->get(TrustedProxyMiddleware::class);
837
        $trustedProxyMiddleware->setTrustedProxyIPs('*');
838
839
        // Clear alternate_base_url for this test
840
        Director::config()->remove('alternate_base_url');
841
842
        // nothing available
843
        $headers = array(
844
            'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
845
        );
846
        foreach ($headers as $header) {
847
            if (isset($_SERVER[$header])) {
848
                unset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']);
849
            }
850
        }
851
852
        $this->assertEquals(
853
            'no',
854
            Director::test('TestController/returnIsSSL')->getBody()
855
        );
856
857
        $this->assertEquals(
858
            'yes',
859
            Director::test(
860
                'TestController/returnIsSSL',
861
                null,
862
                null,
863
                null,
864
                null,
865
                ['X-Forwarded-Protocol' => 'https']
866
            )->getBody()
867
        );
868
869
        $this->assertEquals(
870
            'no',
871
            Director::test(
872
                'TestController/returnIsSSL',
873
                null,
874
                null,
875
                null,
876
                null,
877
                ['X-Forwarded-Protocol' => 'http']
878
            )->getBody()
879
        );
880
881
        $this->assertEquals(
882
            'no',
883
            Director::test(
884
                'TestController/returnIsSSL',
885
                null,
886
                null,
887
                null,
888
                null,
889
                ['X-Forwarded-Protocol' => 'ftp']
890
            )->getBody()
891
        );
892
893
        // https via HTTPS
894
        $_SERVER['HTTPS'] = 'true';
895
        $this->assertEquals(
896
            'yes',
897
            Director::test('TestController/returnIsSSL')->getBody()
898
        );
899
900
        $_SERVER['HTTPS'] = '1';
901
        $this->assertEquals(
902
            'yes',
903
            Director::test('TestController/returnIsSSL')->getBody()
904
        );
905
906
        $_SERVER['HTTPS'] = 'off';
907
        $this->assertEquals(
908
            'no',
909
            Director::test('TestController/returnIsSSL')->getBody()
910
        );
911
912
        // https via SSL
913
        $_SERVER['SSL'] = '';
914
        $this->assertEquals(
915
            'yes',
916
            Director::test('TestController/returnIsSSL')->getBody()
917
        );
918
    }
919
920
    public function testTestIgnoresHashes()
921
    {
922
        //test that hashes are ignored
923
        $url = "TestController/returnGetValue?somekey=key";
924
        $hash = "#test";
925
        /** @var HTTPRequest $request */
926
        $response = Director::test($url . $hash, null, null, null, null, null, null, $request);
927
        $this->assertFalse($response->isError());
928
        $this->assertEquals('key', $response->getBody());
929
        $this->assertEquals($request->getURL(true), $url);
930
931
        //test encoded hashes are accepted
932
        $url = "TestController/returnGetValue?somekey=test%23key";
933
        $response = Director::test($url, null, null, null, null, null, null, $request);
934
        $this->assertFalse($response->isError());
935
        $this->assertEquals('test#key', $response->getBody());
936
        $this->assertEquals($request->getURL(true), $url);
937
    }
938
939
    public function testRequestFilterInDirectorTest()
940
    {
941
        $filter = new DirectorTest\TestRequestFilter;
942
943
        $processor = new RequestProcessor(array($filter));
0 ignored issues
show
Deprecated Code introduced by
The class SilverStripe\Control\RequestProcessor has been deprecated: 4.0.0:5.0.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

943
        $processor = /** @scrutinizer ignore-deprecated */ new RequestProcessor(array($filter));
Loading history...
944
945
        Injector::inst()->registerService($processor, RequestProcessor::class);
946
        $response = Director::test('some-dummy-url');
947
        $this->assertEquals(404, $response->getStatusCode());
948
949
        $this->assertEquals(1, $filter->preCalls);
950
        $this->assertEquals(1, $filter->postCalls);
951
952
        $filter->failPost = true;
953
954
        $response = Director::test('some-dummy-url');
955
        $this->assertEquals(500, $response->getStatusCode());
956
        $this->assertEquals(_t(Director::class . '.REQUEST_ABORTED', 'Request aborted'), $response->getBody());
957
958
        $this->assertEquals(2, $filter->preCalls);
959
        $this->assertEquals(2, $filter->postCalls);
960
961
        $filter->failPre = true;
962
963
        $response = Director::test('some-dummy-url');
964
        $this->assertEquals(400, $response->getStatusCode());
965
        $this->assertEquals(_t(Director::class . '.INVALID_REQUEST', 'Invalid request'), $response->getBody());
966
967
        $this->assertEquals(3, $filter->preCalls);
968
969
        // preCall 'true' will trigger an exception and prevent post call execution
970
        $this->assertEquals(2, $filter->postCalls);
971
    }
972
973
    public function testGlobalMiddleware()
974
    {
975
        $middleware = new DirectorTest\TestMiddleware;
976
        Director::singleton()->setMiddlewares([$middleware]);
977
978
        $response = Director::test('some-dummy-url');
979
        $this->assertEquals(404, $response->getStatusCode());
980
981
        // Both triggered
982
        $this->assertEquals(1, $middleware->preCalls);
983
        $this->assertEquals(1, $middleware->postCalls);
984
985
        $middleware->failPost = true;
986
987
        $response = Director::test('some-dummy-url');
988
        $this->assertEquals(500, $response->getStatusCode());
989
990
        // Both triggered
991
        $this->assertEquals(2, $middleware->preCalls);
992
        $this->assertEquals(2, $middleware->postCalls);
993
994
        $middleware->failPre = true;
995
996
        $response = Director::test('some-dummy-url');
997
        $this->assertEquals(400, $response->getStatusCode());
998
999
        // Pre triggered, post not
1000
        $this->assertEquals(3, $middleware->preCalls);
1001
        $this->assertEquals(2, $middleware->postCalls);
1002
    }
1003
1004
    public function testRouteSpecificMiddleware()
1005
    {
1006
        // Inject adapter in place of controller
1007
        $specificMiddleware = new DirectorTest\TestMiddleware;
1008
        Injector::inst()->registerService($specificMiddleware, 'SpecificMiddleware');
1009
1010
        // Register adapter as factory for creating this controller
1011
        Config::modify()->merge(
1012
            Injector::class,
1013
            'ControllerWithMiddleware',
1014
            [
1015
                'class' => RequestHandlerMiddlewareAdapter::class,
1016
                'constructor' => [
1017
                    '%$' . TestController::class
1018
                ],
1019
                'properties' => [
1020
                    'Middlewares' => [
1021
                        '%$SpecificMiddleware',
1022
                    ],
1023
                ],
1024
            ]
1025
        );
1026
1027
        // Global middleware
1028
        $middleware = new DirectorTest\TestMiddleware;
1029
        Director::singleton()->setMiddlewares([$middleware]);
1030
1031
        // URL rules, one of which has a specific middleware
1032
        Config::modify()->set(
1033
            Director::class,
1034
            'rules',
1035
            [
1036
                'url-one' => TestController::class,
1037
                'url-two' => [
1038
                    'Controller' => 'ControllerWithMiddleware',
1039
                ],
1040
            ]
1041
        );
1042
1043
        // URL without a route-specific middleware
1044
        Director::test('url-one');
1045
1046
        // Only the global middleware triggered
1047
        $this->assertEquals(1, $middleware->preCalls);
1048
        $this->assertEquals(0, $specificMiddleware->postCalls);
1049
1050
        Director::test('url-two');
1051
1052
        // Both triggered on the url with the specific middleware applied
1053
        $this->assertEquals(2, $middleware->preCalls);
1054
        $this->assertEquals(1, $specificMiddleware->postCalls);
1055
    }
1056
1057
    /**
1058
     * If using phpdbg it returns itself instead of "cli" from php_sapi_name()
1059
     */
1060
    public function testIsCli()
1061
    {
1062
        $this->assertTrue(Director::is_cli(), 'is_cli should be true for PHP CLI and phpdbg');
1063
    }
1064
1065
    public function testMockRequest()
1066
    {
1067
        Director::config()->set('alternate_base_url', 'http://www.mysite.com:9090/some-subdir/');
1068
1069
        // Can handle root-relative $url
1070
        Director::mockRequest(function (HTTPRequest $request) {
1071
            $this->assertEquals('some-page/nested', $request->getURL());
1072
            $this->assertEquals(1, $request->getVar('query'));
1073
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1074
        }, '/some-subdir/some-page/nested?query=1');
1075
1076
        // Can handle absolute $url
1077
        Director::mockRequest(function (HTTPRequest $request) {
1078
            $this->assertEquals('some-page/nested', $request->getURL());
1079
            $this->assertEquals(1, $request->getVar('query'));
1080
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1081
        }, 'http://www.mysite.com:9090/some-subdir/some-page/nested?query=1');
1082
1083
        // Can handle relative $url
1084
        Director::mockRequest(function (HTTPRequest $request) {
1085
            $this->assertEquals('some-page/nested', $request->getURL());
1086
            $this->assertEquals(1, $request->getVar('query'));
1087
            $this->assertEquals('/some-subdir/some-page/nested', $_SERVER['REQUEST_URI']);
1088
        }, 'some-page/nested?query=1');
1089
    }
1090
}
1091