[FEATURE] Introduce Request/Response based on PSR-7
[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\Core\Tests\UnitTestCase {
25
26 /**
27 * @test
28 */
29 public function constructorSetsAllProperties() {
30 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
31 $this->assertEquals('https', $uri->getScheme());
32 $this->assertEquals('user:pass', $uri->getUserInfo());
33 $this->assertEquals('local.example.com', $uri->getHost());
34 $this->assertEquals(3001, $uri->getPort());
35 $this->assertEquals('user:pass@local.example.com:3001', $uri->getAuthority());
36 $this->assertEquals('/foo', $uri->getPath());
37 $this->assertEquals('bar=baz', $uri->getQuery());
38 $this->assertEquals('quz', $uri->getFragment());
39 }
40
41 /**
42 * @test
43 */
44 public function canSerializeToString() {
45 $url = 'https://user:pass@local.example.com:3001/foo?bar=baz#quz';
46 $uri = new Uri($url);
47 $this->assertEquals($url, (string) $uri);
48 }
49
50 /**
51 * @test
52 */
53 public function withSchemeReturnsNewInstanceWithNewScheme() {
54 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
55 $new = $uri->withScheme('http');
56 $this->assertNotSame($uri, $new);
57 $this->assertEquals('http', $new->getScheme());
58 $this->assertEquals('http://user:pass@local.example.com:3001/foo?bar=baz#quz', (string) $new);
59 }
60
61 /**
62 * @test
63 */
64 public function withUserInfoReturnsNewInstanceWithProvidedUser() {
65 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
66 $new = $uri->withUserInfo('matthew');
67 $this->assertNotSame($uri, $new);
68 $this->assertEquals('matthew', $new->getUserInfo());
69 $this->assertEquals('https://matthew@local.example.com:3001/foo?bar=baz#quz', (string) $new);
70 }
71
72 /**
73 * @test
74 */
75 public function withUserInfoReturnsNewInstanceWithProvidedUserAndPassword() {
76 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
77 $new = $uri->withUserInfo('matthew', 'zf2');
78 $this->assertNotSame($uri, $new);
79 $this->assertEquals('matthew:zf2', $new->getUserInfo());
80 $this->assertEquals('https://matthew:zf2@local.example.com:3001/foo?bar=baz#quz', (string) $new);
81 }
82
83 /**
84 * @test
85 */
86 public function withHostReturnsNewInstanceWithProvidedHost() {
87 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
88 $new = $uri->withHost('framework.zend.com');
89 $this->assertNotSame($uri, $new);
90 $this->assertEquals('framework.zend.com', $new->getHost());
91 $this->assertEquals('https://user:pass@framework.zend.com:3001/foo?bar=baz#quz', (string) $new);
92 }
93
94 /**
95 * @return array
96 */
97 public function validPortsDataProvider() {
98 return [
99 'int' => [3000],
100 'string' => ["3000"]
101 ];
102 }
103
104 /**
105 * @dataProvider validPortsDataProvider
106 * @test
107 */
108 public function withPortReturnsNewInstanceWithProvidedPort($port) {
109 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
110 $new = $uri->withPort($port);
111 $this->assertNotSame($uri, $new);
112 $this->assertEquals($port, $new->getPort());
113 $this->assertEquals(
114 sprintf('https://user:pass@local.example.com:%d/foo?bar=baz#quz', $port),
115 (string) $new
116 );
117 }
118
119 /**
120 * @return array
121 */
122 public function invalidPortsDataProvider() {
123 return [
124 'null' => [NULL],
125 'true' => [TRUE],
126 'false' => [FALSE],
127 'string' => ['string'],
128 'array' => [[3000]],
129 'object' => [(object) [3000]],
130 'zero' => [0],
131 'too-small' => [-1],
132 'too-big' => [65536],
133 ];
134 }
135
136 /**
137 * @dataProvider invalidPortsDataProvider
138 */
139 public function withPortRaisesExceptionForInvalidPorts($port) {
140 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
141 $this->setExpectedException('InvalidArgumentException', 'Invalid port');
142 $new = $uri->withPort($port);
143 }
144
145 /**
146 * @test
147 */
148 public function withPathReturnsNewInstanceWithProvidedPath() {
149 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
150 $new = $uri->withPath('/bar/baz');
151 $this->assertNotSame($uri, $new);
152 $this->assertEquals('/bar/baz', $new->getPath());
153 $this->assertEquals('https://user:pass@local.example.com:3001/bar/baz?bar=baz#quz', (string) $new);
154 }
155
156 /**
157 * @return array
158 */
159 public function invalidPathsDataProvider() {
160 return [
161 'null' => [NULL],
162 'true' => [TRUE],
163 'false' => [FALSE],
164 'array' => [['/bar/baz']],
165 'object' => [(object) ['/bar/baz']],
166 'query' => ['/bar/baz?bat=quz'],
167 'fragment' => ['/bar/baz#bat'],
168 ];
169 }
170
171 /**
172 * @dataProvider invalidPathsDataProvider
173 * @test
174 */
175 public function withPathRaisesExceptionForInvalidPaths($path) {
176 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
177 $this->setExpectedException('InvalidArgumentException', 'Invalid path');
178 $new = $uri->withPath($path);
179 }
180
181 /**
182 * @test
183 */
184 public function withQueryReturnsNewInstanceWithProvidedQuery() {
185 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
186 $new = $uri->withQuery('baz=bat');
187 $this->assertNotSame($uri, $new);
188 $this->assertEquals('baz=bat', $new->getQuery());
189 $this->assertEquals('https://user:pass@local.example.com:3001/foo?baz=bat#quz', (string) $new);
190 }
191
192 /**
193 * @return array
194 */
195 public function invalidQueryStringsDataProvider() {
196 return [
197 'null' => [NULL],
198 'true' => [TRUE],
199 'false' => [FALSE],
200 'array' => [['baz=bat']],
201 'object' => [(object) ['baz=bat']],
202 'fragment' => ['baz=bat#quz'],
203 ];
204 }
205
206 /**
207 * @dataProvider invalidQueryStringsDataProvider
208 * @test
209 */
210 public function withQueryRaisesExceptionForInvalidQueryStrings($query) {
211 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
212 $this->setExpectedException('InvalidArgumentException', 'Query string');
213 $new = $uri->withQuery($query);
214 }
215
216 /**
217 * @test
218 */
219 public function withFragmentReturnsNewInstanceWithProvidedFragment() {
220 $uri = new Uri('https://user:pass@local.example.com:3001/foo?bar=baz#quz');
221 $new = $uri->withFragment('qat');
222 $this->assertNotSame($uri, $new);
223 $this->assertEquals('qat', $new->getFragment());
224 $this->assertEquals('https://user:pass@local.example.com:3001/foo?bar=baz#qat', (string) $new);
225 }
226
227 /**
228 * @return array
229 */
230 public function authorityInfoDataProvider() {
231 return [
232 'host-only' => ['http://foo.com/bar', 'foo.com'],
233 'host-port' => ['http://foo.com:3000/bar', 'foo.com:3000'],
234 'user-host' => ['http://me@foo.com/bar', 'me@foo.com'],
235 'user-host-port' => ['http://me@foo.com:3000/bar', 'me@foo.com:3000'],
236 ];
237 }
238
239 /**
240 * @dataProvider authorityInfoDataProvider
241 * @test
242 */
243 public function getAuthorityReturnsExpectedValues($url, $expected) {
244 $uri = new Uri($url);
245 $this->assertEquals($expected, $uri->getAuthority());
246 }
247
248 /**
249 * @test
250 */
251 public function canEmitOriginFormUrl() {
252 $url = '/foo/bar?baz=bat';
253 $uri = new Uri($url);
254 $this->assertEquals($url, (string) $uri);
255 }
256
257 /**
258 * @test
259 */
260 public function settingEmptyPathOnAbsoluteUriReturnsAnEmptyPath() {
261 $uri = new Uri('http://example.com/foo');
262 $new = $uri->withPath('');
263 $this->assertEquals('', $new->getPath());
264 }
265
266 /**
267 * @test
268 */
269 public function stringRepresentationOfAbsoluteUriWithNoPathSetsAnEmptyPath() {
270 $uri = new Uri('http://example.com');
271 $this->assertEquals('http://example.com', (string) $uri);
272 }
273
274 /**
275 * @test
276 */
277 public function getPathOnOriginFormRemainsAnEmptyPath() {
278 $uri = new Uri('?foo=bar');
279 $this->assertEquals('', $uri->getPath());
280 }
281
282 /**
283 * @test
284 */
285 public function stringRepresentationOfOriginFormWithNoPathRetainsEmptyPath() {
286 $uri = new Uri('?foo=bar');
287 $this->assertEquals('?foo=bar', (string) $uri);
288 }
289
290 /**
291 * @return array
292 */
293 public function invalidConstructorUrisDataProvider() {
294 return [
295 'null' => [NULL],
296 'true' => [TRUE],
297 'false' => [FALSE],
298 'int' => [1],
299 'float' => [1.1],
300 'array' => [['http://example.com/']],
301 'object' => [(object) ['uri' => 'http://example.com/']],
302 ];
303 }
304
305 /**
306 * @dataProvider invalidConstructorUrisDataProvider
307 */
308 public function constructorRaisesExceptionForNonStringURI($uri) {
309 $this->setExpectedException('InvalidArgumentException');
310 new Uri($uri);
311 }
312
313 /**
314 * @test
315 */
316 public function constructorRaisesExceptionForSeriouslyMalformedURI() {
317 $this->setExpectedException('InvalidArgumentException');
318 new Uri('http:///www.php-fig.org/');
319 }
320
321 /**
322 * @test
323 */
324 public function withSchemeStripsOffDelimiter() {
325 $uri = new Uri('http://example.com');
326 $new = $uri->withScheme('https://');
327 $this->assertEquals('https', $new->getScheme());
328 }
329
330 /**
331 * @return array
332 */
333 public function invalidSchemesDataProvider() {
334 return [
335 'mailto' => ['mailto'],
336 'ftp' => ['ftp'],
337 'telnet' => ['telnet'],
338 'ssh' => ['ssh'],
339 'git' => ['git'],
340 ];
341 }
342
343 /**
344 * @dataProvider invalidSchemesDataProvider
345 * @test
346 */
347 public function constructWithUnsupportedSchemeRaisesAnException($scheme) {
348 $this->setExpectedException('InvalidArgumentException', 'Unsupported scheme');
349 $uri = new Uri($scheme . '://example.com');
350 }
351
352 /**
353 * @dataProvider invalidSchemesDataProvider
354 * @test
355 */
356 public function withSchemeUsingUnsupportedSchemeRaisesAnException($scheme) {
357 $uri = new Uri('http://example.com');
358 $this->setExpectedException('InvalidArgumentException', 'Unsupported scheme');
359 $uri->withScheme($scheme);
360 }
361
362 /**
363 * @test
364 */
365 public function withPathIsNotPrefixedWithSlashIfSetWithoutOne() {
366 $uri = new Uri('http://example.com');
367 $new = $uri->withPath('foo/bar');
368 $this->assertEquals('foo/bar', $new->getPath());
369 }
370
371 /**
372 * @test
373 */
374 public function withPathNotSlashPrefixedIsEmittedWithSlashDelimiterWhenUriIsCastToString() {
375 $uri = new Uri('http://example.com');
376 $new = $uri->withPath('foo/bar');
377 $this->assertEquals('http://example.com/foo/bar', $new->__toString());
378 }
379
380 /**
381 * @test
382 */
383 public function withQueryStripsQueryPrefixIfPresent() {
384 $uri = new Uri('http://example.com');
385 $new = $uri->withQuery('?foo=bar');
386 $this->assertEquals('foo=bar', $new->getQuery());
387 }
388
389 /**
390 * @test
391 */
392 public function withFragmentStripsFragmentPrefixIfPresent() {
393 $uri = new Uri('http://example.com');
394 $new = $uri->withFragment('#/foo/bar');
395 $this->assertEquals('/foo/bar', $new->getFragment());
396 }
397
398 /**
399 * @return array
400 */
401 public function standardSchemePortCombinationsDataProvider() {
402 return [
403 'http' => ['http', 80],
404 'https' => ['https', 443],
405 ];
406 }
407
408 /**
409 * @dataProvider standardSchemePortCombinationsDataProvider
410 * @test
411 */
412 public function getAuthorityOmitsPortForStandardSchemePortCombinations($scheme, $port) {
413 $uri = (new Uri())
414 ->withHost('example.com')
415 ->withScheme($scheme)
416 ->withPort($port);
417 $this->assertEquals('example.com', $uri->getAuthority());
418 }
419
420 /**
421 * @test
422 */
423 public function getPathIsProperlyEncoded() {
424 $uri = (new Uri())->withPath('/foo^bar');
425 $expected = '/foo%5Ebar';
426 $this->assertEquals($expected, $uri->getPath());
427 }
428
429 /**
430 * @test
431 */
432 public function getPathDoesNotBecomeDoubleEncoded() {
433 $uri = (new Uri())->withPath('/foo%5Ebar');
434 $expected = '/foo%5Ebar';
435 $this->assertEquals($expected, $uri->getPath());
436 }
437
438 /**
439 * @return array
440 */
441 public function queryStringsForEncodingDataProvider() {
442 return [
443 'key-only' => ['k^ey', 'k%5Eey'],
444 'key-value' => ['k^ey=valu`', 'k%5Eey=valu%60'],
445 'array-key-only' => ['key[]', 'key%5B%5D'],
446 'array-key-value' => ['key[]=valu`', 'key%5B%5D=valu%60'],
447 'complex' => ['k^ey&key[]=valu`&f<>=`bar', 'k%5Eey&key%5B%5D=valu%60&f%3C%3E=%60bar'],
448 ];
449 }
450
451 /**
452 * @dataProvider queryStringsForEncodingDataProvider
453 * @test
454 */
455 public function getQueryIsProperlyEncoded($query, $expected) {
456 $uri = (new Uri())->withQuery($query);
457 $this->assertEquals($expected, $uri->getQuery());
458 }
459
460 /**
461 * @dataProvider queryStringsForEncodingDataProvider
462 * @test
463 */
464 public function getQueryIsNotDoubleEncoded($query, $expected) {
465 $uri = (new Uri())->withQuery($expected);
466 $this->assertEquals($expected, $uri->getQuery());
467 }
468
469 /**
470 * @test
471 */
472 public function getFragmentIsProperlyEncoded() {
473 $uri = (new Uri())->withFragment('/p^th?key^=`bar#b@z');
474 $expected = '/p%5Eth?key%5E=%60bar%23b@z';
475 $this->assertEquals($expected, $uri->getFragment());
476 }
477
478 /**
479 * @test
480 */
481 public function getFragmentIsNotDoubleEncoded() {
482 $expected = '/p%5Eth?key%5E=%60bar%23b@z';
483 $uri = (new Uri())->withFragment($expected);
484 $this->assertEquals($expected, $uri->getFragment());
485 }
486 }