[!!!][TASK] Extract testing framework for TYPO3
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Http / UriTest.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\Uri;
18
19 /**
20 * Testcase for \TYPO3\CMS\Core\Http\Uri
21 *
22 * Adapted from https://github.com/phly/http/
23 */
24 class UriTest extends \TYPO3\CMS\Components\TestingFramework\Core\UnitTestCase
25 {
26 /**
27 * @test
28 */
29 public function constructorSetsAllProperties()
30 {
31 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
32 $this->assertEquals('https', $uri->getScheme());
33 $this->assertEquals('user:pass', $uri->getUserInfo());
34 $this->assertEquals('local.example.com', $uri->getHost());
35 $this->assertEquals(3001, $uri->getPort());
36 $this->assertEquals('user:pass@local.example.com:3001', $uri->getAuthority());
37 $this->assertEquals('/foo', $uri->getPath());
38 $this->assertEquals('bar=baz', $uri->getQuery());
39 $this->assertEquals('quz', $uri->getFragment());
40 }
41
42 /**
43 * @test
44 */
45 public function canSerializeToString()
46 {
47 $url = 'https://user:pass@local.example.com:3001/foo?bar=baz#quz';
48 $uri = new Uri($url);
49 $this->assertEquals($url, (string) $uri);
50 }
51
52 /**
53 * @test
54 */
55 public function withSchemeReturnsNewInstanceWithNewScheme()
56 {
57 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
58 $new = $uri->withScheme('http');
59 $this->assertNotSame($uri, $new);
60 $this->assertEquals('http', $new->getScheme());
61 $this->assertEquals('http://user:pass@local.example.com:3001/foo?bar=baz#quz', (string) $new);
62 }
63
64 /**
65 * @test
66 */
67 public function withUserInfoReturnsNewInstanceWithProvidedUser()
68 {
69 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
70 $new = $uri->withUserInfo('matthew');
71 $this->assertNotSame($uri, $new);
72 $this->assertEquals('matthew', $new->getUserInfo());
73 $this->assertEquals('https://matthew@local.example.com:3001/foo?bar=baz#quz', (string) $new);
74 }
75
76 /**
77 * @test
78 */
79 public function withUserInfoReturnsNewInstanceWithProvidedUserAndPassword()
80 {
81 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
82 $new = $uri->withUserInfo('matthew', 'zf2');
83 $this->assertNotSame($uri, $new);
84 $this->assertEquals('matthew:zf2', $new->getUserInfo());
85 $this->assertEquals('https://matthew:zf2@local.example.com:3001/foo?bar=baz#quz', (string) $new);
86 }
87
88 /**
89 * @test
90 */
91 public function withHostReturnsNewInstanceWithProvidedHost()
92 {
93 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
94 $new = $uri->withHost('framework.zend.com');
95 $this->assertNotSame($uri, $new);
96 $this->assertEquals('framework.zend.com', $new->getHost());
97 $this->assertEquals('https://user:pass@framework.zend.com:3001/foo?bar=baz#quz', (string) $new);
98 }
99
100 /**
101 * @return array
102 */
103 public function validPortsDataProvider()
104 {
105 return [
106 'int' => [3000],
107 'string' => ['3000']
108 ];
109 }
110
111 /**
112 * @dataProvider validPortsDataProvider
113 * @test
114 */
115 public function withPortReturnsNewInstanceWithProvidedPort($port)
116 {
117 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
118 $new = $uri->withPort($port);
119 $this->assertNotSame($uri, $new);
120 $this->assertEquals($port, $new->getPort());
121 $this->assertEquals(
122 sprintf('https://user:pass@local.example.com:%d/foo?bar=baz#quz', $port),
123 (string) $new
124 );
125 }
126
127 /**
128 * @return array
129 */
130 public function invalidPortsDataProviderType()
131 {
132 return [
133 'null' => [null],
134 'false' => [false],
135 'string' => ['string'],
136 'array' => [[3000]],
137 'object' => [(object) [3000]],
138 ];
139 }
140
141 /**
142 * @dataProvider invalidPortsDataProviderType
143 * @test
144 */
145 public function withPortRaisesExceptionForInvalidPortsByType($port)
146 {
147 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
148 $this->expectException(\InvalidArgumentException::class);
149 $this->expectExceptionCode(1436717324);
150 $uri->withPort($port);
151 }
152
153 /**
154 * @return array
155 */
156 public function invalidPortsDataProviderRange()
157 {
158 return [
159 'zero' => [0],
160 'too-small' => [-1],
161 'too-big' => [65536],
162 ];
163 }
164
165 /**
166 * @test
167 * @todo: Currently, boolean true is interpreted as 1 by canBeInterpretedAsInteger().
168 * @todo: This test shows that, but there is an inconsistency and maybe it would be better
169 * @todo: if the code would not accept 'true' as valid port but throw an exception instead.
170 * @todo: If that is changed, 'true' should be added to the 'invalid type' data provider above.
171 */
172 public function withPortAcceptsBooleanTrueAsPortOne()
173 {
174 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
175 $new = $uri->withPort(true);
176 $this->assertNotSame($uri, $new);
177 $this->assertEquals(1, $new->getPort());
178 }
179
180 /**
181 * @dataProvider invalidPortsDataProviderRange
182 * @test
183 */
184 public function withPortRaisesExceptionForInvalidPortsByRange($port)
185 {
186 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
187 $this->expectException(\InvalidArgumentException::class);
188 $this->expectExceptionCode(1436717326);
189 $uri->withPort($port);
190 }
191
192 /**
193 * @test
194 */
195 public function withPathReturnsNewInstanceWithProvidedPath()
196 {
197 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
198 $new = $uri->withPath('/bar/baz');
199 $this->assertNotSame($uri, $new);
200 $this->assertEquals('/bar/baz', $new->getPath());
201 $this->assertEquals('https://user:pass@local.example.com:3001/bar/baz?bar=baz#quz', (string) $new);
202 }
203
204 /**
205 * @return array
206 */
207 public function invalidPathsDataProvider()
208 {
209 return [
210 'null' => [null],
211 'true' => [true],
212 'false' => [false],
213 'array' => [['/bar/baz']],
214 'object' => [(object) ['/bar/baz']],
215 ];
216 }
217
218 /**
219 * @dataProvider invalidPathsDataProvider
220 * @test
221 */
222 public function withPathRaisesExceptionForInvalidPaths($path)
223 {
224 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
225 $this->expectException(\InvalidArgumentException::class);
226 $this->expectExceptionCode(1436717328);
227 $uri->withPath($path);
228 }
229
230 /**
231 * @test
232 */
233 public function withPathRaisesExceptionForInvalidPathsWithQuery()
234 {
235 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
236 $this->expectException(\InvalidArgumentException::class);
237 $this->expectExceptionCode(1436717330);
238 $uri->withPath('/bar/baz?bat=quz');
239 }
240
241 /**
242 * @test
243 */
244 public function withPathRaisesExceptionForInvalidPathsWithFragment()
245 {
246 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
247 $this->expectException(\InvalidArgumentException::class);
248 $this->expectExceptionCode(1436717332);
249 $uri->withPath('/bar/baz#bat');
250 }
251
252 /**
253 * @test
254 */
255 public function withQueryReturnsNewInstanceWithProvidedQuery()
256 {
257 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
258 $new = $uri->withQuery('baz=bat');
259 $this->assertNotSame($uri, $new);
260 $this->assertEquals('baz=bat', $new->getQuery());
261 $this->assertEquals('https://user:pass@local.example.com:3001/foo?baz=bat#quz', (string) $new);
262 }
263
264 /**
265 * @return array
266 */
267 public function invalidQueryStringsDataProvider()
268 {
269 return [
270 'null' => [null],
271 'true' => [true],
272 'false' => [false],
273 'array' => [['baz=bat']],
274 'object' => [(object) ['baz=bat']],
275 ];
276 }
277
278 /**
279 * @dataProvider invalidQueryStringsDataProvider
280 * @test
281 */
282 public function withQueryRaisesExceptionForInvalidQueryStringsByType($query)
283 {
284 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
285 $this->expectException(\InvalidArgumentException::class);
286 $this->expectExceptionCode(1436717334);
287 $uri->withQuery($query);
288 }
289
290 /**
291 * @test
292 */
293 public function withQueryRaisesExceptionForInvalidQueryStringsByFragment()
294 {
295 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
296 $this->expectException(\InvalidArgumentException::class);
297 $this->expectExceptionCode(1436717336);
298 $uri->withQuery('baz=bat#quz');
299 }
300
301 /**
302 * @test
303 */
304 public function withFragmentReturnsNewInstanceWithProvidedFragment()
305 {
306 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
307 $new = $uri->withFragment('qat');
308 $this->assertNotSame($uri, $new);
309 $this->assertEquals('qat', $new->getFragment());
310 $this->assertEquals('https://user:pass@local.example.com:3001/foo?bar=baz#qat', (string) $new);
311 }
312
313 /**
314 * @return array
315 */
316 public function authorityInfoDataProvider()
317 {
318 return [
319 'host-only' => ['http://foo.com/bar', 'foo.com'],
320 'host-port' => ['http://foo.com:3000/bar', 'foo.com:3000'],
321 'user-host' => ['http://me@foo.com/bar', 'me@foo.com'],
322 'user-host-port' => ['http://me@foo.com:3000/bar', 'me@foo.com:3000'],
323 ];
324 }
325
326 /**
327 * @dataProvider authorityInfoDataProvider
328 * @test
329 */
330 public function getAuthorityReturnsExpectedValues($url, $expected)
331 {
332 $uri = new Uri($url);
333 $this->assertEquals($expected, $uri->getAuthority());
334 }
335
336 /**
337 * @test
338 */
339 public function canEmitOriginFormUrl()
340 {
341 $url = '/foo/bar?baz=bat';
342 $uri = new Uri($url);
343 $this->assertEquals($url, (string) $uri);
344 }
345
346 /**
347 * @test
348 */
349 public function settingEmptyPathOnAbsoluteUriReturnsAnEmptyPath()
350 {
351 $uri = new Uri('http://example.com/foo');
352 $new = $uri->withPath('');
353 $this->assertEquals('', $new->getPath());
354 }
355
356 /**
357 * @test
358 */
359 public function stringRepresentationOfAbsoluteUriWithNoPathSetsAnEmptyPath()
360 {
361 $uri = new Uri('http://example.com');
362 $this->assertEquals('http://example.com', (string) $uri);
363 }
364
365 /**
366 * @test
367 */
368 public function getPathOnOriginFormRemainsAnEmptyPath()
369 {
370 $uri = new Uri('?foo=bar');
371 $this->assertEquals('', $uri->getPath());
372 }
373
374 /**
375 * @test
376 */
377 public function stringRepresentationOfOriginFormWithNoPathRetainsEmptyPath()
378 {
379 $uri = new Uri('?foo=bar');
380 $this->assertEquals('?foo=bar', (string) $uri);
381 }
382
383 /**
384 * @return array
385 */
386 public function invalidConstructorUrisDataProvider()
387 {
388 return [
389 'null' => [null],
390 'true' => [true],
391 'false' => [false],
392 'int' => [1],
393 'float' => [1.1],
394 'array' => [['http://example.com/']],
395 'object' => [(object) ['uri' => 'http://example.com/']],
396 ];
397 }
398
399 /**
400 * @dataProvider invalidConstructorUrisDataProvider
401 */
402 public function constructorRaisesExceptionForNonStringURI($uri)
403 {
404 $this->expectException(\InvalidArgumentException::class);
405 new Uri($uri);
406 }
407
408 /**
409 * @test
410 */
411 public function constructorRaisesExceptionForSeriouslyMalformedURI()
412 {
413 $this->expectException(\InvalidArgumentException::class);
414 new Uri('http:///www.php-fig.org/');
415 }
416
417 /**
418 * @test
419 */
420 public function withSchemeStripsOffDelimiter()
421 {
422 $uri = new Uri('http://example.com');
423 $new = $uri->withScheme('https://');
424 $this->assertEquals('https', $new->getScheme());
425 }
426
427 /**
428 * @return array
429 */
430 public function invalidSchemesDataProvider()
431 {
432 return [
433 'mailto' => ['mailto'],
434 'ftp' => ['ftp'],
435 'telnet' => ['telnet'],
436 'ssh' => ['ssh'],
437 'git' => ['git'],
438 ];
439 }
440
441 /**
442 * @dataProvider invalidSchemesDataProvider
443 * @test
444 */
445 public function constructWithUnsupportedSchemeRaisesAnException($scheme)
446 {
447 $this->expectException(\InvalidArgumentException::class);
448 $this->expectExceptionCode(1436717338);
449 new Uri($scheme . '://example.com');
450 }
451
452 /**
453 * @dataProvider invalidSchemesDataProvider
454 * @test
455 */
456 public function withSchemeUsingUnsupportedSchemeRaisesAnException($scheme)
457 {
458 $uri = new Uri('http://example.com');
459 $this->expectException(\InvalidArgumentException::class);
460 $this->expectExceptionCode(1436717338);
461 $uri->withScheme($scheme);
462 }
463
464 /**
465 * @test
466 */
467 public function withPathIsNotPrefixedWithSlashIfSetWithoutOne()
468 {
469 $uri = new Uri('http://example.com');
470 $new = $uri->withPath('foo/bar');
471 $this->assertEquals('foo/bar', $new->getPath());
472 }
473
474 /**
475 * @test
476 */
477 public function withPathNotSlashPrefixedIsEmittedWithSlashDelimiterWhenUriIsCastToString()
478 {
479 $uri = new Uri('http://example.com');
480 $new = $uri->withPath('foo/bar');
481 $this->assertEquals('http://example.com/foo/bar', $new->__toString());
482 }
483
484 /**
485 * @test
486 */
487 public function withQueryStripsQueryPrefixIfPresent()
488 {
489 $uri = new Uri('http://example.com');
490 $new = $uri->withQuery('?foo=bar');
491 $this->assertEquals('foo=bar', $new->getQuery());
492 }
493
494 /**
495 * @test
496 */
497 public function withFragmentStripsFragmentPrefixIfPresent()
498 {
499 $uri = new Uri('http://example.com');
500 $new = $uri->withFragment('#/foo/bar');
501 $this->assertEquals('/foo/bar', $new->getFragment());
502 }
503
504 /**
505 * @return array
506 */
507 public function standardSchemePortCombinationsDataProvider()
508 {
509 return [
510 'http' => ['http', 80],
511 'https' => ['https', 443],
512 ];
513 }
514
515 /**
516 * @dataProvider standardSchemePortCombinationsDataProvider
517 * @test
518 */
519 public function getAuthorityOmitsPortForStandardSchemePortCombinations($scheme, $port)
520 {
521 $uri = (new Uri())
522 ->withHost('example.com')
523 ->withScheme($scheme)
524 ->withPort($port);
525 $this->assertEquals('example.com', $uri->getAuthority());
526 }
527
528 /**
529 * @test
530 */
531 public function getPathIsProperlyEncoded()
532 {
533 $uri = (new Uri())->withPath('/foo^bar');
534 $expected = '/foo%5Ebar';
535 $this->assertEquals($expected, $uri->getPath());
536 }
537
538 /**
539 * @test
540 */
541 public function getPathDoesNotBecomeDoubleEncoded()
542 {
543 $uri = (new Uri())->withPath('/foo%5Ebar');
544 $expected = '/foo%5Ebar';
545 $this->assertEquals($expected, $uri->getPath());
546 }
547
548 /**
549 * @return array
550 */
551 public function queryStringsForEncodingDataProvider()
552 {
553 return [
554 'key-only' => ['k^ey', 'k%5Eey'],
555 'key-value' => ['k^ey=valu`', 'k%5Eey=valu%60'],
556 'array-key-only' => ['key[]', 'key%5B%5D'],
557 'array-key-value' => ['key[]=valu`', 'key%5B%5D=valu%60'],
558 'complex' => ['k^ey&key[]=valu`&f<>=`bar', 'k%5Eey&key%5B%5D=valu%60&f%3C%3E=%60bar'],
559 ];
560 }
561
562 /**
563 * @dataProvider queryStringsForEncodingDataProvider
564 * @test
565 */
566 public function getQueryIsProperlyEncoded($query, $expected)
567 {
568 $uri = (new Uri())->withQuery($query);
569 $this->assertEquals($expected, $uri->getQuery());
570 }
571
572 /**
573 * @dataProvider queryStringsForEncodingDataProvider
574 * @test
575 */
576 public function getQueryIsNotDoubleEncoded($query, $expected)
577 {
578 $uri = (new Uri())->withQuery($expected);
579 $this->assertEquals($expected, $uri->getQuery());
580 }
581
582 /**
583 * @test
584 */
585 public function getFragmentIsProperlyEncoded()
586 {
587 $uri = (new Uri())->withFragment('/p^th?key^=`bar#b@z');
588 $expected = '/p%5Eth?key%5E=%60bar%23b@z';
589 $this->assertEquals($expected, $uri->getFragment());
590 }
591
592 /**
593 * @test
594 */
595 public function getFragmentIsNotDoubleEncoded()
596 {
597 $expected = '/p%5Eth?key%5E=%60bar%23b@z';
598 $uri = (new Uri())->withFragment($expected);
599 $this->assertEquals($expected, $uri->getFragment());
600 }
601 }