[TASK] Shorten the extend class in all unit tests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Http / RequestTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Http\Request;
18 use TYPO3\CMS\Core\Http\Stream;
19 use TYPO3\CMS\Core\Http\Uri;
20 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
21
22 /**
23 * Testcase for \TYPO3\CMS\Core\Http\Request
24 *
25 * Adapted from https://github.com/phly/http/
26 */
27 class RequestTest extends UnitTestCase
28 {
29 /**
30 * @var Request
31 */
32 protected $request;
33
34 protected function setUp()
35 {
36 $this->request = new Request();
37 }
38
39 /**
40 * @test
41 */
42 public function getMethodIsGetByDefault()
43 {
44 $this->assertEquals('GET', $this->request->getMethod());
45 }
46
47 /**
48 * @test
49 */
50 public function getMethodMutatorReturnsCloneWithChangedMethod()
51 {
52 $request = $this->request->withMethod('GET');
53 $this->assertNotSame($this->request, $request);
54 $this->assertEquals('GET', $request->getMethod());
55 }
56
57 /**
58 * @test
59 */
60 public function getUriIsNullByDefault()
61 {
62 $this->assertNull($this->request->getUri());
63 }
64
65 /**
66 * @test
67 */
68 public function constructorRaisesExceptionForInvalidStream()
69 {
70 $this->expectException(\InvalidArgumentException::class);
71 new Request(['TOTALLY INVALID']);
72 }
73
74 /**
75 * @test
76 */
77 public function withUriReturnsNewInstanceWithNewUri()
78 {
79 $request = $this->request->withUri(new Uri('https://example.com:10082/foo/bar?baz=bat'));
80 $this->assertNotSame($this->request, $request);
81 $request2 = $request->withUri(new Uri('/baz/bat?foo=bar'));
82 $this->assertNotSame($this->request, $request2);
83 $this->assertNotSame($request, $request2);
84 $this->assertEquals('/baz/bat?foo=bar', (string)$request2->getUri());
85 }
86
87 /**
88 * @test
89 */
90 public function constructorCanAcceptAllMessageParts()
91 {
92 $uri = new Uri('http://example.com/');
93 $body = new Stream('php://memory');
94 $headers = [
95 'x-foo' => ['bar'],
96 ];
97 $request = new Request(
98 $uri,
99 'POST',
100 $body,
101 $headers
102 );
103
104 $this->assertSame($uri, $request->getUri());
105 $this->assertEquals('POST', $request->getMethod());
106 $this->assertSame($body, $request->getBody());
107 $testHeaders = $request->getHeaders();
108 foreach ($headers as $key => $value) {
109 $this->assertArrayHasKey($key, $testHeaders);
110 $this->assertEquals($value, $testHeaders[$key]);
111 }
112 }
113
114 /**
115 * @return array
116 */
117 public function invalidRequestUriDataProvider()
118 {
119 return [
120 'true' => [true],
121 'false' => [false],
122 'int' => [1],
123 'float' => [1.1],
124 'array' => [['http://example.com']],
125 'stdClass' => [(object)['href' => 'http://example.com']],
126 ];
127 }
128
129 /**
130 * @dataProvider invalidRequestUriDataProvider
131 * @test
132 */
133 public function constructorRaisesExceptionForInvalidUri($uri)
134 {
135 $this->expectException(\InvalidArgumentException::class);
136 $this->expectExceptionCode(1436717272);
137 new Request($uri);
138 }
139
140 /**
141 * @return array
142 */
143 public function invalidRequestMethodDataProvider()
144 {
145 return [
146 'true' => [true],
147 'false' => [false],
148 'int' => [1],
149 'float' => [1.1],
150 'array' => [['POST']],
151 'stdClass' => [(object)['method' => 'POST']],
152 ];
153 }
154
155 /**
156 * @dataProvider invalidRequestMethodDataProvider
157 * @test
158 */
159 public function constructorRaisesExceptionForInvalidMethodByType($method)
160 {
161 $this->expectException(\InvalidArgumentException::class);
162 $this->expectExceptionCode(1436717274);
163 new Request(null, $method);
164 }
165
166 /**
167 * @test
168 */
169 public function constructorRaisesExceptionForInvalidMethodByString()
170 {
171 $this->expectException(\InvalidArgumentException::class);
172 $this->expectExceptionCode(1436717275);
173 new Request(null, 'BOGUS-METHOD');
174 }
175
176 /**
177 * @return array
178 */
179 public function invalidRequestBodyDataProvider()
180 {
181 return [
182 'true' => [true],
183 'false' => [false],
184 'int' => [1],
185 'float' => [1.1],
186 'array' => [['BODY']],
187 'stdClass' => [(object)['body' => 'BODY']],
188 ];
189 }
190
191 /**
192 * @dataProvider invalidRequestBodyDataProvider
193 * @test
194 */
195 public function constructorRaisesExceptionForInvalidBody($body)
196 {
197 $this->expectException(\InvalidArgumentException::class);
198 $this->expectExceptionCode(1436717271);
199 new Request(null, null, $body);
200 }
201
202 /**
203 * @test
204 */
205 public function constructorIgnoresInvalidHeaders()
206 {
207 $headers = [
208 ['INVALID'],
209 'x-invalid-null' => null,
210 'x-invalid-true' => true,
211 'x-invalid-false' => false,
212 'x-invalid-int' => 1,
213 'x-invalid-object' => (object)['INVALID'],
214 'x-valid-string' => 'VALID',
215 'x-valid-array' => ['VALID'],
216 ];
217 $expected = [
218 'x-valid-string' => ['VALID'],
219 'x-valid-array' => ['VALID'],
220 ];
221 $request = new Request(null, null, 'php://memory', $headers);
222 $this->assertEquals($expected, $request->getHeaders());
223 }
224
225 /**
226 * @test
227 */
228 public function getRequestTargetIsSlashWhenNoUriPresent()
229 {
230 $request = new Request();
231 $this->assertEquals('/', $request->getRequestTarget());
232 }
233
234 /**
235 * @test
236 */
237 public function getRequestTargetIsSlashWhenUriHasNoPathOrQuery()
238 {
239 $request = (new Request())
240 ->withUri(new Uri('http://example.com'));
241 $this->assertEquals('/', $request->getRequestTarget());
242 }
243
244 /**
245 * @return array
246 */
247 public function requestsWithUriDataProvider()
248 {
249 return [
250 'absolute-uri' => [
251 (new Request())
252 ->withUri(new Uri('https://api.example.com/user'))
253 ->withMethod('POST'),
254 '/user'
255 ],
256 'absolute-uri-with-query' => [
257 (new Request())
258 ->withUri(new Uri('https://api.example.com/user?foo=bar'))
259 ->withMethod('POST'),
260 '/user?foo=bar'
261 ],
262 'relative-uri' => [
263 (new Request())
264 ->withUri(new Uri('/user'))
265 ->withMethod('GET'),
266 '/user'
267 ],
268 'relative-uri-with-query' => [
269 (new Request())
270 ->withUri(new Uri('/user?foo=bar'))
271 ->withMethod('GET'),
272 '/user?foo=bar'
273 ],
274 ];
275 }
276
277 /**
278 * @dataProvider requestsWithUriDataProvider
279 * @test
280 */
281 public function getRequestTargetWhenUriIsPresent($request, $expected)
282 {
283 $this->assertEquals($expected, $request->getRequestTarget());
284 }
285
286 /**
287 * @return array
288 */
289 public function validRequestTargetsDataProvider()
290 {
291 return [
292 'asterisk-form' => ['*'],
293 'authority-form' => ['api.example.com'],
294 'absolute-form' => ['https://api.example.com/users'],
295 'absolute-form-query' => ['https://api.example.com/users?foo=bar'],
296 'origin-form-path-only' => ['/users'],
297 'origin-form' => ['/users?id=foo'],
298 ];
299 }
300
301 /**
302 * @dataProvider validRequestTargetsDataProvider
303 * @test
304 */
305 public function getRequestTargetCanProvideARequestTarget($requestTarget)
306 {
307 $request = (new Request())->withRequestTarget($requestTarget);
308 $this->assertEquals($requestTarget, $request->getRequestTarget());
309 }
310
311 /**
312 * @test
313 */
314 public function withRequestTargetCannotContainWhitespace()
315 {
316 $request = new Request();
317 $this->expectException(\InvalidArgumentException::class);
318 $this->expectExceptionCode(1436717273);
319 $request->withRequestTarget('foo bar baz');
320 }
321
322 /**
323 * @test
324 */
325 public function getRequestTargetDoesNotCacheBetweenInstances()
326 {
327 $request = (new Request())->withUri(new Uri('https://example.com/foo/bar'));
328 $original = $request->getRequestTarget();
329 $newRequest = $request->withUri(new Uri('http://mwop.net/bar/baz'));
330 $this->assertNotEquals($original, $newRequest->getRequestTarget());
331 }
332
333 /**
334 * @test
335 */
336 public function getRequestTargetIsResetWithNewUri()
337 {
338 $request = (new Request())->withUri(new Uri('https://example.com/foo/bar'));
339 $request->getRequestTarget();
340 $request->withUri(new Uri('http://mwop.net/bar/baz'));
341 }
342
343 /**
344 * @test
345 */
346 public function getHeadersContainsHostHeaderIfUriWithHostIsPresent()
347 {
348 $request = new Request('http://example.com');
349 $headers = $request->getHeaders();
350 $this->assertArrayHasKey('host', $headers);
351 $this->assertContains('example.com', $headers['host']);
352 }
353
354 /**
355 * @test
356 */
357 public function getHeadersContainsNoHostHeaderIfNoUriPresent()
358 {
359 $request = new Request();
360 $headers = $request->getHeaders();
361 $this->assertArrayNotHasKey('host', $headers);
362 }
363
364 /**
365 * @test
366 */
367 public function getHeadersContainsNoHostHeaderIfUriDoesNotContainHost()
368 {
369 $request = new Request(new Uri());
370 $headers = $request->getHeaders();
371 $this->assertArrayNotHasKey('host', $headers);
372 }
373
374 /**
375 * @test
376 */
377 public function getHeaderWithHostReturnsUriHostWhenPresent()
378 {
379 $request = new Request('http://example.com');
380 $header = $request->getHeader('host');
381 $this->assertEquals(['example.com'], $header);
382 }
383
384 /**
385 * @test
386 */
387 public function getHeaderWithHostReturnsEmptyArrayIfNoUriPresent()
388 {
389 $request = new Request();
390 $this->assertSame([], $request->getHeader('host'));
391 }
392
393 /**
394 * @test
395 */
396 public function getHeaderWithHostReturnsEmptyArrayIfUriDoesNotContainHost()
397 {
398 $request = new Request(new Uri());
399 $this->assertSame([], $request->getHeader('host'));
400 }
401
402 /**
403 * @test
404 */
405 public function getHeaderLineWithHostReturnsUriHostWhenPresent()
406 {
407 $request = new Request('http://example.com');
408 $header = $request->getHeaderLine('host');
409 $this->assertContains('example.com', $header);
410 }
411
412 /**
413 * @test
414 */
415 public function getHeaderLineWithHostReturnsEmptyStringIfNoUriPresent()
416 {
417 $request = new Request();
418 $this->assertSame('', $request->getHeaderLine('host'));
419 }
420
421 /**
422 * @test
423 */
424 public function getHeaderLineWithHostReturnsEmptyStringIfUriDoesNotContainHost()
425 {
426 $request = new Request(new Uri());
427 $this->assertSame('', $request->getHeaderLine('host'));
428 }
429
430 /**
431 * @test
432 */
433 public function getHeaderLineWithHostTakesPrecedenceOverModifiedUri()
434 {
435 $request = (new Request())
436 ->withAddedHeader('Host', 'example.com');
437
438 $uri = (new Uri())->withHost('www.example.com');
439 $new = $request->withUri($uri, true);
440
441 $this->assertEquals('example.com', $new->getHeaderLine('Host'));
442 }
443
444 /**
445 * @test
446 */
447 public function getHeaderLineWithHostTakesPrecedenceOverEmptyUri()
448 {
449 $request = (new Request())
450 ->withAddedHeader('Host', 'example.com');
451
452 $uri = new Uri();
453 $new = $request->withUri($uri);
454
455 $this->assertEquals('example.com', $new->getHeaderLine('Host'));
456 }
457
458 /**
459 * @test
460 */
461 public function getHeaderLineWithHostDoesNotTakePrecedenceOverHostWithPortFromUri()
462 {
463 $request = (new Request())
464 ->withAddedHeader('Host', 'example.com');
465
466 $uri = (new Uri())
467 ->withHost('www.example.com')
468 ->withPort(10081);
469 $new = $request->withUri($uri);
470
471 $this->assertEquals('www.example.com:10081', $new->getHeaderLine('Host'));
472 }
473
474 /**
475 * @return array
476 */
477 public function headersWithUpperAndLowerCaseValuesDataProvider()
478 {
479 // 'name' => [$headerName, $headerValue, $expectedValue]
480 return [
481 'Foo' => ['Foo', 'bar', 'bar'],
482 'foo' => ['foo', 'bar', 'bar'],
483 'Foo-with-array' => ['Foo', ['bar'], 'bar'],
484 'foo-with-array' => ['foo', ['bar'], 'bar'],
485 ];
486 }
487
488 /**
489 * @test
490 * @dataProvider headersWithUpperAndLowerCaseValuesDataProvider
491 */
492 public function headerCanBeRetrieved($header, $value, $expected)
493 {
494 $request = new Request(null, null, 'php://memory', [$header => $value]);
495 $this->assertEquals([$expected], $request->getHeader(strtolower($header)));
496 $this->assertEquals([$expected], $request->getHeader(strtoupper($header)));
497 }
498
499 /**
500 * @return array
501 */
502 public function headersWithInjectionVectorsDataProvider()
503 {
504 return [
505 'name-with-cr' => ["X-Foo\r-Bar", 'value'],
506 'name-with-lf' => ["X-Foo\n-Bar", 'value'],
507 'name-with-crlf' => ["X-Foo\r\n-Bar", 'value'],
508 'name-with-2crlf' => ["X-Foo\r\n\r\n-Bar", 'value'],
509 'value-with-cr' => ['X-Foo-Bar', "value\rinjection"],
510 'value-with-lf' => ['X-Foo-Bar', "value\ninjection"],
511 'value-with-crlf' => ['X-Foo-Bar', "value\r\ninjection"],
512 'value-with-2crlf' => ['X-Foo-Bar', "value\r\n\r\ninjection"],
513 'array-value-with-cr' => ['X-Foo-Bar', ["value\rinjection"]],
514 'array-value-with-lf' => ['X-Foo-Bar', ["value\ninjection"]],
515 'array-value-with-crlf' => ['X-Foo-Bar', ["value\r\ninjection"]],
516 'array-value-with-2crlf' => ['X-Foo-Bar', ["value\r\n\r\ninjection"]],
517 ];
518 }
519
520 /**
521 * @test
522 * @dataProvider headersWithInjectionVectorsDataProvider
523 */
524 public function constructorRaisesExceptionForHeadersWithCRLFVectors($name, $value)
525 {
526 $this->expectException(\InvalidArgumentException::class);
527 new Request(null, null, 'php://memory', [$name => $value]);
528 }
529 }