Completed
Push — master ( daed8c...cf758d )
by Damian
08:03
created

DirectorTest::testGlobalMiddleware()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 17
nc 1
nop 0
dl 0
loc 30
rs 8.8571
c 0
b 0
f 0
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\HTTPRequestBuilder;
8
use SilverStripe\Control\HTTPResponse;
9
use SilverStripe\Control\HTTPResponse_Exception;
10
use SilverStripe\Control\Middleware\HTTPMiddleware;
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\Injector\Injector;
17
use SilverStripe\Core\Kernel;
18
use SilverStripe\Dev\SapphireTest;
19
20
/**
21
 * @todo test Director::alternateBaseFolder()
22
 */
23
class DirectorTest extends SapphireTest
24
{
25
    protected static $extra_controllers = [
26
        TestController::class,
27
    ];
28
29
    protected function setUp()
30
    {
31
        parent::setUp();
32
        Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
33
        $this->expectedRedirect = null;
34
    }
35
36
    protected function getExtraRoutes()
37
    {
38
        $rules = parent::getExtraRoutes();
39
40
        $rules['DirectorTestRule/$Action/$ID/$OtherID'] = TestController::class;
41
        $rules['en-nz/$Action/$ID/$OtherID'] = [
42
            'Controller' => TestController::class,
43
            'Locale' => 'en_NZ',
44
        ];
45
        return $rules;
46
    }
47
48
    protected function setUpRoutes()
49
    {
50
        // Don't merge with any existing rules
51
        Director::config()->set('rules', $this->getExtraRoutes());
52
    }
53
54
    public function testFileExists()
55
    {
56
        $tempFileName = 'DirectorTest_testFileExists.tmp';
57
        $tempFilePath = TEMP_FOLDER . '/' . $tempFileName;
58
59
        // create temp file
60
        file_put_contents($tempFilePath, '');
61
62
        $this->assertTrue(
63
            Director::fileExists($tempFilePath),
64
            'File exist check with absolute path'
65
        );
66
67
        $this->assertTrue(
68
            Director::fileExists($tempFilePath . '?queryparams=1&foo[bar]=bar'),
69
            'File exist check with query params ignored'
70
        );
71
72
        unlink($tempFilePath);
73
    }
74
75
    public function testAbsoluteURL()
0 ignored issues
show
Coding Style introduced by
testAbsoluteURL uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
76
    {
77
        Director::config()->set('alternate_base_url', 'http://www.mysite.com/mysite/');
78
        $_SERVER['REQUEST_URI'] = "http://www.mysite.com/mysite/sub-page/";
79
80
        //test empty / local urls
81
        foreach (array('', './', '.') as $url) {
82
            $this->assertEquals("http://www.mysite.com/mysite/", Director::absoluteURL($url, Director::BASE));
83
            $this->assertEquals("http://www.mysite.com/", Director::absoluteURL($url, Director::ROOT));
84
            $this->assertEquals("http://www.mysite.com/mysite/sub-page/", Director::absoluteURL($url, Director::REQUEST));
85
        }
86
87
        // Test site root url
88
        $this->assertEquals("http://www.mysite.com/", Director::absoluteURL('/'));
89
90
        // Test Director::BASE
91
        $this->assertEquals('http://www.mysite.com/', Director::absoluteURL('http://www.mysite.com/', Director::BASE));
92
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::BASE));
93
        $this->assertEquals("http://www.mysite.com/test", Director::absoluteURL("http://www.mysite.com/test", Director::BASE));
94
        $this->assertEquals("http://www.mysite.com/root", Director::absoluteURL("/root", Director::BASE));
95
        $this->assertEquals("http://www.mysite.com/root/url", Director::absoluteURL("/root/url", Director::BASE));
96
97
        // Test Director::ROOT
98
        $this->assertEquals('http://www.mysite.com/', Director::absoluteURL('http://www.mysite.com/', Director::ROOT));
99
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::ROOT));
100
        $this->assertEquals("http://www.mysite.com/test", Director::absoluteURL("http://www.mysite.com/test", Director::ROOT));
101
        $this->assertEquals("http://www.mysite.com/root", Director::absoluteURL("/root", Director::ROOT));
102
        $this->assertEquals("http://www.mysite.com/root/url", Director::absoluteURL("/root/url", Director::ROOT));
103
104
        // Test Director::REQUEST
105
        $this->assertEquals('http://www.mysite.com/', Director::absoluteURL('http://www.mysite.com/', Director::REQUEST));
106
        $this->assertEquals('http://www.mytest.com', Director::absoluteURL('http://www.mytest.com', Director::REQUEST));
107
        $this->assertEquals("http://www.mysite.com/test", Director::absoluteURL("http://www.mysite.com/test", Director::REQUEST));
108
        $this->assertEquals("http://www.mysite.com/root", Director::absoluteURL("/root", Director::REQUEST));
109
        $this->assertEquals("http://www.mysite.com/root/url", Director::absoluteURL("/root/url", Director::REQUEST));
110
111
        // Test evaluating relative urls relative to base (default)
112
        $this->assertEquals("http://www.mysite.com/mysite/test", Director::absoluteURL("test"));
113
        $this->assertEquals("http://www.mysite.com/mysite/test/url", Director::absoluteURL("test/url"));
114
        $this->assertEquals("http://www.mysite.com/mysite/test", Director::absoluteURL("test", Director::BASE));
115
        $this->assertEquals("http://www.mysite.com/mysite/test/url", Director::absoluteURL("test/url", Director::BASE));
116
117
        // Test evaluting relative urls relative to root
118
        $this->assertEquals("http://www.mysite.com/test", Director::absoluteURL("test", Director::ROOT));
119
        $this->assertEquals("http://www.mysite.com/test/url", Director::absoluteURL("test/url", Director::ROOT));
120
121
        // Test relative to requested page
122
        $this->assertEquals("http://www.mysite.com/mysite/sub-page/test", Director::absoluteURL("test", Director::REQUEST));
123
        $this->assertEquals("http://www.mysite.com/mysite/sub-page/test/url", Director::absoluteURL("test/url", Director::REQUEST));
124
125
        // Test that javascript links are not left intact
126
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('javascript:alert("attack")'));
127
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('javascript:alert("attack")'));
128
        $this->assertStringStartsNotWith('javascript', Director::absoluteURL('alert("attack")'));
129
        $this->assertStringStartsNotWith('alert', Director::absoluteURL('alert("attack")'));
130
    }
131
132
    public function testAlternativeBaseURL()
0 ignored issues
show
Coding Style introduced by
testAlternativeBaseURL uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
133
    {
134
        // relative base URLs - you should end them in a /
135
        Director::config()->set('alternate_base_url', '/relativebase/');
136
        $_SERVER['HTTP_HOST'] = 'www.somesite.com';
137
        $_SERVER['REQUEST_URI'] = "/relativebase/sub-page/";
138
139
        $this->assertEquals('/relativebase/', Director::baseURL());
140
        $this->assertEquals('http://www.somesite.com/relativebase/', Director::absoluteBaseURL());
141
        $this->assertEquals(
142
            'http://www.somesite.com/relativebase/subfolder/test',
143
            Director::absoluteURL('subfolder/test')
144
        );
145
146
        // absolute base URLS with subdirectory - You should end them in a /
147
        Director::config()->set('alternate_base_url', 'http://www.example.org/relativebase/');
148
        $_SERVER['REQUEST_URI'] = "http://www.example.org/relativebase/sub-page/";
149
        $this->assertEquals('/relativebase/', Director::baseURL()); // Non-absolute url
150
        $this->assertEquals('http://www.example.org/relativebase/', Director::absoluteBaseURL());
151
        $this->assertEquals('http://www.example.org/relativebase/sub-page/', Director::absoluteURL('', Director::REQUEST));
152
        $this->assertEquals('http://www.example.org/relativebase/', Director::absoluteURL('', Director::BASE));
153
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
154
        $this->assertEquals(
155
            'http://www.example.org/relativebase/sub-page/subfolder/test',
156
            Director::absoluteURL('subfolder/test', Director::REQUEST)
157
        );
158
        $this->assertEquals(
159
            'http://www.example.org/subfolder/test',
160
            Director::absoluteURL('subfolder/test', Director::ROOT)
161
        );
162
        $this->assertEquals(
163
            'http://www.example.org/relativebase/subfolder/test',
164
            Director::absoluteURL('subfolder/test', Director::BASE)
165
        );
166
167
        // absolute base URLs - you should end them in a /
168
        Director::config()->set('alternate_base_url', 'http://www.example.org/');
169
        $_SERVER['REQUEST_URI'] = "http://www.example.org/sub-page/";
170
        $this->assertEquals('/', Director::baseURL()); // Non-absolute url
171
        $this->assertEquals('http://www.example.org/', Director::absoluteBaseURL());
172
        $this->assertEquals('http://www.example.org/sub-page/', Director::absoluteURL('', Director::REQUEST));
173
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::BASE));
174
        $this->assertEquals('http://www.example.org/', Director::absoluteURL('', Director::ROOT));
175
        $this->assertEquals(
176
            'http://www.example.org/sub-page/subfolder/test',
177
            Director::absoluteURL('subfolder/test', Director::REQUEST)
178
        );
179
        $this->assertEquals(
180
            'http://www.example.org/subfolder/test',
181
            Director::absoluteURL('subfolder/test', Director::ROOT)
182
        );
183
        $this->assertEquals(
184
            'http://www.example.org/subfolder/test',
185
            Director::absoluteURL('subfolder/test', Director::BASE)
186
        );
187
    }
188
189
    /**
190
     * Tests that {@link Director::is_absolute()} works under different environment types
191
     */
192
    public function testIsAbsolute()
193
    {
194
        $expected = array (
195
            'C:/something' => true,
196
            'd:\\'         => true,
197
            'e/'           => false,
198
            's:/directory' => true,
199
            '/var/www'     => true,
200
            '\\Something'  => true,
201
            'something/c:' => false,
202
            'folder'       => false,
203
            'a/c:/'        => false
204
        );
205
206
        foreach ($expected as $path => $result) {
207
            $this->assertEquals(Director::is_absolute($path), $result, "Test result for $path");
208
        }
209
    }
210
211
    public function testIsAbsoluteUrl()
212
    {
213
        $this->assertTrue(Director::is_absolute_url('http://test.com/testpage'));
214
        $this->assertTrue(Director::is_absolute_url('ftp://test.com'));
215
        $this->assertFalse(Director::is_absolute_url('test.com/testpage'));
216
        $this->assertFalse(Director::is_absolute_url('/relative'));
217
        $this->assertFalse(Director::is_absolute_url('relative'));
218
        $this->assertFalse(Director::is_absolute_url("/relative/?url=http://foo.com"));
219
        $this->assertFalse(Director::is_absolute_url("/relative/#http://foo.com"));
220
        $this->assertTrue(Director::is_absolute_url("https://test.com/?url=http://foo.com"));
221
        $this->assertTrue(Director::is_absolute_url("trickparseurl:http://test.com"));
222
        $this->assertTrue(Director::is_absolute_url('//test.com'));
223
        $this->assertTrue(Director::is_absolute_url('/////test.com'));
224
        $this->assertTrue(Director::is_absolute_url('  ///test.com'));
225
        $this->assertTrue(Director::is_absolute_url('http:test.com'));
226
        $this->assertTrue(Director::is_absolute_url('//http://test.com'));
227
    }
228
229
    public function testIsRelativeUrl()
230
    {
231
        $this->assertFalse(Director::is_relative_url('http://test.com'));
232
        $this->assertFalse(Director::is_relative_url('https://test.com'));
233
        $this->assertFalse(Director::is_relative_url('   https://test.com/testpage   '));
234
        $this->assertTrue(Director::is_relative_url('test.com/testpage'));
235
        $this->assertFalse(Director::is_relative_url('ftp://test.com'));
236
        $this->assertTrue(Director::is_relative_url('/relative'));
237
        $this->assertTrue(Director::is_relative_url('relative'));
238
        $this->assertTrue(Director::is_relative_url('/relative/?url=http://test.com'));
239
        $this->assertTrue(Director::is_relative_url('/relative/#=http://test.com'));
240
    }
241
242
    public function testMakeRelative()
243
    {
244
        $siteUrl = Director::absoluteBaseURL();
245
        $siteUrlNoProtocol = preg_replace('/https?:\/\//', '', $siteUrl);
246
247
        $this->assertEquals(Director::makeRelative("$siteUrl"), '');
248
        $this->assertEquals(Director::makeRelative("https://$siteUrlNoProtocol"), '');
249
        $this->assertEquals(Director::makeRelative("http://$siteUrlNoProtocol"), '');
250
251
        $this->assertEquals(Director::makeRelative("   $siteUrl/testpage   "), 'testpage');
252
        $this->assertEquals(Director::makeRelative("$siteUrlNoProtocol/testpage"), 'testpage');
253
254
        $this->assertEquals(Director::makeRelative('ftp://test.com'), 'ftp://test.com');
255
        $this->assertEquals(Director::makeRelative('http://test.com'), 'http://test.com');
256
257
        $this->assertEquals(Director::makeRelative('relative'), 'relative');
258
        $this->assertEquals(Director::makeRelative("$siteUrl/?url=http://test.com"), '?url=http://test.com');
259
260
        $this->assertEquals("test", Director::makeRelative("https://".$siteUrlNoProtocol."/test"));
261
        $this->assertEquals("test", Director::makeRelative("http://".$siteUrlNoProtocol."/test"));
262
    }
263
264
    /**
265
     * Mostly tested by {@link testIsRelativeUrl()},
266
     * just adding the host name matching aspect here.
267
     */
268
    public function testIsSiteUrl()
269
    {
270
        $this->assertFalse(Director::is_site_url("http://test.com"));
271
        $this->assertTrue(Director::is_site_url(Director::absoluteBaseURL()));
0 ignored issues
show
Security Bug introduced by
It seems like \SilverStripe\Control\Director::absoluteBaseURL() targeting SilverStripe\Control\Director::absoluteBaseURL() can also be of type false; however, SilverStripe\Control\Director::is_site_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
272
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . Director::absoluteBaseURL()));
273
        $this->assertFalse(Director::is_site_url("http://test.com?url=" . urlencode(Director::absoluteBaseURL())));
274
        $this->assertFalse(Director::is_site_url("//test.com?url=" . Director::absoluteBaseURL()));
275
    }
276
277
    /**
278
     * Tests isDev, isTest, isLive set from querystring
279
     */
280
    public function testQueryIsEnvironment()
0 ignored issues
show
Coding Style introduced by
testQueryIsEnvironment uses the super-global variable $_SESSION which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
testQueryIsEnvironment uses the super-global variable $_GET which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
281
    {
282
        if (!isset($_SESSION)) {
283
            $_SESSION = [];
284
        }
285
        // Reset
286
        unset($_SESSION['isDev']);
287
        unset($_SESSION['isLive']);
288
        unset($_GET['isTest']);
289
        unset($_GET['isDev']);
290
291
        /** @var Kernel $kernel */
292
        $kernel = Injector::inst()->get(Kernel::class);
293
        $kernel->setEnvironment(null);
294
295
        // Test isDev=1
296
        $_GET['isDev'] = '1';
297
        $this->assertTrue(Director::isDev());
298
        $this->assertFalse(Director::isTest());
299
        $this->assertFalse(Director::isLive());
300
301
        // Test persistence
302
        unset($_GET['isDev']);
303
        $this->assertTrue(Director::isDev());
304
        $this->assertFalse(Director::isTest());
305
        $this->assertFalse(Director::isLive());
306
307
        // Test change to isTest
308
        $_GET['isTest'] = '1';
309
        $this->assertFalse(Director::isDev());
310
        $this->assertTrue(Director::isTest());
311
        $this->assertFalse(Director::isLive());
312
313
        // Test persistence
314
        unset($_GET['isTest']);
315
        $this->assertFalse(Director::isDev());
316
        $this->assertTrue(Director::isTest());
317
        $this->assertFalse(Director::isLive());
318
    }
319
320
    public function testResetGlobalsAfterTestRequest()
0 ignored issues
show
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_GET which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
testResetGlobalsAfterTestRequest uses the super-global variable $_COOKIE which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
321
    {
322
        $_GET = array('somekey' => 'getvalue');
323
        $_POST = array('somekey' => 'postvalue');
324
        $_COOKIE = array('somekey' => 'cookievalue');
325
326
        $cookies = Injector::inst()->createWithArgs(
327
            Cookie_Backend::class,
328
            array(array('somekey' => 'sometestcookievalue'))
329
        );
330
331
        Director::test(
332
            'errorpage?somekey=sometestgetvalue',
333
            array('somekey' => 'sometestpostvalue'),
334
            null,
335
            null,
336
            null,
337
            null,
338
            $cookies
339
        );
340
341
        $this->assertEquals(
342
            'getvalue',
343
            $_GET['somekey'],
344
            '$_GET reset to original value after Director::test()'
345
        );
346
        $this->assertEquals(
347
            'postvalue',
348
            $_POST['somekey'],
349
            '$_POST reset to original value after Director::test()'
350
        );
351
        $this->assertEquals(
352
            'cookievalue',
353
            $_COOKIE['somekey'],
354
            '$_COOKIE reset to original value after Director::test()'
355
        );
356
    }
357
358
    public function providerTestTestRequestCarriesGlobals()
359
    {
360
        $tests = [];
361
        $fixture = [ 'somekey' => 'sometestvalue' ];
362
        foreach (array('get', 'post') as $method) {
363
            foreach (array('return%sValue', 'returnRequestValue', 'returnCookieValue') as $testfunction) {
364
                $url = 'TestController/' . sprintf($testfunction, ucfirst($method))
365
                    . '?' . http_build_query($fixture);
366
                $tests[] = [$url, $fixture, $method];
367
            }
368
        }
369
        return $tests;
370
    }
371
372
    /**
373
     * @dataProvider providerTestTestRequestCarriesGlobals
374
     * @param $url
375
     * @param $fixture
376
     * @param $method
377
     */
378
    public function testTestRequestCarriesGlobals($url, $fixture, $method)
379
    {
380
        $getresponse = Director::test(
381
            $url,
382
            $fixture,
383
            null,
384
            strtoupper($method),
385
            null,
386
            null,
387
            Injector::inst()->createWithArgs(Cookie_Backend::class, array($fixture))
388
        );
389
390
        $this->assertInstanceOf(HTTPResponse::class, $getresponse, 'Director::test() returns HTTPResponse');
391
        $this->assertEquals($fixture['somekey'], $getresponse->getBody(), "Director::test({$url}, {$method})");
392
    }
393
394
    /**
395
     * Tests that additional parameters specified in the routing table are
396
     * saved in the request
397
     */
398
    public function testRouteParams()
399
    {
400
        /** @var HTTPRequest $request */
401
        Director::test('en-nz/myaction/myid/myotherid', null, null, null, null, null, null, $request);
402
403
        $this->assertEquals(
404
            array(
405
                'Controller' => TestController::class,
406
                'Action' => 'myaction',
407
                'ID' => 'myid',
408
                'OtherID' => 'myotherid',
409
                'Locale' => 'en_NZ'
410
            ),
411
            $request->params()
412
        );
413
    }
414
415
    public function testForceSSLProtectsEntireSite()
416
    {
417
        $this->expectExceptionRedirect('https://www.mysite.com/some-url');
418
        Director::mockRequest(function () {
419
            Director::forceSSL();
420
        }, '/some-url');
421
    }
422
423
    public function testForceSSLOnTopLevelPagePattern()
424
    {
425
        // Expect admin to trigger redirect
426
        $this->expectExceptionRedirect('https://www.mysite.com/admin');
427
        Director::mockRequest(function () {
428
            Director::forceSSL(array('/^admin/'));
429
        }, '/admin');
430
    }
431
432
    public function testForceSSLOnSubPagesPattern()
433
    {
434
        // Expect to redirect to security login page
435
        $this->expectExceptionRedirect('https://www.mysite.com/Security/login');
436
        Director::mockRequest(function () {
437
            Director::forceSSL(array('/^Security/'));
438
        }, '/Security/login');
439
    }
440
441
    public function testForceSSLWithPatternDoesNotMatchOtherPages()
442
    {
443
        // Not on same url should not trigger redirect
444
        Director::mockRequest(function () {
445
            $this->assertFalse(Director::forceSSL(array('/^admin/')));
446
        }, Director::baseURL() . 'normal-page');
447
448
        // nested url should not triger redirect either
449
        Director::mockRequest(function () {
450
            $this->assertFalse(Director::forceSSL(array('/^admin/', '/^Security/')));
451
        }, Director::baseURL() . 'just-another-page/sub-url');
452
    }
453
454
    public function testForceSSLAlternateDomain()
455
    {
456
        // Ensure that forceSSL throws the appropriate exception
457
        $this->expectExceptionRedirect('https://secure.mysite.com/admin');
458
        Director::mockRequest(function (HTTPRequest $request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

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

Loading history...
459
            return Director::forceSSL(array('/^admin/'), 'secure.mysite.com');
460
        }, Director::baseURL() . 'admin');
461
    }
462
463
    /**
464
     * Set url to redirect to
465
     *
466
     * @var string
467
     */
468
    protected $expectedRedirect = null;
469
470
    /**
471
     * Expects this test to throw a HTTPResponse_Exception with the given redirect
472
     *
473
     * @param string $url
474
     */
475
    protected function expectExceptionRedirect($url)
476
    {
477
        $this->expectedRedirect = $url;
478
    }
479
480
    protected function runTest()
481
    {
482
        try {
483
            $result = parent::runTest();
484
            if ($this->expectedRedirect) {
485
                $this->fail("Expected to redirect to {$this->expectedRedirect} but no redirect found");
486
            }
487
            return $result;
488
        } catch (HTTPResponse_Exception $exception) {
489
            // Check URL
490
            if ($this->expectedRedirect) {
491
                $url = $exception->getResponse()->getHeader('Location');
492
                $this->assertEquals($this->expectedRedirect, $url, "Expected to redirect to {$this->expectedRedirect}");
493
                return null;
494
            } else {
495
                throw $exception;
496
            }
497
        }
498
    }
499
500
    /**
501
     * @covers \SilverStripe\Control\Director::extract_request_headers()
502
     */
503
    public function testExtractRequestHeaders()
504
    {
505
        $request = array(
506
            'REDIRECT_STATUS'      => '200',
507
            'HTTP_HOST'            => 'host',
508
            'HTTP_USER_AGENT'      => 'User Agent',
509
            'HTTP_ACCEPT'          => 'text/html',
510
            'HTTP_ACCEPT_LANGUAGE' => 'en-us',
511
            'HTTP_COOKIE'          => 'MyCookie=1',
512
            'SERVER_PROTOCOL'      => 'HTTP/1.1',
513
            'REQUEST_METHOD'       => 'GET',
514
            'REQUEST_URI'          => '/',
515
            'SCRIPT_NAME'          => FRAMEWORK_DIR . '/main.php',
516
            'CONTENT_TYPE'         => 'text/xml',
517
            'CONTENT_LENGTH'       => 10
518
        );
519
520
        $headers = array(
521
            'Host'            => 'host',
522
            'User-Agent'      => 'User Agent',
523
            'Accept'          => 'text/html',
524
            'Accept-Language' => 'en-us',
525
            'Cookie'          => 'MyCookie=1',
526
            'Content-Type'    => 'text/xml',
527
            'Content-Length'  => '10'
528
        );
529
530
        $this->assertEquals($headers, HTTPRequestBuilder::extractRequestHeaders($request));
531
    }
532
533
    public function testUnmatchedRequestReturns404()
534
    {
535
        // Remove non-tested rules
536
        $this->assertEquals(404, Director::test('no-route')->getStatusCode());
537
    }
538
539
    public function testIsHttps()
0 ignored issues
show
Coding Style introduced by
testIsHttps uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
540
    {
541
        // Trust all IPs for this test
542
        /** @var TrustedProxyMiddleware $trustedProxyMiddleware */
543
        $trustedProxyMiddleware
544
            = Injector::inst()->get(TrustedProxyMiddleware::class);
545
        $trustedProxyMiddleware->setTrustedProxyIPs('*');
546
547
        // Clear alternate_base_url for this test
548
        Director::config()->remove('alternate_base_url');
549
550
        // nothing available
551
        $headers = array(
552
            'HTTP_X_FORWARDED_PROTOCOL', 'HTTPS', 'SSL'
553
        );
554
        foreach ($headers as $header) {
555
            if (isset($_SERVER[$header])) {
556
                unset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']);
557
            }
558
        }
559
560
        $this->assertEquals(
561
            'no',
562
            Director::test('TestController/returnIsSSL')->getBody()
563
        );
564
565
        $this->assertEquals(
566
            'yes',
567
            Director::test(
568
                'TestController/returnIsSSL',
569
                null,
570
                null,
571
                null,
572
                null,
573
                [ 'X-Forwarded-Protocol' => 'https' ]
574
            )->getBody()
575
        );
576
577
        $this->assertEquals(
578
            'no',
579
            Director::test(
580
                'TestController/returnIsSSL',
581
                null,
582
                null,
583
                null,
584
                null,
585
                [ 'X-Forwarded-Protocol' => 'http' ]
586
            )->getBody()
587
        );
588
589
        $this->assertEquals(
590
            'no',
591
            Director::test(
592
                'TestController/returnIsSSL',
593
                null,
594
                null,
595
                null,
596
                null,
597
                [ 'X-Forwarded-Protocol' => 'ftp' ]
598
            )->getBody()
599
        );
600
601
        // https via HTTPS
602
        $_SERVER['HTTPS'] = 'true';
603
        $this->assertEquals(
604
            'yes',
605
            Director::test('TestController/returnIsSSL')->getBody()
606
        );
607
608
        $_SERVER['HTTPS'] = '1';
609
        $this->assertEquals(
610
            'yes',
611
            Director::test('TestController/returnIsSSL')->getBody()
612
        );
613
614
        $_SERVER['HTTPS'] = 'off';
615
        $this->assertEquals(
616
            'no',
617
            Director::test('TestController/returnIsSSL')->getBody()
618
        );
619
620
        // https via SSL
621
        $_SERVER['SSL'] = '';
622
        $this->assertEquals(
623
            'yes',
624
            Director::test('TestController/returnIsSSL')->getBody()
625
        );
626
    }
627
628
    public function testTestIgnoresHashes()
629
    {
630
        //test that hashes are ignored
631
        $url = "TestController/returnGetValue?somekey=key";
632
        $hash = "#test";
633
        /** @var HTTPRequest $request */
634
        $response = Director::test($url . $hash, null, null, null, null, null, null, $request);
635
        $this->assertFalse($response->isError());
636
        $this->assertEquals('key', $response->getBody());
637
        $this->assertEquals($request->getURL(true), $url);
638
639
        //test encoded hashes are accepted
640
        $url = "TestController/returnGetValue?somekey=test%23key";
641
        $response = Director::test($url, null, null, null, null, null, null, $request);
642
        $this->assertFalse($response->isError());
643
        $this->assertEquals('test#key', $response->getBody());
644
        $this->assertEquals($request->getURL(true), $url);
645
    }
646
647
    public function testRequestFilterInDirectorTest()
648
    {
649
        $filter = new DirectorTest\TestRequestFilter;
650
651
        $processor = new RequestProcessor(array($filter));
0 ignored issues
show
Deprecated Code introduced by
The class SilverStripe\Control\RequestProcessor has been deprecated with message: 4.0..5.0 Use HTTPMiddleware directly instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
652
653
        Injector::inst()->registerService($processor, RequestProcessor::class);
654
        $response = Director::test('some-dummy-url');
655
        $this->assertEquals(404, $response->getStatusCode());
656
657
        $this->assertEquals(1, $filter->preCalls);
658
        $this->assertEquals(1, $filter->postCalls);
659
660
        $filter->failPost = true;
661
662
        $response = Director::test('some-dummy-url');
663
        $this->assertEquals(500, $response->getStatusCode());
664
        $this->assertEquals(_t(Director::class.'.REQUEST_ABORTED', 'Request aborted'), $response->getBody());
665
666
        $this->assertEquals(2, $filter->preCalls);
667
        $this->assertEquals(2, $filter->postCalls);
668
669
        $filter->failPre = true;
670
671
        $response = Director::test('some-dummy-url');
672
        $this->assertEquals(400, $response->getStatusCode());
673
        $this->assertEquals(_t(Director::class.'.INVALID_REQUEST', 'Invalid request'), $response->getBody());
674
675
        $this->assertEquals(3, $filter->preCalls);
676
677
        // preCall 'true' will trigger an exception and prevent post call execution
678
        $this->assertEquals(2, $filter->postCalls);
679
    }
680
681
    public function testGlobalMiddleware()
682
    {
683
        $middleware = new DirectorTest\TestMiddleware;
684
        Director::singleton()->setMiddlewares([$middleware]);
685
686
        $response = Director::test('some-dummy-url');
687
        $this->assertEquals(404, $response->getStatusCode());
688
689
        // Both triggered
690
        $this->assertEquals(1, $middleware->preCalls);
691
        $this->assertEquals(1, $middleware->postCalls);
692
693
        $middleware->failPost = true;
694
695
        $response = Director::test('some-dummy-url');
696
        $this->assertEquals(500, $response->getStatusCode());
697
698
        // Both triggered
699
        $this->assertEquals(2, $middleware->preCalls);
700
        $this->assertEquals(2, $middleware->postCalls);
701
702
        $middleware->failPre = true;
703
704
        $response = Director::test('some-dummy-url');
705
        $this->assertEquals(400, $response->getStatusCode());
706
707
        // Pre triggered, post not
708
        $this->assertEquals(3, $middleware->preCalls);
709
        $this->assertEquals(2, $middleware->postCalls);
710
    }
711
712
    public function testRouteSpecificMiddleware()
713
    {
714
        // Inject adapter in place of controller
715
        $specificMiddleware = new DirectorTest\TestMiddleware;
716
        Injector::inst()->registerService($specificMiddleware, 'SpecificMiddleware');
717
718
        // Register adapter as factory for creating this controller
719
        Config::modify()->merge(
720
            Injector::class,
721
            'ControllerWithMiddleware',
722
            [
723
                'class' => RequestHandlerMiddlewareAdapter::class,
724
                'constructor' => [
725
                    '%$' . TestController::class
726
                ],
727
                'properties' => [
728
                    'Middlewares' => [
729
                        '%$SpecificMiddleware',
730
                    ],
731
                ],
732
            ]
733
        );
734
735
        // Global middleware
736
        $middleware = new DirectorTest\TestMiddleware;
737
        Director::singleton()->setMiddlewares([ $middleware ]);
738
739
        // URL rules, one of which has a specific middleware
740
        Config::modify()->set(
741
            Director::class,
742
            'rules',
743
            [
744
                'url-one' => TestController::class,
745
                'url-two' => [
746
                    'Controller' => 'ControllerWithMiddleware',
747
                ],
748
            ]
749
        );
750
751
        // URL without a route-specific middleware
752
        Director::test('url-one');
753
754
        // Only the global middleware triggered
755
        $this->assertEquals(1, $middleware->preCalls);
756
        $this->assertEquals(0, $specificMiddleware->postCalls);
757
758
        Director::test('url-two');
759
760
        // Both triggered on the url with the specific middleware applied
761
        $this->assertEquals(2, $middleware->preCalls);
762
        $this->assertEquals(1, $specificMiddleware->postCalls);
763
    }
764
}
765