Completed
Push — 6.7 ( 805cdd...fa132c )
by Łukasz
20:48
created

BaseTest::createUserWithPolicies()   B

Complexity

Conditions 2
Paths 8

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 21
nc 8
nop 2
dl 0
loc 29
rs 8.8571
c 0
b 0
f 0
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 eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException;
12
use eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ValidationErrorOccurs as PHPUnitConstraintValidationErrorOccurs;
13
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
14
use PHPUnit\Framework\TestCase;
15
use eZ\Publish\API\Repository\Repository;
16
use eZ\Publish\API\Repository\Values\ValueObject;
17
use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation;
18
use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation;
19
use eZ\Publish\API\Repository\Values\User\UserGroup;
20
use eZ\Publish\Core\REST\Client\Sessionable;
21
use DateTime;
22
use ArrayObject;
23
use Exception;
24
use PDOException;
25
26
/**
27
 * Base class for api specific tests.
28
 */
29
abstract class BaseTest extends TestCase
30
{
31
    /**
32
     * Maximum integer number accepted by the different backends.
33
     */
34
    const DB_INT_MAX = 2147483647;
35
36
    /**
37
     * @var \eZ\Publish\API\Repository\Tests\SetupFactory
38
     */
39
    private $setupFactory;
40
41
    /**
42
     * @var \eZ\Publish\API\Repository\Repository
43
     */
44
    private $repository;
45
46
    protected function setUp()
47
    {
48
        parent::setUp();
49
50
        try {
51
            // Use setup factory instance here w/o clearing data in case test don't need to
52
            $repository = $this->getSetupFactory()->getRepository(false);
53
54
            // Set session if we are testing the REST backend to make it
55
            // possible to persist data in the memory backend during multiple
56
            // requests.
57
            if ($repository instanceof Sessionable) {
58
                $repository->setSession($id = md5(microtime()));
59
            }
60
        } catch (PDOException $e) {
61
            $this->fail(
62
                'The communication with the database cannot be established. ' .
63
                "This is required in order to perform the tests.\n\n" .
64
                'Exception: ' . $e
65
            );
66
        } catch (Exception $e) {
67
            $this->fail(
68
                'Cannot create a repository with predefined user. ' .
69
                'Check the UserService or RoleService implementation. ' .
70
                PHP_EOL . PHP_EOL .
71
                'Exception: ' . $e
72
            );
73
        }
74
    }
75
76
    /**
77
     * Resets the temporary used repository between each test run.
78
     */
79
    protected function tearDown()
80
    {
81
        $this->repository = null;
82
        parent::tearDown();
83
    }
84
85
    /**
86
     * Returns the ID generator, fitting to the repository implementation.
87
     *
88
     * @return \eZ\Publish\API\Repository\Tests\IdManager
89
     */
90
    protected function getIdManager()
91
    {
92
        return $this->getSetupFactory()->getIdManager();
93
    }
94
95
    /**
96
     * Generates a repository specific ID value.
97
     *
98
     * @param string $type
99
     * @param mixed $rawId
100
     *
101
     * @return mixed
102
     */
103
    protected function generateId($type, $rawId)
104
    {
105
        return $this->getIdManager()->generateId($type, $rawId);
106
    }
107
108
    /**
109
     * Parses a repository specific ID value.
110
     *
111
     * @param string $type
112
     * @param mixed $id
113
     *
114
     * @return mixed
115
     */
116
    protected function parseId($type, $id)
117
    {
118
        return $this->getIdManager()->parseId($type, $id);
119
    }
120
121
    /**
122
     * Returns a config setting provided by the setup factory.
123
     *
124
     * @param string $configKey
125
     *
126
     * @return mixed
127
     */
128
    protected function getConfigValue($configKey)
129
    {
130
        return $this->getSetupFactory()->getConfigValue($configKey);
131
    }
132
133
    /**
134
     * Tests if the currently tested api is based on a V4 implementation.
135
     *
136
     * @return bool
137
     */
138
    protected function isVersion4()
139
    {
140
        return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion'];
141
    }
142
143
    /**
144
     * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test
145
     *
146
     * @return \eZ\Publish\API\Repository\Repository
147
     */
148
    protected function getRepository($initialInitializeFromScratch = true)
149
    {
150
        if (null === $this->repository) {
151
            $this->repository = $this->getSetupFactory()->getRepository($initialInitializeFromScratch);
152
        }
153
154
        return $this->repository;
155
    }
156
157
    /**
158
     * @return \eZ\Publish\API\Repository\Tests\SetupFactory
159
     */
160
    protected function getSetupFactory()
161
    {
162
        if (null === $this->setupFactory) {
163
            if (false === isset($_ENV['setupFactory'])) {
164
                throw new \ErrorException('Missing mandatory setting $_ENV["setupFactory"].');
165
            }
166
167
            $setupClass = $_ENV['setupFactory'];
168
            if (false === class_exists($setupClass)) {
169
                throw new \ErrorException('$_ENV["setupFactory"] does not reference an existing class.');
170
            }
171
172
            $this->setupFactory = new $setupClass();
173
        }
174
175
        return $this->setupFactory;
176
    }
177
178
    /**
179
     * Asserts that properties given in $expectedValues are correctly set in
180
     * $actualObject.
181
     *
182
     * @param mixed[] $expectedValues
183
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
184
     */
185
    protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject)
186
    {
187
        foreach ($expectedValues as $propertyName => $propertyValue) {
188
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
189
        }
190
    }
191
192
    /**
193
     * Asserts that properties given in $expectedValues are correctly set in
194
     * $actualObject.
195
     *
196
     * If the property type is array, it will be sorted before comparison.
197
     *
198
     * @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734
199
     *
200
     * @param mixed[] $expectedValues
201
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
202
     */
203
    protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject)
204
    {
205
        foreach ($expectedValues as $propertyName => $propertyValue) {
206
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true);
207
        }
208
    }
209
210
    /**
211
     * Asserts all properties from $expectedValues are correctly set in
212
     * $actualObject. Additional (virtual) properties can be asserted using
213
     * $additionalProperties.
214
     *
215
     * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues
216
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
217
     * @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...
218
     */
219
    protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array())
220
    {
221
        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...
222
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
223
        }
224
225
        foreach ($additionalProperties as $propertyName) {
226
            $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName);
227
        }
228
    }
229
230
    /**
231
     * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted()
232
     *
233
     * @param array $items An array of scalar values
234
     */
235
    private function sortItems(array &$items)
236
    {
237
        $sorter = function ($a, $b) {
238
            if (!is_scalar($a) || !is_scalar($b)) {
239
                $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values');
240
            }
241
242
            return strcmp($a, $b);
243
        };
244
        usort($items, $sorter);
245
    }
246
247
    private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false)
248
    {
249
        if ($expectedValue instanceof ArrayObject) {
250
            $expectedValue = $expectedValue->getArrayCopy();
251
        } elseif ($expectedValue instanceof DateTime) {
252
            $expectedValue = $expectedValue->format(DateTime::RFC850);
253
        }
254
        if ($actualValue instanceof ArrayObject) {
255
            $actualValue = $actualValue->getArrayCopy();
256
        } elseif ($actualValue instanceof DateTime) {
257
            $actualValue = $actualValue->format(DateTime::RFC850);
258
        }
259
260
        if ($sortArray && is_array($actualValue) && is_array($expectedValue)) {
261
            $this->sortItems($actualValue);
262
            $this->sortItems($expectedValue);
263
        }
264
265
        $this->assertEquals(
266
            $expectedValue,
267
            $actualValue,
268
            sprintf('Object property "%s" incorrect.', $propertyName)
269
        );
270
    }
271
272
    /**
273
     * Create a user in editor user group.
274
     *
275
     * @param string $login
276
     *
277
     * @return \eZ\Publish\API\Repository\Values\User\User
278
     */
279 View Code Duplication
    protected function createUserVersion1($login = 'user')
280
    {
281
        $repository = $this->getRepository();
282
283
        /* BEGIN: Inline */
284
        // ID of the "Editors" user group in an eZ Publish demo installation
285
        $editorsGroupId = 13;
286
287
        $userService = $repository->getUserService();
288
289
        // Instantiate a create struct with mandatory properties
290
        $userCreate = $userService->newUserCreateStruct(
291
            $login,
292
            "{$login}@example.com",
293
            'secret',
294
            'eng-US'
295
        );
296
        $userCreate->enabled = true;
297
298
        // Set some fields required by the user ContentType
299
        $userCreate->setField('first_name', 'Example');
300
        $userCreate->setField('last_name', 'User');
301
302
        // Load parent group for the user
303
        $group = $userService->loadUserGroup($editorsGroupId);
304
305
        // Create a new user instance.
306
        $user = $userService->createUser($userCreate, array($group));
307
        /* END: Inline */
308
309
        return $user;
310
    }
311
312
    /**
313
     * Create a user in new user group with editor rights limited to Media Library (/1/48/).
314
     *
315
     * @uses ::createCustomUserVersion1()
316
     *
317
     * @return \eZ\Publish\API\Repository\Values\User\User
318
     */
319
    protected function createMediaUserVersion1()
320
    {
321
        return $this->createCustomUserVersion1(
322
            'Media Editor',
323
            'Editor',
324
            new SubtreeLimitation(array('limitationValues' => array('/1/43/')))
325
        );
326
    }
327
328
    /**
329
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
330
     *
331
     * @param string $userGroupName Name of the new user group to create
332
     * @param string $roleIdentifier Role identifier to assign to the new group
333
     * @param RoleLimitation|null $roleLimitation
334
     *
335
     * @return \eZ\Publish\API\Repository\Values\User\User
336
     */
337
    protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null)
338
    {
339
        $repository = $this->getRepository();
340
341
        /* BEGIN: Inline */
342
        // ID of the "Users" user group in an eZ Publish demo installation
343
        $rootUsersGroupId = $this->generateId('location', 4);
344
345
        $roleService = $repository->getRoleService();
346
        $userService = $repository->getUserService();
347
348
        // Get a group create struct
349
        $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US');
350
        $userGroupCreate->setField('name', $userGroupName);
351
352
        // Create new group with media editor rights
353
        $userGroup = $userService->createUserGroup(
354
            $userGroupCreate,
355
            $userService->loadUserGroup($rootUsersGroupId)
356
        );
357
        $roleService->assignRoleToUserGroup(
358
            $roleService->loadRoleByIdentifier($roleIdentifier),
359
            $userGroup,
360
            $roleLimitation
361
        );
362
363
        // Instantiate a create struct with mandatory properties
364
        $userCreate = $userService->newUserCreateStruct(
365
            'user',
366
            '[email protected]',
367
            'secret',
368
            'eng-US'
369
        );
370
        $userCreate->enabled = true;
371
372
        // Set some fields required by the user ContentType
373
        $userCreate->setField('first_name', 'Example');
374
        $userCreate->setField('last_name', 'User');
375
376
        // Create a new user instance.
377
        $user = $userService->createUser($userCreate, array($userGroup));
378
        /* END: Inline */
379
380
        return $user;
381
    }
382
383
    /**
384
     * Create a user using given data.
385
     *
386
     * @param string $login
387
     * @param string $firstName
388
     * @param string $lastName
389
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default
390
     *
391
     * @return \eZ\Publish\API\Repository\Values\User\User
392
     */
393
    protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null)
394
    {
395
        $repository = $this->getRepository();
396
397
        $userService = $repository->getUserService();
398
        if (null === $userGroup) {
399
            $userGroup = $userService->loadUserGroup(13);
400
        }
401
402
        // Instantiate a create struct with mandatory properties
403
        $userCreate = $userService->newUserCreateStruct(
404
            $login,
405
            "{$login}@example.com",
406
            'secret',
407
            'eng-US'
408
        );
409
        $userCreate->enabled = true;
410
411
        // Set some fields required by the user ContentType
412
        $userCreate->setField('first_name', $firstName);
413
        $userCreate->setField('last_name', $lastName);
414
415
        // Create a new user instance.
416
        $user = $userService->createUser($userCreate, array($userGroup));
417
418
        return $user;
419
    }
420
421
    /**
422
     * Only for internal use.
423
     *
424
     * Creates a \DateTime object for $timestamp in the current time zone
425
     *
426
     * @param int $timestamp
427
     *
428
     * @return \DateTime
429
     */
430 View Code Duplication
    public function createDateTime($timestamp = null)
431
    {
432
        $dateTime = new \DateTime();
433
        if ($timestamp !== null) {
434
            $dateTime->setTimestamp($timestamp);
435
        }
436
437
        return $dateTime;
438
    }
439
440
    /**
441
     * Calls given Repository's aggregated SearchHandler::refresh().
442
     *
443
     * Currently implemented only in Solr search engine.
444
     *
445
     * @param \eZ\Publish\API\Repository\Repository $repository
446
     */
447
    protected function refreshSearch(Repository $repository)
448
    {
449
        $setupFactory = $this->getSetupFactory();
450
451
        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...
452
            return;
453
        }
454
455
        while (true) {
456
            $repositoryReflection = new \ReflectionObject($repository);
457
            // If the repository is decorated, we need to recurse in the "repository" property
458
            if (!$repositoryReflection->hasProperty('repository')) {
459
                break;
460
            }
461
462
            $repositoryProperty = $repositoryReflection->getProperty('repository');
463
            $repositoryProperty->setAccessible(true);
464
            $repository = $repositoryProperty->getValue($repository);
465
        }
466
467
        $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler');
468
        $searchHandlerProperty->setAccessible(true);
469
470
        /** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */
471
        $searchHandler = $searchHandlerProperty->getValue($repository);
472
473
        $searchHandler->commit();
474
    }
475
476
    /**
477
     * Create role of a given name with the given policies described by an array.
478
     *
479
     * @param $roleName
480
     * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []]
481
     *
482
     * @return \eZ\Publish\API\Repository\Values\User\Role
483
     *
484
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
485
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException
486
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
487
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
488
     */
489
    public function createRoleWithPolicies($roleName, array $policiesData)
490
    {
491
        $repository = $this->getRepository(false);
492
        $roleService = $repository->getRoleService();
493
494
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
495
        foreach ($policiesData as $policyData) {
496
            $policyCreateStruct = $roleService->newPolicyCreateStruct(
497
                $policyData['module'],
498
                $policyData['function']
499
            );
500
501
            if (isset($policyData['limitations'])) {
502
                foreach ($policyData['limitations'] as $limitation) {
503
                    $policyCreateStruct->addLimitation($limitation);
504
                }
505
            }
506
507
            $roleCreateStruct->addPolicy($policyCreateStruct);
508
        }
509
510
        $roleDraft = $roleService->createRole($roleCreateStruct);
511
512
        $roleService->publishRoleDraft($roleDraft);
513
514
        return $roleService->loadRole($roleDraft->id);
515
    }
516
517
    /**
518
     * Create user and assign new role with the given policies.
519
     *
520
     * @param string $login
521
     * @param array $policiesData list of policies in the form of <code>[ [ 'module' => 'name', 'function' => 'name'] ]</code>
522
     *
523
     * @return \eZ\Publish\API\Repository\Values\User\User
524
     *
525
     * @throws \Exception
526
     */
527
    public function createUserWithPolicies($login, array $policiesData)
528
    {
529
        $repository = $this->getRepository(false);
530
        $roleService = $repository->getRoleService();
531
        $userService = $repository->getUserService();
532
533
        $repository->beginTransaction();
534
        try {
535
            $userCreateStruct = $userService->newUserCreateStruct(
536
                $login,
537
                "{$login}@test.local",
538
                $login,
539
                'eng-GB'
540
            );
541
            $userCreateStruct->setField('first_name', $login);
542
            $userCreateStruct->setField('last_name', $login);
543
            $user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]);
544
545
            $role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_'), $policiesData);
546
            $roleService->assignRoleToUser($role, $user);
547
548
            $repository->commit();
549
550
            return $user;
551
        } catch (\Exception $ex) {
552
            $repository->rollback();
553
            throw $ex;
554
        }
555
    }
556
557
    /**
558
     * Traverse all errors for all fields in all Translations to find expected one.
559
     *
560
     * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception
561
     * @param string $expectedValidationErrorMessage
562
     */
563
    protected function assertValidationErrorOccurs(
564
        ContentFieldValidationException $exception,
565
        $expectedValidationErrorMessage
566
    ) {
567
        $constraint = new PHPUnitConstraintValidationErrorOccurs($expectedValidationErrorMessage);
568
569
        self::assertThat($exception, $constraint);
570
    }
571
}
572