Completed
Push — 6.7 ( 19695c...758853 )
by Łukasz
57:55
created

BaseTest   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 636
Duplicated Lines 6.45 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 0
Metric Value
dl 41
loc 636
rs 3.564
c 0
b 0
f 0
wmc 60
lcom 1
cbo 20

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