Completed
Push — master ( ae9952...b2a7f6 )
by
unknown
137:19 queued 113:13
created

BaseTest   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 690
Duplicated Lines 2.03 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
wmc 67
lcom 1
cbo 20
dl 14
loc 690
rs 2.95
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A 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 25 4
A assertPropertiesCorrect() 0 18 6
A assertPropertiesCorrectUnsorted() 7 10 3
A assertStructPropertiesCorrect() 7 14 4
A sortItems() 0 11 3
B assertPropertiesEqual() 0 24 8
A createUserVersion1() 0 32 1
A createMediaUserVersion1() 0 8 1
A createCustomUserVersion1() 0 10 1
A createCustomUserWithLogin() 0 50 1
A createUser() 0 27 2
A createDateTime() 0 9 2
A refreshSearch() 0 28 4
A createRoleWithPolicies() 0 27 4
A createUserWithPolicies() 0 29 2
A getRawDatabaseConnection() 0 14 2
A performRawDatabaseOperation() 0 21 2
A assertValidationErrorOccurs() 0 8 1
A createFolder() 0 26 3

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

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
228
            if ($propertyValue instanceof ValueObject) {
229
                $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName);
230
            } else {
231
                $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true);
232
            }
233
        }
234
    }
235
236
    /**
237
     * Asserts all properties from $expectedValues are correctly set in
238
     * $actualObject. Additional (virtual) properties can be asserted using
239
     * $additionalProperties.
240
     *
241
     * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues
242
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
243
     * @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...
244
     */
245
    protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array())
246
    {
247 View Code Duplication
        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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
            if ($propertyValue instanceof ValueObject) {
249
                $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName);
250
            } else {
251
                $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
252
            }
253
        }
254
255
        foreach ($additionalProperties as $propertyName) {
256
            $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName);
257
        }
258
    }
259
260
    /**
261
     * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted()
262
     *
263
     * @param array $items An array of scalar values
264
     */
265
    private function sortItems(array &$items)
266
    {
267
        $sorter = function ($a, $b) {
268
            if (!is_scalar($a) || !is_scalar($b)) {
269
                $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values');
270
            }
271
272
            return strcmp($a, $b);
273
        };
274
        usort($items, $sorter);
275
    }
276
277
    private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false)
278
    {
279
        if ($expectedValue instanceof ArrayObject) {
280
            $expectedValue = $expectedValue->getArrayCopy();
281
        } elseif ($expectedValue instanceof DateTime) {
282
            $expectedValue = $expectedValue->format(DateTime::RFC850);
283
        }
284
        if ($actualValue instanceof ArrayObject) {
285
            $actualValue = $actualValue->getArrayCopy();
286
        } elseif ($actualValue instanceof DateTime) {
287
            $actualValue = $actualValue->format(DateTime::RFC850);
288
        }
289
290
        if ($sortArray && is_array($actualValue) && is_array($expectedValue)) {
291
            $this->sortItems($actualValue);
292
            $this->sortItems($expectedValue);
293
        }
294
295
        $this->assertEquals(
296
            $expectedValue,
297
            $actualValue,
298
            sprintf('Object property "%s" incorrect.', $propertyName)
299
        );
300
    }
301
302
    /**
303
     * Create a user in editor user group.
304
     *
305
     * @param string $login
306
     *
307
     * @return \eZ\Publish\API\Repository\Values\User\User
308
     */
309
    protected function createUserVersion1($login = 'user')
310
    {
311
        $repository = $this->getRepository();
312
313
        /* BEGIN: Inline */
314
        // ID of the "Editors" user group in an eZ Publish demo installation
315
        $editorsGroupId = 13;
316
317
        $userService = $repository->getUserService();
318
319
        // Instantiate a create struct with mandatory properties
320
        $userCreate = $userService->newUserCreateStruct(
321
            $login,
322
            "{$login}@example.com",
323
            'secret',
324
            'eng-US'
325
        );
326
        $userCreate->enabled = true;
327
328
        // Set some fields required by the user ContentType
329
        $userCreate->setField('first_name', 'Example');
330
        $userCreate->setField('last_name', 'User');
331
332
        // Load parent group for the user
333
        $group = $userService->loadUserGroup($editorsGroupId);
334
335
        // Create a new user instance.
336
        $user = $userService->createUser($userCreate, array($group));
337
        /* END: Inline */
338
339
        return $user;
340
    }
341
342
    /**
343
     * Create a user in new user group with editor rights limited to Media Library (/1/48/).
344
     *
345
     * @uses ::createCustomUserVersion1()
346
     *
347
     * @return \eZ\Publish\API\Repository\Values\User\User
348
     */
349
    protected function createMediaUserVersion1()
350
    {
351
        return $this->createCustomUserVersion1(
352
            'Media Editor',
353
            'Editor',
354
            new SubtreeLimitation(array('limitationValues' => array('/1/43/')))
355
        );
356
    }
357
358
    /**
359
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
360
     *
361
     * @param string $userGroupName Name of the new user group to create
362
     * @param string $roleIdentifier Role identifier to assign to the new group
363
     * @param RoleLimitation|null $roleLimitation
364
     *
365
     * @return \eZ\Publish\API\Repository\Values\User\User
366
     */
367
    protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null)
368
    {
369
        return $this->createCustomUserWithLogin(
370
            'user',
371
            '[email protected]',
372
            $userGroupName,
373
            $roleIdentifier,
374
            $roleLimitation
375
        );
376
    }
377
378
    /**
379
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
380
     *
381
     * @param string $login User login
382
     * @param string $email User e-mail
383
     * @param string $userGroupName Name of the new user group to create
384
     * @param string $roleIdentifier Role identifier to assign to the new group
385
     * @param RoleLimitation|null $roleLimitation
386
     * @return \eZ\Publish\API\Repository\Values\User\User
387
     */
388
    protected function createCustomUserWithLogin(
389
        $login,
390
        $email,
391
        $userGroupName,
392
        $roleIdentifier,
393
        RoleLimitation $roleLimitation = null
394
    ) {
395
        $repository = $this->getRepository();
396
397
        /* BEGIN: Inline */
398
        // ID of the "Users" user group in an eZ Publish demo installation
399
        $rootUsersGroupId = $this->generateId('location', 4);
400
401
        $roleService = $repository->getRoleService();
402
        $userService = $repository->getUserService();
403
404
        // Get a group create struct
405
        $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US');
406
        $userGroupCreate->setField('name', $userGroupName);
407
408
        // Create new group with media editor rights
409
        $userGroup = $userService->createUserGroup(
410
            $userGroupCreate,
411
            $userService->loadUserGroup($rootUsersGroupId)
412
        );
413
        $roleService->assignRoleToUserGroup(
414
            $roleService->loadRoleByIdentifier($roleIdentifier),
415
            $userGroup,
416
            $roleLimitation
417
        );
418
419
        // Instantiate a create struct with mandatory properties
420
        $userCreate = $userService->newUserCreateStruct(
421
            $login,
422
            $email,
423
            'secret',
424
            'eng-US'
425
        );
426
        $userCreate->enabled = true;
427
428
        // Set some fields required by the user ContentType
429
        $userCreate->setField('first_name', 'Example');
430
        $userCreate->setField('last_name', ucfirst($login));
431
432
        // Create a new user instance.
433
        $user = $userService->createUser($userCreate, array($userGroup));
434
        /* END: Inline */
435
436
        return $user;
437
    }
438
439
    /**
440
     * Create a user using given data.
441
     *
442
     * @param string $login
443
     * @param string $firstName
444
     * @param string $lastName
445
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default
446
     *
447
     * @return \eZ\Publish\API\Repository\Values\User\User
448
     */
449
    protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null)
450
    {
451
        $repository = $this->getRepository();
452
453
        $userService = $repository->getUserService();
454
        if (null === $userGroup) {
455
            $userGroup = $userService->loadUserGroup(13);
456
        }
457
458
        // Instantiate a create struct with mandatory properties
459
        $userCreate = $userService->newUserCreateStruct(
460
            $login,
461
            "{$login}@example.com",
462
            'secret',
463
            'eng-US'
464
        );
465
        $userCreate->enabled = true;
466
467
        // Set some fields required by the user ContentType
468
        $userCreate->setField('first_name', $firstName);
469
        $userCreate->setField('last_name', $lastName);
470
471
        // Create a new user instance.
472
        $user = $userService->createUser($userCreate, array($userGroup));
473
474
        return $user;
475
    }
476
477
    /**
478
     * Only for internal use.
479
     *
480
     * Creates a \DateTime object for $timestamp in the current time zone
481
     *
482
     * @param int $timestamp
483
     *
484
     * @return \DateTime
485
     */
486
    public function createDateTime($timestamp = null)
487
    {
488
        $dateTime = new \DateTime();
489
        if ($timestamp !== null) {
490
            $dateTime->setTimestamp($timestamp);
491
        }
492
493
        return $dateTime;
494
    }
495
496
    /**
497
     * Calls given Repository's aggregated SearchHandler::refresh().
498
     *
499
     * Currently implemented only in Solr search engine.
500
     *
501
     * @param \eZ\Publish\API\Repository\Repository $repository
502
     */
503
    protected function refreshSearch(Repository $repository)
504
    {
505
        $setupFactory = $this->getSetupFactory();
506
507
        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...
508
            return;
509
        }
510
511
        while (true) {
512
            $repositoryReflection = new \ReflectionObject($repository);
513
            // If the repository is decorated, we need to recurse in the "repository" property
514
            if (!$repositoryReflection->hasProperty('repository')) {
515
                break;
516
            }
517
518
            $repositoryProperty = $repositoryReflection->getProperty('repository');
519
            $repositoryProperty->setAccessible(true);
520
            $repository = $repositoryProperty->getValue($repository);
521
        }
522
523
        $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler');
524
        $searchHandlerProperty->setAccessible(true);
525
526
        /** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */
527
        $searchHandler = $searchHandlerProperty->getValue($repository);
528
529
        $searchHandler->commit();
530
    }
531
532
    /**
533
     * Create role of a given name with the given policies described by an array.
534
     *
535
     * @param $roleName
536
     * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []]
537
     *
538
     * @return \eZ\Publish\API\Repository\Values\User\Role
539
     *
540
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
541
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException
542
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
543
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
544
     */
545
    public function createRoleWithPolicies($roleName, array $policiesData)
546
    {
547
        $repository = $this->getRepository(false);
548
        $roleService = $repository->getRoleService();
549
550
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
551
        foreach ($policiesData as $policyData) {
552
            $policyCreateStruct = $roleService->newPolicyCreateStruct(
553
                $policyData['module'],
554
                $policyData['function']
555
            );
556
557
            if (isset($policyData['limitations'])) {
558
                foreach ($policyData['limitations'] as $limitation) {
559
                    $policyCreateStruct->addLimitation($limitation);
560
                }
561
            }
562
563
            $roleCreateStruct->addPolicy($policyCreateStruct);
564
        }
565
566
        $roleDraft = $roleService->createRole($roleCreateStruct);
567
568
        $roleService->publishRoleDraft($roleDraft);
569
570
        return $roleService->loadRole($roleDraft->id);
571
    }
572
573
    /**
574
     * Create user and assign new role with the given policies.
575
     *
576
     * @param string $login
577
     * @param array $policiesData list of policies in the form of <code>[ [ 'module' => 'name', 'function' => 'name'] ]</code>
578
     *
579
     * @return \eZ\Publish\API\Repository\Values\User\User
580
     *
581
     * @throws \Exception
582
     */
583
    public function createUserWithPolicies($login, array $policiesData)
584
    {
585
        $repository = $this->getRepository(false);
586
        $roleService = $repository->getRoleService();
587
        $userService = $repository->getUserService();
588
589
        $repository->beginTransaction();
590
        try {
591
            $userCreateStruct = $userService->newUserCreateStruct(
592
                $login,
593
                "{$login}@test.local",
594
                $login,
595
                'eng-GB'
596
            );
597
            $userCreateStruct->setField('first_name', $login);
598
            $userCreateStruct->setField('last_name', $login);
599
            $user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]);
600
601
            $role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_'), $policiesData);
602
            $roleService->assignRoleToUser($role, $user);
603
604
            $repository->commit();
605
606
            return $user;
607
        } catch (\Exception $ex) {
608
            $repository->rollback();
609
            throw $ex;
610
        }
611
    }
612
613
    /**
614
     * @return \Doctrine\DBAL\Connection
615
     *
616
     * @throws \ErrorException
617
     */
618
    protected function getRawDatabaseConnection()
619
    {
620
        $connection = $this
621
            ->getSetupFactory()
622
            ->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.connection');
623
624
        if (!$connection instanceof Connection) {
625
            throw new \RuntimeException(
626
                sprintf('Expected %s got %s', Connection::class, get_class($connection))
627
            );
628
        }
629
630
        return $connection;
631
    }
632
633
    /**
634
     * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection).
635
     * Returns the result returned by the given callback.
636
     *
637
     * **Note**: The method clears the entire persistence cache pool.
638
     *
639
     * @throws \Exception
640
     *
641
     * @param callable $callback
642
     *
643
     * @return mixed the return result of the given callback
644
     */
645
    public function performRawDatabaseOperation(callable $callback)
646
    {
647
        $repository = $this->getRepository(false);
648
        $repository->beginTransaction();
649
        try {
650
            $callback(
651
                $this->getRawDatabaseConnection()
652
            );
653
            $repository->commit();
654
        } catch (Exception $e) {
655
            $repository->rollback();
656
            throw $e;
657
        }
658
659
        /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */
660
        $cachePool = $this
661
            ->getSetupFactory()
662
            ->getServiceContainer()->get('ezpublish.cache_pool');
663
664
        $cachePool->clear();
665
    }
666
667
    /**
668
     * Traverse all errors for all fields in all Translations to find expected one.
669
     *
670
     * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception
671
     * @param string $expectedValidationErrorMessage
672
     */
673
    protected function assertValidationErrorOccurs(
674
        ContentFieldValidationException $exception,
675
        $expectedValidationErrorMessage
676
    ) {
677
        $constraint = new PHPUnitConstraintValidationErrorOccurs($expectedValidationErrorMessage);
678
679
        self::assertThat($exception, $constraint);
680
    }
681
682
    /**
683
     * Create 'folder' Content.
684
     *
685
     * @param array $names Folder names in the form of <code>['&lt;language_code&gt;' => '&lt;name&gt;']</code>
686
     * @param int $parentLocationId
687
     *
688
     * @return \eZ\Publish\API\Repository\Values\Content\Content published Content
689
     *
690
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
691
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
692
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
693
     */
694
    protected function createFolder(array $names, $parentLocationId)
695
    {
696
        $repository = $this->getRepository(false);
697
        $contentService = $repository->getContentService();
698
        $contentTypeService = $repository->getContentTypeService();
699
        $locationService = $repository->getLocationService();
700
701
        if (empty($names)) {
702
            throw new \RuntimeException(sprintf('%s expects non-empty names list', __METHOD__));
703
        }
704
        $mainLanguageCode = array_keys($names)[0];
705
706
        $struct = $contentService->newContentCreateStruct(
707
            $contentTypeService->loadContentTypeByIdentifier('folder'),
708
            $mainLanguageCode
709
        );
710
        foreach ($names as $languageCode => $translatedName) {
711
            $struct->setField('name', $translatedName, $languageCode);
712
        }
713
        $contentDraft = $contentService->createContent(
714
            $struct,
715
            [$locationService->newLocationCreateStruct($parentLocationId)]
716
        );
717
718
        return $contentService->publishVersion($contentDraft->versionInfo);
719
    }
720
}
721