ObjectUrlTest   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 301
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 301
rs 10
c 1
b 0
f 0
wmc 16
lcom 0
cbo 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A testInvalidRemoteUrl() 0 4 1
A testRemoteDraftUrl() 0 6 1
A testInvalidUrl() 0 4 1
A testInvalidUrlPath() 0 4 1
A testUrlSchemeSetter() 0 6 1
A testUrlHostSetter() 0 6 1
A testUrlPortSetter() 0 6 1
A testUrlPathOverride() 0 8 1
A testUrlAbsolute() 0 6 1
A testUrlAbsoluteLocal() 0 5 1
A testUrlRelative() 0 5 1
B testRemoteUrl() 0 26 1
A testLeadedLocalUrl() 0 8 1
B testUrlSetters() 0 29 1
A testUrlRemote() 0 7 1
B testObjectUrlComparison() 0 44 1
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Infrastructure
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Tests;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Domain\Model\Object\Id;
41
use Apparat\Object\Domain\Model\Object\Revision;
42
use Apparat\Object\Domain\Model\Object\Type;
43
use Apparat\Object\Domain\Model\Uri\ObjectUrl;
44
use Apparat\Object\Domain\Repository\Service;
45
use Apparat\Object\Ports\Types\Object as ObjectTypes;
46
47
/**
48
 * Object URL tests
49
 *
50
 * @package Apparat\Object
51
 * @subpackage Apparat\Object\Test
52
 */
53
class ObjectUrlTest extends AbstractDisabledAutoconnectorTest
54
{
55
    /**
56
     * Example query fragment
57
     *
58
     * @var string
59
     */
60
    const QUERY_FRAGMENT = '?param=value#fragment';
61
    /**
62
     * Repository URL
63
     *
64
     * @var string
65
     */
66
    const REPOSITORY_URL = '/repo';
67
    /**
68
     * Example locator
69
     *
70
     * @var string
71
     */
72
    const LOCATOR = '/2015/10/01/36704-event/36704-1';
73
    /**
74
     * Example locator (draft mode)
75
     *
76
     * @var string
77
     */
78
    const DRAFT_LOCATOR = '/2015/10/01/36704-event/.36704';
79
    /**
80
     * Example URL
81
     *
82
     * @var string
83
     */
84
    const URL = self::REPOSITORY_URL.self::LOCATOR.self::QUERY_FRAGMENT;
85
    /**
86
     * Example remote repository URL
87
     *
88
     * @var string
89
     */
90
    const REMOTE_REPOSITORY_URL = 'http://apparat:[email protected]:80';
91
    /**
92
     * Example remote URL
93
     *
94
     * @var string
95
     */
96
    const REMOTE_URL = self::REMOTE_REPOSITORY_URL.self::LOCATOR.self::QUERY_FRAGMENT;
97
98
    /**
99
     * Test an URL
100
     *
101
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
102
     * @expectedExceptionCode 1451515385
103
     */
104
    public function testInvalidRemoteUrl()
105
    {
106
        new ObjectUrl(self::REMOTE_URL);
107
    }
108
109
    /**
110
     * Test a remote URL
111
     */
112
    public function testRemoteUrl()
113
    {
114
        $url = new ObjectUrl(self::REMOTE_URL, true);
115
        $this->assertInstanceOf(ObjectUrl::class, $url);
116
        $this->assertEquals(self::REMOTE_URL, strval($url));
117
        $this->assertEquals('http', $url->getScheme());
118
        $this->assertEquals('apparat', $url->getUser());
119
        $this->assertEquals('tools', $url->getPassword());
120
        $this->assertEquals('apparat.tools', $url->getHost());
121
        $this->assertEquals(80, $url->getPort());
122
        $this->assertEquals('', $url->getPath());
123
        $this->assertEquals(['param' => 'value'], $url->getQueryParams());
124
        $this->assertEquals('param=value', $url->getQuery());
125
        $this->assertEquals('fragment', $url->getFragment());
126
        $this->assertInstanceOf(\DateTimeImmutable::class, $url->getCreationDate());
127
        $this->assertEquals('2015-10-01', $url->getCreationDate()->format('Y-m-d'));
128
        $this->assertInstanceOf(Id::class, $url->getId());
129
        $this->assertEquals(new Id(36704), $url->getId());
130
        $this->assertInstanceOf(Type::class, $url->getObjectType());
131
        $this->assertEquals(Kernel::create(Type::class, [ObjectTypes::EVENT]), $url->getObjectType());
132
        $this->assertInstanceOf(Revision::class, $url->getRevision());
133
        $this->assertEquals(new Revision(1), $url->getRevision());
134
        $this->assertEquals(self::REMOTE_REPOSITORY_URL, Service::normalizeRepositoryUrl($url));
135
        $this->assertFalse($url->isDraft());
136
        $this->assertTrue($url->setDraft(true)->isDraft());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Apparat\Object\Domain\Model\Uri\LocatorInterface as the method isDraft() does only exist in the following implementations of said interface: Apparat\Object\Domain\Model\Uri\ApparatUrl, Apparat\Object\Domain\Model\Uri\ObjectUrl, Apparat\Object\Tests\TestObjectUrl.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
137
    }
138
139
    /**
140
     * Test a remote draft URL
141
     */
142
    public function testRemoteDraftUrl()
143
    {
144
        $url = new ObjectUrl(self::REMOTE_REPOSITORY_URL.self::DRAFT_LOCATOR, true);
145
        $this->assertInstanceOf(ObjectUrl::class, $url);
146
        $this->assertTrue($url->isDraft());
147
    }
148
149
    /**
150
     * Test a local URL with path prefix
151
     */
152
    public function testLeadedLocalUrl()
153
    {
154
        $pathPrefix = '/prefix/path';
155
        $url = new ObjectUrl($pathPrefix.self::LOCATOR);
156
        $this->assertEquals($pathPrefix, $url->getPath());
157
        $this->assertEquals(self::LOCATOR, $url->getLocator());
158
        $this->assertEquals($pathPrefix.strtok(self::LOCATOR, '-'), $url->toUrl(true));
159
    }
160
161
    /**
162
     * Test an invalid URL
163
     *
164
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
165
     * @expectedExceptionCode 1449873819
166
     */
167
    public function testInvalidUrl()
168
    {
169
        new ObjectUrl('invalid://');
170
    }
171
172
    /**
173
     * Test an invalid URL path
174
     *
175
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
176
     * @expectedExceptionCode 1449874494
177
     */
178
    public function testInvalidUrlPath()
179
    {
180
        new ObjectUrl('http://invalid~url*path', true);
181
    }
182
183
    /**
184
     * Test the scheme setter
185
     *
186
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
187
     * @expectedExceptionCode 1449924914
188
     */
189
    public function testUrlSchemeSetter()
190
    {
191
        $url = new ObjectUrl(self::URL);
192
        $this->assertEquals(ObjectUrl::SCHEME_HTTPS, $url->setScheme(ObjectUrl::SCHEME_HTTPS)->getScheme());
193
        $url->setScheme('invalid');
194
    }
195
196
    /**
197
     * Test the host setter
198
     *
199
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
200
     * @expectedExceptionCode 1449925567
201
     */
202
    public function testUrlHostSetter()
203
    {
204
        $url = new ObjectUrl(self::URL);
205
        $this->assertEquals('apparat.com', $url->setHost('apparat.com')->getHost());
206
        $url->setHost('_');
207
    }
208
209
    /**
210
     * Test the port setter
211
     *
212
     * @expectedException \Apparat\Object\Domain\Model\Uri\InvalidArgumentException
213
     * @expectedExceptionCode 1449925885
214
     */
215
    public function testUrlPortSetter()
216
    {
217
        $url = new ObjectUrl(self::URL);
218
        $this->assertEquals(443, $url->setPort(443)->getPort());
219
        $url->setPort(123456789);
220
    }
221
222
    /**
223
     * Test the remaining setter methods
224
     */
225
    public function testUrlSetters()
226
    {
227
        /** @var Type $articleType */
228
        $articleType = Kernel::create(Type::class, [ObjectTypes::ARTICLE]);
229
        $url = new ObjectUrl(self::URL);
230
        $this->assertEquals('test', $url->setUser('test')->getUser());
231
        $this->assertEquals(null, $url->setUser(null)->getUser());
232
        $this->assertEquals('password', $url->setPassword('password')->getPassword());
233
        $this->assertEquals(null, $url->setPassword(null)->getPassword());
234
        $this->assertEquals('/path/prefix', $url->setPath('/path/prefix')->getPath());
235
        $this->assertEquals(['param2' => 'value2'], $url->setQueryParams(['param2' => 'value2'])->getQueryParams());
236
        $this->assertEquals('param=value', $url->setQuery('param=value')->getQuery());
237
        $this->assertEquals('fragment2', $url->setFragment('fragment2')->getFragment());
238
239
        $this->assertEquals(
240
            '2016-01-01',
241
            $url->setCreationDate(new \DateTimeImmutable('@1451606400'))->getCreationDate()->format('Y-m-d')
242
        );
243
        $this->assertEquals(123, $url->setId(new Id(123))->getId()->getId());
244
        $this->assertEquals(
245
            'article',
246
            $url->setObjectType($articleType)->getObjectType()->getType()
247
        );
248
        $this->assertTrue($url->setHidden(true)->isHidden());
249
        $this->assertEquals(
250
            Revision::CURRENT,
251
            $url->setRevision(Revision::current())->getRevision()->getRevision()
252
        );
253
    }
254
255
    /**
256
     * Test the override functionality when getting the URL path
257
     */
258
    public function testUrlPathOverride()
259
    {
260
        $url = new TestObjectUrl(self::URL);
261
        $this->assertEquals(
262
            'https://user:[email protected]:443/path/prefix/2015/10/01/36704-event/36704-2?param2=value2#fragment2',
263
            $url->getUrlOverride()
264
        );
265
    }
266
267
    /**
268
     * Test absolute URL
269
     */
270
    public function testUrlAbsolute()
271
    {
272
        $url = new ObjectUrl(self::REMOTE_URL, true);
273
        $this->assertEquals(true, $url->isAbsolute());
274
        $this->assertEquals(self::REMOTE_REPOSITORY_URL, $url->getRepositoryUrl());
275
    }
276
277
    /**
278
     * Test absolute URL
279
     */
280
    public function testUrlAbsoluteLocal()
281
    {
282
        $url = new ObjectUrl(rtrim(getenv('APPARAT_BASE_URL'), '/').self::REPOSITORY_URL.self::LOCATOR, true);
283
        $this->assertTrue($url->isAbsoluteLocal());
284
    }
285
286
    /**
287
     * Test relative URL
288
     */
289
    public function testUrlRelative()
290
    {
291
        $url = new ObjectUrl(self::LOCATOR.self::QUERY_FRAGMENT);
292
        $this->assertEquals(false, $url->isAbsolute());
293
    }
294
295
    /**
296
     * Test remote URL
297
     */
298
    public function testUrlRemote()
299
    {
300
        $url = new ObjectUrl(self::REMOTE_REPOSITORY_URL.self::REPOSITORY_URL.self::LOCATOR, true);
301
        $this->assertTrue($url->isRemote());
302
        $url = new ObjectUrl(rtrim(getenv('APPARAT_BASE_URL'), '/').self::REPOSITORY_URL.self::LOCATOR, true);
303
        $this->assertFalse($url->isRemote());
304
    }
305
306
    /**
307
     * Test object URL comparison
308
     */
309
    public function testObjectUrlComparison()
310
    {
311
        $this->assertFalse(
312
            (
313
            new ObjectUrl(
314
                'http://example.com/2015/10/01/36704-event/36704-1',
315
                true
316
            )
317
            )->matches(new ObjectUrl('https://example.com/2015/10/01/36704-event/36704-1', true))
318
        );
319
        $this->assertFalse(
320
            (
321
            new ObjectUrl(
322
                'http://example.com/2015/10/01/36704-event/36704-1',
323
                true
324
            )
325
            )->matches(new ObjectUrl('http://example.com/2016/10/01/36704-event/36704-1', true))
326
        );
327
        $this->assertFalse(
328
            (
329
            new ObjectUrl(
330
                'http://example.com/2015/10/01/36704-event/36704-1',
331
                true
332
            )
333
            )->matches(new ObjectUrl('http://example.com/2015/10/01/36705-event/36705-1', true))
334
        );
335
        $this->assertFalse(
336
            (
337
            new ObjectUrl(
338
                'http://example.com/2015/10/01/36704-event/36704-1',
339
                true
340
            )
341
            )->matches(new ObjectUrl('http://example.com/2015/10/01/36704-article/36704-1', true))
342
        );
343
        $this->assertFalse(
344
            (
345
            new ObjectUrl(
346
                'http://example.com/2015/10/01/36704-event/36704-1',
347
                true
348
            )
349
            )->matches(new ObjectUrl('http://example.com/2015/10/01/36704-event/36704-2', true))
350
        );
351
        $this->assertTrue((new ObjectUrl(self::REMOTE_URL, true))->matches(new ObjectUrl(self::REMOTE_URL, true)));
352
    }
353
}
354