Completed
Pull Request — master (#178)
by ignace nyamagana
03:19
created

UriTest::testEmptyValueDetection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/**
4
 * League.Uri (https://uri.thephpleague.com)
5
 *
6
 * (c) Ignace Nyamagana Butera <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace LeagueTest\Uri;
13
14
use League\Uri\Exceptions\SyntaxError;
15
use League\Uri\Uri;
16
use PHPUnit\Framework\TestCase;
17
use TypeError;
18
19
/**
20
 * @group uri
21
 * @coversDefaultClass League\Uri\Uri
22
 */
23
class UriTest extends TestCase
24
{
25
    /**
26
     * @var Uri
27
     */
28
    private $uri;
29
30
    protected function setUp(): void
31
    {
32
        $this->uri = Uri::createFromString(
33
            'http://login:[email protected]:443/test/query.php?kingkong=toto#doc3'
34
        );
35
    }
36
37
    protected function tearDown(): void
38
    {
39
        unset($this->uri);
40
    }
41
42
    /**
43
     * @covers ::__toString
44
     * @covers ::formatHost
45
     * @covers ::formatRegisteredName
46
     * @covers ::formatQueryAndFragment
47
     * @covers ::formatPort
48
     * @covers ::formatUserInfo
49
     * @covers ::formatScheme
50
     */
51
    public function testAutomaticUrlNormalization(): void
52
    {
53
        $raw = 'HtTpS://MaStEr.B%c3%A9b%c3%a9.eXaMpLe.CoM:/%7ejohndoe/%a1/in+dex.php?fào.%bar=v%61lue#fragment';
54
        $normalized = 'https://master.xn--bb-bjab.example.com/%7ejohndoe/%a1/in+dex.php?f%C3%A0o.%bar=v%61lue#fragment';
55
        self::assertSame($normalized, (string) Uri::createFromString($raw));
56
    }
57
58
    /**
59
     * @covers ::__toString
60
     * @covers ::formatHost
61
     */
62
    public function testAutomaticUrlNormalizationBis(): void
63
    {
64
        self::assertSame(
65
            'http://xn--bb-bjab.be./path',
66
            (string) Uri::createFromString('http://Bébé.BE./path')
67
        );
68
    }
69
70
    /**
71
     * @covers ::__toString
72
     * @covers ::formatScheme
73
     */
74
    public function testConstructingUrisWithSchemesWithNonLeadingDigits(): void
75
    {
76
        $uri = 's3://somebucket/somefile.txt';
77
        self::assertSame($uri, (string) Uri::createFromString($uri));
78
    }
79
80
    /**
81
     * @covers ::__toString
82
     * @covers ::formatScheme
83
     * @covers ::withScheme
84
     */
85
    public function testSettingSchemesWithNonLeadingDigits(): void
86
    {
87
        $uri = 'http://somebucket/somefile.txt';
88
        $expected_uri = 's3://somebucket/somefile.txt';
89
        self::assertSame($expected_uri, (string) Uri::createFromString($uri)->withScheme('s3'));
90
    }
91
92
    /**
93
     * @covers ::getUriString
94
     * @covers ::__toString
95
     * @covers ::formatUserInfo
96
     * @covers ::formatQueryAndFragment
97
     */
98
    public function testPreserveComponentsOnInstantiation(): void
99
    {
100
        $uri = 'http://:@example.com?#';
101
        self::assertSame($uri, (string) Uri::createFromString($uri));
102
    }
103
104
    /**
105
     * @covers ::getScheme
106
     * @covers ::withScheme
107
     */
108
    public function testScheme(): void
109
    {
110
        self::assertSame('http', $this->uri->getScheme());
111
        self::assertSame($this->uri, $this->uri->withScheme('http'));
112
        self::assertNotEquals($this->uri, $this->uri->withScheme('https'));
113
        self::assertSame(
114
            '//login:[email protected]:443/test/query.php?kingkong=toto#doc3',
115
            (string) $this->uri->withScheme(null)
116
        );
117
    }
118
119
    /**
120
     * @covers ::getUserInfo
121
     * @covers ::withUserInfo
122
     * @covers ::formatUserInfo
123
     */
124
    public function testUserInfo(): void
125
    {
126
        self::assertSame('login:pass', $this->uri->getUserInfo());
127
        self::assertSame($this->uri, $this->uri->withUserInfo('login', 'pass'));
128
129
        $newUri = $this->uri->withUserInfo('login', null);
130
        self::assertNotEquals($this->uri, $newUri);
131
132
        $altUri = $this->uri->withUserInfo(null);
133
        self::assertNotEquals($this->uri, $altUri);
134
135
        self::assertSame('http://secure.example.com:443/test/query.php?kingkong=toto#doc3', (string) $altUri);
136
    }
137
138
    /**
139
     * @covers ::getHost
140
     * @covers ::withHost
141
     * @covers ::formatHost
142
     * @covers ::formatIp
143
     * @covers ::formatRegisteredName
144
     */
145
    public function testHost(): void
146
    {
147
        self::assertSame('secure.example.com', $this->uri->getHost());
148
        self::assertSame($this->uri, $this->uri->withHost('secure.example.com'));
149
        self::assertNotEquals($this->uri, $this->uri->withHost('[::1]'));
150
    }
151
152
    /**
153
     * @covers ::getAuthority
154
     */
155
    public function testGetAuthority(): void
156
    {
157
        self::assertSame('login:[email protected]:443', $this->uri->getAuthority());
158
    }
159
160
    /**
161
     * @covers ::withUserInfo
162
     * @covers ::withPort
163
     * @covers ::withScheme
164
     * @covers ::withHost
165
     */
166
    public function testRemoveAuthority(): void
167
    {
168
        $uri_with_host = (string) $this->uri
169
            ->withUserInfo(null)
170
            ->withPort(null)
171
            ->withScheme(null)
172
            ->withHost(null);
173
        self::assertSame('/test/query.php?kingkong=toto#doc3', $uri_with_host);
174
    }
175
176
    /**
177
     * @covers ::getPort
178
     * @covers ::withPort
179
     * @covers ::formatPort
180
     */
181
    public function testPort(): void
182
    {
183
        self::assertSame(443, $this->uri->getPort());
184
        self::assertSame($this->uri, $this->uri->withPort(443));
185
        self::assertNotEquals($this->uri, $this->uri->withPort(81));
186
        self::assertSame(
187
            'http://login:[email protected]/test/query.php?kingkong=toto#doc3',
188
            (string) $this->uri->withPort(null)
189
        );
190
    }
191
192
    /**
193
     * @covers ::getPath
194
     * @covers ::withPath
195
     */
196
    public function testPath(): void
197
    {
198
        self::assertSame('/test/query.php', $this->uri->getPath());
199
        self::assertSame($this->uri, $this->uri->withPath('/test/query.php'));
200
        self::assertNotEquals($this->uri, $this->uri->withPath('/test/file.php'));
201
        self::assertSame(
202
            'http://login:[email protected]:443?kingkong=toto#doc3',
203
            (string) $this->uri->withPath('')
204
        );
205
    }
206
207
    /**
208
     * @covers ::getQuery
209
     * @covers ::withQuery
210
     */
211
    public function testQuery(): void
212
    {
213
        self::assertSame('kingkong=toto', $this->uri->getQuery());
214
        self::assertSame($this->uri, $this->uri->withQuery('kingkong=toto'));
215
        self::assertNotEquals($this->uri, $this->uri->withQuery('kingkong=tata'));
216
        self::assertSame(
217
            'http://login:[email protected]:443/test/query.php#doc3',
218
            (string) $this->uri->withQuery(null)
219
        );
220
    }
221
222
    /**
223
     * @covers ::getFragment
224
     * @covers ::withFragment
225
     */
226
    public function testFragment(): void
227
    {
228
        self::assertSame('doc3', $this->uri->getFragment());
229
        self::assertSame($this->uri, $this->uri->withFragment('doc3'));
230
        self::assertNotEquals($this->uri, $this->uri->withFragment('doc2'));
231
        self::assertSame(
232
            'http://login:[email protected]:443/test/query.php?kingkong=toto',
233
            (string) $this->uri->withFragment(null)
234
        );
235
    }
236
237
    /**
238
     * @covers ::getIDNAErrors
239
     * @covers ::formatHost
240
     */
241
    public function testCannotConvertInvalidHost(): void
242
    {
243
        self::expectException(SyntaxError::class);
244
        Uri::createFromString('http://_b%C3%A9bé.be-/foo/bar');
245
    }
246
247
    public function testWithSchemeFailedWithInvalidSchemeValue(): void
248
    {
249
        self::expectException(SyntaxError::class);
250
        Uri::createFromString('http://example.com')->withScheme('tété');
251
    }
252
253
    /**
254
     * @covers ::filterString
255
     */
256
    public function testWithInvalidCharacters(): void
257
    {
258
        self::expectException(TypeError::class);
259
        Uri::createFromString('')->withPath(date_create());
260
    }
261
262
    /**
263
     * @covers ::assertValidState
264
     */
265
    public function testWithPathFailedWithInvalidChars(): void
266
    {
267
        self::expectException(SyntaxError::class);
268
        Uri::createFromString('http://example.com')->withPath('#24');
269
    }
270
271
    /**
272
     * @covers ::assertValidState
273
     */
274
    public function testWithPathFailedWithInvalidPathRelativeToTheAuthority(): void
275
    {
276
        self::expectException(SyntaxError::class);
277
        Uri::createFromString('http://example.com')->withPath('foo/bar');
278
    }
279
280
    /**
281
     * @covers ::formatRegisteredName
282
     * @covers ::withHost
283
     */
284
    public function testModificationFailedWithInvalidHost(): void
285
    {
286
        self::expectException(SyntaxError::class);
287
        Uri::createFromString('http://example.com/path')->withHost('%23');
288
    }
289
290
    /**
291
     * @covers ::assertValidState
292
     * @dataProvider missingAuthorityProvider
293
     */
294
    public function testModificationFailedWithMissingAuthority(string $path): void
295
    {
296
        self::expectException(SyntaxError::class);
297
        Uri::createFromString('http://example.com/path')
298
            ->withScheme(null)
299
            ->withHost(null)
300
            ->withPath($path);
301
    }
302
303
    /**
304
     * @covers ::assertValidState
305
     */
306
    public function missingAuthorityProvider(): array
307
    {
308
        return [
309
            ['data:go'],
310
            ['//data'],
311
        ];
312
    }
313
314
    /**
315
     * @covers ::__toString
316
     * @covers ::formatHost
317
     * @covers ::formatRegisteredName
318
     * @covers ::formatQueryAndFragment
319
     * @covers ::formatPort
320
     * @covers ::formatUserInfo
321
     */
322
    public function testEmptyValueDetection(): void
323
    {
324
        $expected = '//0:0@0/0?0#0';
325
        self::assertSame($expected, Uri::createFromString($expected)->__toString());
326
    }
327
328
    public function testPathDetection(): void
329
    {
330
        $expected = 'foo/bar:';
331
        self::assertSame($expected, Uri::createFromString($expected)->getPath());
332
    }
333
334
    /**
335
     * @covers ::filterString
336
     * @covers ::withPath
337
     */
338
    public function testWithPathThrowTypeErrorOnWrongType(): void
339
    {
340
        self::expectException(TypeError::class);
341
        Uri::createFromString('https://example.com')->withPath(null);
342
    }
343
344
    /**
345
     * @dataProvider setStateDataProvider
346
     *
347
     * @covers ::__set_state
348
     */
349
    public function testSetState(Uri $uri): void
350
    {
351
        self::assertEquals($uri, eval('return '.var_export($uri, true).';'));
352
    }
353
354
    public function setStateDataProvider(): array
355
    {
356
        return [
357
            'all components' => [Uri::createFromString('https://a:b@c:442/d?q=r#f')],
358
            'without scheme' => [Uri::createFromString('//a:b@c:442/d?q=r#f')],
359
            'without userinfo' => [Uri::createFromString('https://c:442/d?q=r#f')],
360
            'without port' => [Uri::createFromString('https://a:b@c/d?q=r#f')],
361
            'without path' => [Uri::createFromString('https://a:b@c:442?q=r#f')],
362
            'without query' => [Uri::createFromString('https://a:b@c:442/d#f')],
363
            'without fragment' => [Uri::createFromString('https://a:b@c:442/d?q=r')],
364
            'without pass' => [Uri::createFromString('https://a@c:442/d?q=r#f')],
365
            'without authority' => [Uri::createFromString('/d?q=r#f')],
366
       ];
367
    }
368
369
    /**
370
     * @covers ::__debugInfo
371
     */
372
    public function testDebugInfo(): void
373
    {
374
        $uri = Uri::createFromString('https://a:b@c:442/d?q=r#f');
375
        $debugInfo = $uri->__debugInfo();
376
        self::assertSame('a:***', $debugInfo['user_info']);
377
        self::assertCount(7, $debugInfo);
378
    }
379
380
    public function testJsonSerialize(): void
381
    {
382
        $uri = Uri::createFromString('https://a:b@c:442/d?q=r#f');
383
        self::assertJsonStringEqualsJsonString(json_encode($uri->__toString()), json_encode($uri));
384
    }
385
386
    /**
387
     * @covers ::createFromComponents
388
     * @covers ::formatRegisteredName
389
     */
390
    public function testCreateFromComponents(): void
391
    {
392
        $uri = '//0:0@0/0?0#0';
393
        self::assertEquals(
394
            Uri::createFromComponents(parse_url($uri)),
395
            Uri::createFromString($uri)
396
        );
397
    }
398
399
    /**
400
     * @covers ::formatPort
401
     * @covers ::withPort
402
     */
403
    public function testModificationFailedWithInvalidPort(): void
404
    {
405
        self::expectException(SyntaxError::class);
406
        Uri::createFromString('http://example.com/path')->withPort(-1);
407
    }
408
409
    /**
410
     * @covers ::formatPort
411
     * @covers ::withPort
412
     */
413
    public function testModificationFailedWithInvalidPort2(): void
414
    {
415
        self::expectException(SyntaxError::class);
416
        Uri::createFromString('http://example.com/path')->withPort('-1');
417
    }
418
419
    /**
420
     * @covers ::formatIp
421
     */
422
    public function testCreateFromComponentsHandlesScopedIpv6(): void
423
    {
424
        $expected = '[fe80:1234::%251]';
425
        self::assertSame(
426
            $expected,
427
            Uri::createFromComponents(['host' => $expected])->getHost()
428
        );
429
    }
430
431
    /**
432
     * @covers ::formatIp
433
     */
434
    public function testCreateFromComponentsHandlesIpvFuture(): void
435
    {
436
        $expected = '[v1.ZZ.ZZ]';
437
        self::assertSame(
438
            $expected,
439
            Uri::createFromComponents(['host' => $expected])->getHost()
440
        );
441
    }
442
443
444
    /**
445
     * @covers ::formatIp
446
     */
447
    public function testCreateFromComponentsThrowsOnInvalidIpvFuture(): void
448
    {
449
        self::expectException(SyntaxError::class);
450
        Uri::createFromComponents(['host' => '[v4.1.2.3]']);
451
    }
452
453
    /**
454
     * @covers ::filterString
455
     */
456
    public function testCreateFromComponentsThrowsExceptionWithInvalidChars(): void
457
    {
458
        self::expectException(SyntaxError::class);
459
        Uri::createFromComponents()->withFragment("\n\rtoto");
460
    }
461
462
    /**
463
     * @covers ::formatIp
464
     */
465
    public function testCreateFromComponentsThrowsException(): void
466
    {
467
        self::expectException(SyntaxError::class);
468
        Uri::createFromComponents(['host' => '[127.0.0.1]']);
469
    }
470
471
    /**
472
     * @covers ::formatIp
473
     */
474
    public function testCreateFromComponentsThrowsException2(): void
475
    {
476
        self::expectException(SyntaxError::class);
477
        Uri::createFromComponents(['host' => '[127.0.0.1%251]']);
478
    }
479
480
    /**
481
     * @covers ::formatIp
482
     */
483
    public function testCreateFromComponentsThrowsException3(): void
484
    {
485
        self::expectException(SyntaxError::class);
486
        Uri::createFromComponents(['host' => '[fe80:1234::%25 1]']);
487
    }
488
489
    /**
490
     * @covers ::formatIp
491
     */
492
    public function testCreateFromComponentsThrowsException4(): void
493
    {
494
        self::expectException(SyntaxError::class);
495
        Uri::createFromComponents(['host' => '[::1%251]']);
496
    }
497
498
    /**
499
     * @covers ::formatRegisteredName
500
     * @covers ::getIDNAErrors
501
     */
502
    public function testCreateFromComponentsThrowsException5(): void
503
    {
504
        self::expectException(SyntaxError::class);
505
        Uri::createFromComponents(['host' => 'a⒈com']);
506
    }
507
508
    /**
509
     * @covers ::formatRegisteredName
510
     * @covers ::getIDNAErrors
511
     */
512
    public function testCreateFromComponentsThrowsException6(): void
513
    {
514
        self::expectException(SyntaxError::class);
515
        Uri::createFromComponents(['host' => 'xn--3']);
516
    }
517
518
    /**
519
     * @covers ::formatRegisteredName
520
     */
521
    public function testCreateFromComponentsThrowsException7(): void
522
    {
523
        self::expectException(SyntaxError::class);
524
        Uri::createFromComponents(['host' => str_repeat('A', 255)]);
525
    }
526
527
    /**
528
     * @covers ::formatRegisteredName
529
     */
530
    public function testCreateFromComponentsWorksWithPunycode(): void
531
    {
532
        $uri = Uri::createFromComponents(['host' => 'xn--mgbh0fb.xn--kgbechtv']);
533
        self::assertSame('xn--mgbh0fb.xn--kgbechtv', $uri->getHost());
534
    }
535
536
    /**
537
     * @covers ::formatPath
538
     */
539
    public function testReservedCharsInPathUnencoded(): void
540
    {
541
        $uri = Uri::createFromString()
542
            ->withHost('api.linkedin.com')
543
            ->withScheme('https')
544
            ->withPath('/v1/people/~:(first-name,last-name,email-address,picture-url)');
545
546
        self::assertStringContainsString(
547
            '/v1/people/~:(first-name,last-name,email-address,picture-url)',
548
            (string) $uri
549
        );
550
    }
551
552
    /**
553
     * @dataProvider userInfoProvider
554
     * @param ?string $credential
0 ignored issues
show
Documentation introduced by
The doc-type ?string could not be parsed: Unknown type name "?string" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
555
     */
556
    public function testWithUserInfoEncodesUsernameAndPassword(string $user, ?string $credential, string $expected): void
557
    {
558
        $uri = Uri::createFromString('https://user:[email protected]:3001/foo?bar=baz#quz');
559
        $new = $uri->withUserInfo($user, $credential);
560
        self::assertSame($expected, $new->getUserInfo());
561
    }
562
563
    public function userInfoProvider(): array
564
    {
565
        return [
566
            'no password' => ['login:', null, 'login%3A'],
567
            'password with delimiter' => ['login', 'password@', 'login:password%40'],
568
            'valid-chars' => ['foo', 'bar', 'foo:bar'],
569
            'colon'       => ['foo:bar', 'baz:bat', 'foo%3Abar:baz:bat'],
570
            'at'          => ['[email protected]', 'cred@foo', 'user%40example.com:cred%40foo'],
571
            'percent'     => ['%25', '%25', '%25:%25'],
572
            'invalid-enc' => ['%ZZ', '%GG', '%25ZZ:%25GG'],
573
        ];
574
    }
575
576
    public function testIssue167ExceptionReasonMisleadingMessage(): void
577
    {
578
        self::expectException(SyntaxError::class);
579
        self::expectExceptionMessage('The uri `file://example.org:80/home/jsmith/foo.txt` is invalid for the `file` scheme.');
580
581
        Uri::createFromString('file://example.org:80/home/jsmith/foo.txt');
582
    }
583
584
    public function testIssue171TheEmptySchemeShouldThrow(): void
585
    {
586
        self::expectException(SyntaxError::class);
587
        self::expectExceptionMessage('The scheme `` is invalid.');
588
589
        Uri::createFromString('domain.com')->withScheme('');
590
    }
591
}
592