1<?php
2/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
3
4namespace Tests\Icinga\Web;
5
6use Mockery;
7use Icinga\Web\Url;
8use Icinga\Test\BaseTestCase;
9
10class UrlTest extends BaseTestCase
11{
12    public function testWhetherFromPathCutsOfTheFirstCharOfThePathIfUrlIsInternalAndHasAUsernameInIt()
13    {
14        $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost')
15            ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080');
16
17        $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html');
18        $this->assertEquals(
19            'path/to/my/url.html',
20            $url->getPath(),
21            'Url::fromPath does not cut of the first char of path if the url is internal and has a username in it'
22        );
23    }
24
25    public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasAUsernameInIt()
26    {
27        $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost')
28            ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080');
29
30        $url = Url::fromPath('http://testusername:testpassword@localhost:8080/path/to/my/url.html');
31        $this->assertEquals(
32            'http://testusername:testpassword@localhost:8080/path/to/my/url.html',
33            $url->getAbsoluteUrl(),
34            'Url::getAbsoluteUrl does not reassemble the correct basePath'
35        );
36    }
37
38    public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsInternalAndHasNoUsernameInIt()
39    {
40        $url = Url::fromPath('/path/to/my/url.html');
41        $this->assertEquals(
42            '/path/to/my/url.html',
43            $url->getAbsoluteUrl(),
44            'Url::getAbsoluteUrl does not reassemble the correct basePath'
45        );
46    }
47
48    public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasAUsernameInIt()
49    {
50        $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost')
51            ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080');
52
53        $url = Url::fromPath('http://testusername:testpassword@testhost/path/to/my/url.html');
54        $this->assertEquals(
55            'http://testusername:testpassword@testhost/path/to/my/url.html',
56            $url->getAbsoluteUrl(),
57            'Url::getAbsoluteUrl does not reassemble the correct basePath'
58        );
59    }
60
61    public function testWhetherGetAbsoluteUrlReturnsTheBasePathIfUrlIsExternalAndHasNoUsernameInIt()
62    {
63        $this->getRequestMock()->shouldReceive('getServer')->with("SERVER_NAME")->andReturn('localhost')
64            ->shouldReceive('getServer')->with("SERVER_PORT")->andReturn('8080');
65
66        $url = Url::fromPath('http://testhost/path/to/my/url.html');
67        $this->assertEquals(
68            'http://testhost/path/to/my/url.html',
69            $url->getAbsoluteUrl(),
70            'Url::getAbsoluteUrl does not reassemble the correct basePath'
71        );
72    }
73
74    public function testWhetherGetAbsoluteUrlReturnsTheGivenUsernameAndPassword()
75    {
76        $url = Url::fromPath('http://testusername:testpassword@testsite.com/path/to/my/url.html');
77        $this->assertEquals(
78            'http://testusername:testpassword@testsite.com/path/to/my/url.html',
79            $url->getAbsoluteUrl(),
80            'Url::fromPath does not reassemble the correct url'
81        );
82    }
83
84    public function testWhetherFromRequestWorksWithoutARequest()
85    {
86        $this->getRequestMock()->shouldReceive('getBaseUrl')->andReturn('/path/to')
87            ->shouldReceive('getPathInfo')->andReturn('my/test/url.html')
88            ->shouldReceive('getQuery')->andReturn(array('param1' => 'value1', 'param2' => 'value2'));
89
90        $url = Url::fromRequest();
91        $this->assertEquals(
92            '/path/to/my/test/url.html?param1=value1&amp;param2=value2',
93            $url->getAbsoluteUrl('&amp;'),
94            'Url::fromRequest does not reassemble the correct url from the global request'
95        );
96    }
97
98    public function testWhetherFromRequestWorksWithARequest()
99    {
100        $request = Mockery::mock('Icinga\Web\Request');
101        $request->shouldReceive('getPathInfo')->andReturn('my/test/url.html')
102            ->shouldReceive('getBaseUrl')->andReturn('/path/to')
103            ->shouldReceive('getQuery')->andReturn(array());
104
105        $url = Url::fromRequest(array(), $request);
106        $this->assertEquals(
107            '/path/to/my/test/url.html',
108            $url->getAbsoluteUrl(),
109            'Url::fromRequest does not reassemble the correct url from a given request'
110        );
111    }
112
113    public function testWhetherFromRequestAcceptsAdditionalParameters()
114    {
115        $request = Mockery::mock('Icinga\Web\Request');
116        $request->shouldReceive('getPathInfo')->andReturn('')
117            ->shouldReceive('getBaseUrl')->andReturn('/')
118            ->shouldReceive('getQuery')->andReturn(array('key1' => 'val1'));
119
120        $url = Url::fromRequest(array('key1' => 'newval1', 'key2' => 'val2'), $request);
121        $this->assertEquals(
122            'val2',
123            $url->getParam('key2', 'wrongval'),
124            'Url::fromRequest does not accept additional parameters'
125        );
126        $this->assertEquals(
127            'newval1',
128            $url->getParam('key1', 'wrongval1'),
129            'Url::fromRequest does not overwrite existing parameters with additional ones'
130        );
131    }
132
133    public function testWhetherFromPathProperlyHandlesInvalidUrls()
134    {
135        $this->expectException(\Icinga\Exception\ProgrammingError::class);
136
137        Url::fromPath(null);
138    }
139
140    public function testWhetherFromPathAcceptsAdditionalParameters()
141    {
142        $url = Url::fromPath('/my/test/url.html', array('key' => 'value'));
143
144        $this->assertEquals(
145            'value',
146            $url->getParam('key', 'wrongvalue'),
147            'Url::fromPath does not accept additional parameters'
148        );
149    }
150
151    public function testWhetherFromPathProperlyParsesUrlsWithoutQuery()
152    {
153        $url = Url::fromPath('/my/test/url.html');
154
155        $this->assertEquals(
156            '',
157            $url->getBasePath(),
158            'Url::fromPath does not recognize the correct base path'
159        );
160        $this->assertEquals(
161            '/my/test/url.html',
162            $url->getAbsoluteUrl(),
163            'Url::fromPath does not recognize the correct url path'
164        );
165    }
166
167    /**
168     * @depends testWhetherFromPathProperlyParsesUrlsWithoutQuery
169     */
170    public function testWhetherFromPathProperlyRecognizesTheBaseUrl()
171    {
172        $url = Url::fromPath(
173            '/path/to/my/test/url.html',
174            array(),
175            Mockery::mock(array('getBaseUrl' => '/path/to'))
176        );
177
178        $this->assertEquals(
179            '/path/to/my/test/url.html',
180            $url->getAbsoluteUrl(),
181            'Url::fromPath does not properly differentiate between the base url and its path'
182        );
183    }
184
185    /**
186     * @depends testWhetherFromPathProperlyRecognizesTheBaseUrl
187     */
188    public function testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters()
189    {
190        $url = Url::fromPath('/my/test/url.html?param1=%25arg1&param2=arg%202'
191            . '&param3[]=1&param3[]=2&param3[]=3&param4[key1]=val1&param4[key2]=val2');
192
193        $this->assertEquals(
194            '%arg1',
195            $url->getParam('param1', 'wrongval'),
196            'Url::fromPath does not properly decode escaped characters in query parameter values'
197        );
198        $this->assertEquals(
199            'arg 2',
200            $url->getParam('param2', 'wrongval'),
201            'Url::fromPath does not properly decode aliases characters in query parameter values'
202        );
203        /*
204        // Temporarily disabled, no [] support right now
205        $this->assertEquals(
206            array('1', '2', '3'),
207            $url->getParam('param3'),
208            'Url::fromPath does not properly reassemble query parameter values as sequenced values'
209        );
210        $this->assertEquals(
211            array('key1' => 'val1', 'key2' => 'val2'),
212            $url->getParam('param4'),
213            'Url::fromPath does not properly reassemble query parameters as associative arrays'
214        );
215        */
216    }
217
218    /**
219     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
220     */
221    public function testWhetherGetAbsoluteUrlReturnsTheAbsoluteUrl()
222    {
223        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2');
224
225        $this->assertEquals(
226            '/my/test/url.html?param=val&param2=val2',
227            $url->getAbsoluteUrl(),
228            'Url::getAbsoluteUrl does not return the absolute url'
229        );
230    }
231
232    public function testWhetherGetRelativeUrlReturnsTheEmptyStringForAbsoluteUrls()
233    {
234        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2');
235
236        $this->assertEquals(
237            '',
238            $url->getRelativeUrl(),
239            'Url::getRelativeUrl does not return the empty string for absolute urls'
240        );
241    }
242
243    /**
244     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
245     */
246    public function testWhetherGetParamReturnsTheCorrectParameter()
247    {
248        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2');
249
250        $this->assertEquals(
251            'val',
252            $url->getParam('param', 'wrongval'),
253            'Url::getParam does not return the correct value for an existing parameter'
254        );
255        $this->assertEquals(
256            'val2',
257            $url->getParam('param2', 'wrongval2'),
258            'Url::getParam does not return the correct value for an existing parameter'
259        );
260        $this->assertEquals(
261            'nonexisting',
262            $url->getParam('param3', 'nonexisting'),
263            'Url::getParam does not return the default value for a non existing parameter'
264        );
265    }
266
267    /**
268     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
269     */
270    public function testWhetherRemoveRemovesAGivenSingleParameter()
271    {
272        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2');
273        $url->remove('param');
274
275        $this->assertEquals(
276            'val2',
277            $url->getParam('param2', 'wrongval2'),
278            'Url::remove removes not only the given parameter'
279        );
280        $this->assertEquals(
281            'rightval',
282            $url->getParam('param', 'rightval'),
283            'Url::remove does not remove the given parameter'
284        );
285    }
286
287    /**
288     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
289     */
290    public function testWhetherRemoveRemovesAGivenSetOfParameters()
291    {
292        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2&param3=val3');
293        $url->remove(array('param', 'param2'));
294
295        $this->assertEquals(
296            'val3',
297            $url->getParam('param3', 'wrongval'),
298            'Url::remove removes not only the given parameters'
299        );
300        $this->assertEquals(
301            'rightval',
302            $url->getParam('param', 'rightval'),
303            'Url::remove does not remove all given parameters'
304        );
305        $this->assertEquals(
306            'rightval',
307            $url->getParam('param2', 'rightval'),
308            'Url::remove does not remove all given parameters'
309        );
310    }
311
312    /**
313     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
314     */
315    public function testWhetherGetUrlWithoutReturnsACopyOfTheUrlWithoutAGivenSetOfParameters()
316    {
317        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2&param3=val3');
318        $url2 = $url->getUrlWithout(array('param', 'param2'));
319
320        $this->assertNotSame($url, $url2, 'Url::getUrlWithout does not return a new copy of the url');
321        $this->assertEquals(
322            array(array('param3', 'val3')),
323            $url2->getParams()->toArray(),
324            'Url::getUrlWithout does not remove a given set of parameters from the url'
325        );
326    }
327
328    /**
329     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
330     */
331    public function testWhetherAddParamsDoesNotOverwriteExistingParameters()
332    {
333        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2&param3=val3');
334        $url->addParams(array('param4' => 'val4', 'param3' => 'newval3'));
335
336        $this->assertEquals(
337            'val4',
338            $url->getParam('param4', 'wrongval'),
339            'Url::addParams does not add new parameters'
340        );
341        $this->assertEquals(
342            'newval3',
343            $url->getParam('param3', 'wrongval'),
344            'Url::addParams does not overwrite existing existing parameters'
345        );
346        $this->assertEquals(
347            array('val3', 'newval3'),
348            $url->getParams()->getValues('param3'),
349            'Url::addParams does not overwrite existing existing parameters'
350        );
351    }
352
353    /**
354     * @depends testWhetherFromPathProperlyRecognizesAndDecodesQueryParameters
355     */
356    public function testWhetherOverwriteParamsOverwritesExistingParameters()
357    {
358        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2&param3=val3');
359        $url->overwriteParams(array('param4' => 'val4', 'param3' => 'newval3'));
360
361        $this->assertEquals(
362            'val4',
363            $url->getParam('param4', 'wrongval'),
364            'Url::addParams does not add new parameters'
365        );
366        $this->assertEquals(
367            'newval3',
368            $url->getParam('param3', 'wrongval'),
369            'Url::addParams does not overwrite existing parameters'
370        );
371    }
372
373    public function testWhetherEqualUrlMaches()
374    {
375        $url1 = '/whatever/is/here?a=b&c=d';
376        $url2 = Url::fromPath('whatever/is/here', array('a' => 'b', 'c' => 'd'));
377        $this->assertEquals(
378            true,
379            $url2->matches($url1)
380        );
381    }
382
383    public function testWhetherDifferentUrlDoesNotMatch()
384    {
385        $url1 = '/whatever/is/here?a=b&d=d';
386        $url2 = Url::fromPath('whatever/is/here', array('a' => 'b', 'c' => 'd'));
387        $this->assertEquals(
388            false,
389            $url2->matches($url1)
390        );
391    }
392
393    /**
394     * @depends testWhetherGetAbsoluteUrlReturnsTheAbsoluteUrl
395     */
396    public function testWhetherToStringConversionReturnsTheAbsoluteUrlForHtmlAttributes()
397    {
398        $url = Url::fromPath('/my/test/url.html?param=val&param2=val2&param3=val3');
399
400        $this->assertEquals(
401            '/my/test/url.html?param=val&amp;param2=val2&amp;param3=val3',
402            (string) $url,
403            'Converting a url to string does not return the absolute url'
404        );
405    }
406}
407