Completed
Push — 6.7 ( 92f9bc...ec3164 )
by André
11:20
created

BaseTest   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 488
Duplicated Lines 8.4 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
dl 41
loc 488
rs 8.6206
c 0
b 0
f 0
wmc 50
lcom 1
cbo 12

21 Methods

Rating   Name   Duplication   Size   Complexity  
B setUp() 0 29 4
A tearDown() 0 5 1
A getIdManager() 0 4 1
A generateId() 0 4 1
A parseId() 0 4 1
A getConfigValue() 0 4 1
A isVersion4() 0 4 2
A getRepository() 0 8 2
A getSetupFactory() 0 17 4
A assertPropertiesCorrect() 0 6 2
A assertPropertiesCorrectUnsorted() 0 6 2
A assertStructPropertiesCorrect() 0 10 3
A sortItems() 0 11 3
C assertPropertiesEqual() 0 24 8
B createUserVersion1() 32 32 1
A createMediaUserVersion1() 0 8 1
B createCustomUserVersion1() 0 45 1
B createUser() 0 27 2
A createDateTime() 9 9 2
B refreshSearch() 0 28 4
B createRoleWithPolicies() 0 27 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BaseTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BaseTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * File containing the BaseTest class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Tests;
10
11
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
12
use PHPUnit\Framework\TestCase;
13
use eZ\Publish\API\Repository\Repository;
14
use eZ\Publish\API\Repository\Values\ValueObject;
15
use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation;
16
use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation;
17
use eZ\Publish\API\Repository\Values\User\UserGroup;
18
use eZ\Publish\Core\REST\Client\Sessionable;
19
use DateTime;
20
use ArrayObject;
21
use Exception;
22
use PDOException;
23
24
/**
25
 * Base class for api specific tests.
26
 */
27
abstract class BaseTest extends TestCase
28
{
29
    /**
30
     * Maximum integer number accepted by the different backends.
31
     */
32
    const DB_INT_MAX = 2147483647;
33
34
    /**
35
     * @var \eZ\Publish\API\Repository\Tests\SetupFactory
36
     */
37
    private $setupFactory;
38
39
    /**
40
     * @var \eZ\Publish\API\Repository\Repository
41
     */
42
    private $repository;
43
44
    protected function setUp()
45
    {
46
        parent::setUp();
47
48
        try {
49
            // Use setup factory instance here w/o clearing data in case test don't need to
50
            $repository = $this->getSetupFactory()->getRepository(false);
51
52
            // Set session if we are testing the REST backend to make it
53
            // possible to persist data in the memory backend during multiple
54
            // requests.
55
            if ($repository instanceof Sessionable) {
56
                $repository->setSession($id = md5(microtime()));
57
            }
58
        } catch (PDOException $e) {
59
            $this->fail(
60
                'The communication with the database cannot be established. ' .
61
                "This is required in order to perform the tests.\n\n" .
62
                'Exception: ' . $e
63
            );
64
        } catch (Exception $e) {
65
            $this->fail(
66
                'Cannot create a repository with predefined user. ' .
67
                'Check the UserService or RoleService implementation. ' .
68
                PHP_EOL . PHP_EOL .
69
                'Exception: ' . $e
70
            );
71
        }
72
    }
73
74
    /**
75
     * Resets the temporary used repository between each test run.
76
     */
77
    protected function tearDown()
78
    {
79
        $this->repository = null;
80
        parent::tearDown();
81
    }
82
83
    /**
84
     * Returns the ID generator, fitting to the repository implementation.
85
     *
86
     * @return \eZ\Publish\API\Repository\Tests\IdManager
87
     */
88
    protected function getIdManager()
89
    {
90
        return $this->getSetupFactory()->getIdManager();
91
    }
92
93
    /**
94
     * Generates a repository specific ID value.
95
     *
96
     * @param string $type
97
     * @param mixed $rawId
98
     *
99
     * @return mixed
100
     */
101
    protected function generateId($type, $rawId)
102
    {
103
        return $this->getIdManager()->generateId($type, $rawId);
104
    }
105
106
    /**
107
     * Parses a repository specific ID value.
108
     *
109
     * @param string $type
110
     * @param mixed $id
111
     *
112
     * @return mixed
113
     */
114
    protected function parseId($type, $id)
115
    {
116
        return $this->getIdManager()->parseId($type, $id);
117
    }
118
119
    /**
120
     * Returns a config setting provided by the setup factory.
121
     *
122
     * @param string $configKey
123
     *
124
     * @return mixed
125
     */
126
    protected function getConfigValue($configKey)
127
    {
128
        return $this->getSetupFactory()->getConfigValue($configKey);
129
    }
130
131
    /**
132
     * Tests if the currently tested api is based on a V4 implementation.
133
     *
134
     * @return bool
135
     */
136
    protected function isVersion4()
137
    {
138
        return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion'];
139
    }
140
141
    /**
142
     * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test
143
     *
144
     * @return \eZ\Publish\API\Repository\Repository
145
     */
146
    protected function getRepository($initialInitializeFromScratch = true)
147
    {
148
        if (null === $this->repository) {
149
            $this->repository = $this->getSetupFactory()->getRepository($initialInitializeFromScratch);
150
        }
151
152
        return $this->repository;
153
    }
154
155
    /**
156
     * @return \eZ\Publish\API\Repository\Tests\SetupFactory
157
     */
158
    protected function getSetupFactory()
159
    {
160
        if (null === $this->setupFactory) {
161
            if (false === isset($_ENV['setupFactory'])) {
162
                throw new \ErrorException('Missing mandatory setting $_ENV["setupFactory"].');
163
            }
164
165
            $setupClass = $_ENV['setupFactory'];
166
            if (false === class_exists($setupClass)) {
167
                throw new \ErrorException('$_ENV["setupFactory"] does not reference an existing class.');
168
            }
169
170
            $this->setupFactory = new $setupClass();
171
        }
172
173
        return $this->setupFactory;
174
    }
175
176
    /**
177
     * Asserts that properties given in $expectedValues are correctly set in
178
     * $actualObject.
179
     *
180
     * @param mixed[] $expectedValues
181
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
182
     */
183
    protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject)
184
    {
185
        foreach ($expectedValues as $propertyName => $propertyValue) {
186
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
187
        }
188
    }
189
190
    /**
191
     * Asserts that properties given in $expectedValues are correctly set in
192
     * $actualObject.
193
     *
194
     * If the property type is array, it will be sorted before comparison.
195
     *
196
     * @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734
197
     *
198
     * @param mixed[] $expectedValues
199
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
200
     */
201
    protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject)
202
    {
203
        foreach ($expectedValues as $propertyName => $propertyValue) {
204
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true);
205
        }
206
    }
207
208
    /**
209
     * Asserts all properties from $expectedValues are correctly set in
210
     * $actualObject. Additional (virtual) properties can be asserted using
211
     * $additionalProperties.
212
     *
213
     * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues
214
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
215
     * @param array $propertyNames
0 ignored issues
show
Bug introduced by
There is no parameter named $propertyNames. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
216
     */
217
    protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array())
218
    {
219
        foreach ($expectedValues as $propertyName => $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $expectedValues of type object<eZ\Publish\API\Re...ory\Values\ValueObject> is not traversable.
Loading history...
220
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
221
        }
222
223
        foreach ($additionalProperties as $propertyName) {
224
            $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName);
225
        }
226
    }
227
228
    /**
229
     * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted()
230
     *
231
     * @param array $items An array of scalar values
232
     */
233
    private function sortItems(array &$items)
234
    {
235
        $sorter = function ($a, $b) {
236
            if (!is_scalar($a) || !is_scalar($b)) {
237
                $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values');
238
            }
239
240
            return strcmp($a, $b);
241
        };
242
        usort($items, $sorter);
243
    }
244
245
    private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false)
246
    {
247
        if ($expectedValue instanceof ArrayObject) {
248
            $expectedValue = $expectedValue->getArrayCopy();
249
        } elseif ($expectedValue instanceof DateTime) {
250
            $expectedValue = $expectedValue->format(DateTime::RFC850);
251
        }
252
        if ($actualValue instanceof ArrayObject) {
253
            $actualValue = $actualValue->getArrayCopy();
254
        } elseif ($actualValue instanceof DateTime) {
255
            $actualValue = $actualValue->format(DateTime::RFC850);
256
        }
257
258
        if ($sortArray && is_array($actualValue) && is_array($expectedValue)) {
259
            $this->sortItems($actualValue);
260
            $this->sortItems($expectedValue);
261
        }
262
263
        $this->assertEquals(
264
            $expectedValue,
265
            $actualValue,
266
            sprintf('Object property "%s" incorrect.', $propertyName)
267
        );
268
    }
269
270
    /**
271
     * Create a user in editor user group.
272
     *
273
     * @param string $login
274
     *
275
     * @return \eZ\Publish\API\Repository\Values\User\User
276
     */
277 View Code Duplication
    protected function createUserVersion1($login = 'user')
278
    {
279
        $repository = $this->getRepository();
280
281
        /* BEGIN: Inline */
282
        // ID of the "Editors" user group in an eZ Publish demo installation
283
        $editorsGroupId = 13;
284
285
        $userService = $repository->getUserService();
286
287
        // Instantiate a create struct with mandatory properties
288
        $userCreate = $userService->newUserCreateStruct(
289
            $login,
290
            "{$login}@example.com",
291
            'secret',
292
            'eng-US'
293
        );
294
        $userCreate->enabled = true;
295
296
        // Set some fields required by the user ContentType
297
        $userCreate->setField('first_name', 'Example');
298
        $userCreate->setField('last_name', 'User');
299
300
        // Load parent group for the user
301
        $group = $userService->loadUserGroup($editorsGroupId);
302
303
        // Create a new user instance.
304
        $user = $userService->createUser($userCreate, array($group));
305
        /* END: Inline */
306
307
        return $user;
308
    }
309
310
    /**
311
     * Create a user in new user group with editor rights limited to Media Library (/1/48/).
312
     *
313
     * @uses ::createCustomUserVersion1()
314
     *
315
     * @return \eZ\Publish\API\Repository\Values\User\User
316
     */
317
    protected function createMediaUserVersion1()
318
    {
319
        return $this->createCustomUserVersion1(
320
            'Media Editor',
321
            'Editor',
322
            new SubtreeLimitation(array('limitationValues' => array('/1/43/')))
323
        );
324
    }
325
326
    /**
327
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
328
     *
329
     * @param string $userGroupName Name of the new user group to create
330
     * @param string $roleIdentifier Role identifier to assign to the new group
331
     * @param RoleLimitation|null $roleLimitation
332
     *
333
     * @return \eZ\Publish\API\Repository\Values\User\User
334
     */
335
    protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null)
336
    {
337
        $repository = $this->getRepository();
338
339
        /* BEGIN: Inline */
340
        // ID of the "Users" user group in an eZ Publish demo installation
341
        $rootUsersGroupId = $this->generateId('location', 4);
342
343
        $roleService = $repository->getRoleService();
344
        $userService = $repository->getUserService();
345
346
        // Get a group create struct
347
        $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US');
348
        $userGroupCreate->setField('name', $userGroupName);
349
350
        // Create new group with media editor rights
351
        $userGroup = $userService->createUserGroup(
352
            $userGroupCreate,
353
            $userService->loadUserGroup($rootUsersGroupId)
354
        );
355
        $roleService->assignRoleToUserGroup(
356
            $roleService->loadRoleByIdentifier($roleIdentifier),
357
            $userGroup,
358
            $roleLimitation
359
        );
360
361
        // Instantiate a create struct with mandatory properties
362
        $userCreate = $userService->newUserCreateStruct(
363
            'user',
364
            '[email protected]',
365
            'secret',
366
            'eng-US'
367
        );
368
        $userCreate->enabled = true;
369
370
        // Set some fields required by the user ContentType
371
        $userCreate->setField('first_name', 'Example');
372
        $userCreate->setField('last_name', 'User');
373
374
        // Create a new user instance.
375
        $user = $userService->createUser($userCreate, array($userGroup));
376
        /* END: Inline */
377
378
        return $user;
379
    }
380
381
    /**
382
     * Create a user using given data.
383
     *
384
     * @param string $login
385
     * @param string $firstName
386
     * @param string $lastName
387
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default
388
     *
389
     * @return \eZ\Publish\API\Repository\Values\User\User
390
     */
391
    protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null)
392
    {
393
        $repository = $this->getRepository();
394
395
        $userService = $repository->getUserService();
396
        if (null === $userGroup) {
397
            $userGroup = $userService->loadUserGroup(13);
398
        }
399
400
        // Instantiate a create struct with mandatory properties
401
        $userCreate = $userService->newUserCreateStruct(
402
            $login,
403
            "{$login}@example.com",
404
            'secret',
405
            'eng-US'
406
        );
407
        $userCreate->enabled = true;
408
409
        // Set some fields required by the user ContentType
410
        $userCreate->setField('first_name', $firstName);
411
        $userCreate->setField('last_name', $lastName);
412
413
        // Create a new user instance.
414
        $user = $userService->createUser($userCreate, array($userGroup));
415
416
        return $user;
417
    }
418
419
    /**
420
     * Only for internal use.
421
     *
422
     * Creates a \DateTime object for $timestamp in the current time zone
423
     *
424
     * @param int $timestamp
425
     *
426
     * @return \DateTime
427
     */
428 View Code Duplication
    public function createDateTime($timestamp = null)
429
    {
430
        $dateTime = new \DateTime();
431
        if ($timestamp !== null) {
432
            $dateTime->setTimestamp($timestamp);
433
        }
434
435
        return $dateTime;
436
    }
437
438
    /**
439
     * Calls given Repository's aggregated SearchHandler::refresh().
440
     *
441
     * Currently implemented only in Solr search engine.
442
     *
443
     * @param \eZ\Publish\API\Repository\Repository $repository
444
     */
445
    protected function refreshSearch(Repository $repository)
446
    {
447
        $setupFactory = $this->getSetupFactory();
448
449
        if (!$setupFactory instanceof LegacySolrSetupFactory) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
450
            return;
451
        }
452
453
        while (true) {
454
            $repositoryReflection = new \ReflectionObject($repository);
455
            // If the repository is decorated, we need to recurse in the "repository" property
456
            if (!$repositoryReflection->hasProperty('repository')) {
457
                break;
458
            }
459
460
            $repositoryProperty = $repositoryReflection->getProperty('repository');
461
            $repositoryProperty->setAccessible(true);
462
            $repository = $repositoryProperty->getValue($repository);
463
        }
464
465
        $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler');
466
        $searchHandlerProperty->setAccessible(true);
467
468
        /** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */
469
        $searchHandler = $searchHandlerProperty->getValue($repository);
470
471
        $searchHandler->commit();
472
    }
473
474
    /**
475
     * Create role of a given name with the given policies described by an array.
476
     *
477
     * @param $roleName
478
     * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []]
479
     *
480
     * @return \eZ\Publish\API\Repository\Values\User\Role
481
     *
482
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
483
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException
484
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
485
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
486
     */
487
    public function createRoleWithPolicies($roleName, array $policiesData)
488
    {
489
        $repository = $this->getRepository(false);
490
        $roleService = $repository->getRoleService();
491
492
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
493
        foreach ($policiesData as $policyData) {
494
            $policyCreateStruct = $roleService->newPolicyCreateStruct(
495
                $policyData['module'],
496
                $policyData['function']
497
            );
498
499
            if (isset($policyData['limitations'])) {
500
                foreach ($policyData['limitations'] as $limitation) {
501
                    $policyCreateStruct->addLimitation($limitation);
502
                }
503
            }
504
505
            $roleCreateStruct->addPolicy($policyCreateStruct);
506
        }
507
508
        $roleDraft = $roleService->createRole($roleCreateStruct);
509
510
        $roleService->publishRoleDraft($roleDraft);
511
512
        return $roleService->loadRole($roleDraft->id);
0 ignored issues
show
Documentation introduced by
The property $id is declared protected in eZ\Publish\API\Repository\Values\User\Role. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
513
    }
514
}
515