Completed
Push — ezp-30928-as_a_developer_i_wan... ( b10e65 )
by
unknown
33:52 queued 18:51
created

testCreateContentThrowsUnauthorizedException()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 56
rs 8.9599
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * File contains: eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest 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\Core\Repository\Tests\Service\Mock;
10
11
use eZ\Publish\API\Repository\Repository;
12
use eZ\Publish\API\Repository\Values\Content\Language;
13
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
14
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
15
use eZ\Publish\API\Repository\ContentTypeService as APIContentTypeService;
16
use eZ\Publish\API\Repository\LocationService as APILocationService;
17
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
18
use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo;
19
use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType;
20
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
21
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition;
22
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct;
23
use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException;
24
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
25
use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest;
26
use eZ\Publish\Core\Repository\ContentService;
27
use eZ\Publish\Core\Repository\Values\Content\Location;
28
use eZ\Publish\Core\Repository\Values\Content\Content;
29
use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct;
30
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
31
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
32
use eZ\Publish\Core\Repository\Helper\DomainMapper;
33
use eZ\Publish\Core\Repository\Helper\RelationProcessor;
34
use eZ\Publish\Core\Repository\Helper\NameSchemaService;
35
use eZ\Publish\API\Repository\Values\Content\Field;
36
use eZ\Publish\Core\FieldType\Value;
37
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
38
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
39
use eZ\Publish\Core\Repository\Values\ContentType\ContentType;
40
use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition;
41
use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
42
use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType;
43
use eZ\Publish\SPI\Persistence\Content as SPIContent;
44
use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct;
45
use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct;
46
use eZ\Publish\SPI\Persistence\Content\Field as SPIField;
47
use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as SPIObjectStateGroup;
48
use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState;
49
use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo;
50
use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo;
51
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct;
52
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
53
use eZ\Publish\Core\Repository\Values\User\UserReference;
54
use Exception;
55
56
/**
57
 * Mock test case for Content service.
58
 */
59
class ContentTest extends BaseServiceMockTest
60
{
61
    /**
62
     * Represents empty Field Value.
63
     */
64
    const EMPTY_FIELD_VALUE = 'empty';
65
66
    /**
67
     * Test for the __construct() method.
68
     *
69
     * @covers \eZ\Publish\Core\Repository\ContentService::__construct
70
     */
71
    public function testConstructor(): void
72
    {
73
        $repositoryMock = $this->getRepositoryMock();
74
        /** @var \eZ\Publish\SPI\Persistence\Handler $persistenceHandlerMock */
75
        $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler');
76
        $domainMapperMock = $this->getDomainMapperMock();
77
        $relationProcessorMock = $this->getRelationProcessorMock();
78
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
79
        $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock();
80
        $permissionResolverMock = $this->getPermissionResolverMock();
81
        $settings = ['default_version_archive_limit' => 10];
82
83
        $service = new ContentService(
0 ignored issues
show
Unused Code introduced by
$service is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
84
            $repositoryMock,
0 ignored issues
show
Bug introduced by
It seems like $repositoryMock defined by $this->getRepositoryMock() on line 73 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\API\Repository\Repository>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85
            $persistenceHandlerMock,
86
            $domainMapperMock,
0 ignored issues
show
Bug introduced by
It seems like $domainMapperMock defined by $this->getDomainMapperMock() on line 76 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...ry\Helper\DomainMapper>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
87
            $relationProcessorMock,
0 ignored issues
show
Bug introduced by
It seems like $relationProcessorMock defined by $this->getRelationProcessorMock() on line 77 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...lper\RelationProcessor>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
88
            $nameSchemaServiceMock,
0 ignored issues
show
Bug introduced by
It seems like $nameSchemaServiceMock defined by $this->getNameSchemaServiceMock() on line 78 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...lper\NameSchemaService>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
89
            $fieldTypeRegistryMock,
90
            $permissionResolverMock,
91
            $settings
92
        );
93
    }
94
95
    /**
96
     * Test for the loadVersionInfo() method.
97
     *
98
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
99
     */
100
    public function testLoadVersionInfoById()
101
    {
102
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
103
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
104
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
105
        $contentHandler = $this->getPersistenceMock()->contentHandler();
106
        $domainMapperMock = $this->getDomainMapperMock();
107
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
108
        $permissionResolver = $this->getPermissionResolverMock();
109
110
        $versionInfoMock->expects($this->once())
111
            ->method('isPublished')
112
            ->willReturn(true);
113
114
        $contentServiceMock->expects($this->once())
115
            ->method('loadContentInfo')
116
            ->with($this->equalTo(42))
117
            ->will(
118
                $this->returnValue(
119
                    new ContentInfo(['currentVersionNo' => 24])
120
                )
121
            );
122
123
        $contentHandler->expects($this->once())
124
            ->method('loadVersionInfo')
125
            ->with(
126
                $this->equalTo(42),
127
                $this->equalTo(24)
128
            )->will(
129
                $this->returnValue(new SPIVersionInfo())
130
            );
131
132
        $domainMapperMock->expects($this->once())
133
            ->method('buildVersionInfoDomainObject')
134
            ->with(new SPIVersionInfo())
135
            ->will($this->returnValue($versionInfoMock));
136
137
        $permissionResolver->expects($this->once())
138
            ->method('canUser')
139
            ->with(
140
                $this->equalTo('content'),
141
                $this->equalTo('read'),
142
                $this->equalTo($versionInfoMock)
143
            )->will($this->returnValue(true));
144
145
        $result = $contentServiceMock->loadVersionInfoById(42);
146
147
        $this->assertEquals($versionInfoMock, $result);
148
    }
149
150
    /**
151
     * Test for the loadVersionInfo() method.
152
     *
153
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
154
     */
155
    public function testLoadVersionInfoByIdThrowsNotFoundException()
156
    {
157
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class);
158
159
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
160
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
161
        $contentHandler = $this->getPersistenceMock()->contentHandler();
162
163
        $contentHandler->expects($this->once())
164
            ->method('loadVersionInfo')
165
            ->with(
166
                $this->equalTo(42),
167
                $this->equalTo(24)
168
            )->will(
169
                $this->throwException(
170
                    new NotFoundException(
171
                        'Content',
172
                        [
173
                            'contentId' => 42,
174
                            'versionNo' => 24,
175
                        ]
176
                    )
177
                )
178
            );
179
180
        $contentServiceMock->loadVersionInfoById(42, 24);
181
    }
182
183
    /**
184
     * Test for the loadVersionInfo() method.
185
     *
186
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
187
     */
188
    public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion()
189
    {
190
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
191
192
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
193
        $contentServiceMock = $this->getPartlyMockedContentService();
194
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
195
        $contentHandler = $this->getPersistenceMock()->contentHandler();
196
        $domainMapperMock = $this->getDomainMapperMock();
197
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
198
        $permissionResolver = $this->getPermissionResolverMock();
199
200
        $versionInfoMock->expects($this->any())
201
            ->method('isPublished')
202
            ->willReturn(false);
203
204
        $contentHandler->expects($this->once())
205
            ->method('loadVersionInfo')
206
            ->with(
207
                $this->equalTo(42),
208
                $this->equalTo(24)
209
            )->will(
210
                $this->returnValue(new SPIVersionInfo())
211
            );
212
213
        $domainMapperMock->expects($this->once())
214
            ->method('buildVersionInfoDomainObject')
215
            ->with(new SPIVersionInfo())
216
            ->will($this->returnValue($versionInfoMock));
217
218
        $permissionResolver->expects($this->once())
219
            ->method('canUser')
220
            ->with(
221
                $this->equalTo('content'),
222
                $this->equalTo('versionread'),
223
                $this->equalTo($versionInfoMock)
224
            )->will($this->returnValue(false));
225
226
        $contentServiceMock->loadVersionInfoById(42, 24);
227
    }
228
229
    /**
230
     * Test for the loadVersionInfo() method.
231
     *
232
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
233
     */
234 View Code Duplication
    public function testLoadVersionInfoByIdPublishedVersion()
235
    {
236
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
237
        $contentServiceMock = $this->getPartlyMockedContentService();
238
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
239
        $contentHandler = $this->getPersistenceMock()->contentHandler();
240
        $domainMapperMock = $this->getDomainMapperMock();
241
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
242
        $permissionResolver = $this->getPermissionResolverMock();
243
244
        $versionInfoMock->expects($this->once())
245
            ->method('isPublished')
246
            ->willReturn(true);
247
248
        $contentHandler->expects($this->once())
249
            ->method('loadVersionInfo')
250
            ->with(
251
                $this->equalTo(42),
252
                $this->equalTo(24)
253
            )->will(
254
                $this->returnValue(new SPIVersionInfo())
255
            );
256
257
        $domainMapperMock->expects($this->once())
258
            ->method('buildVersionInfoDomainObject')
259
            ->with(new SPIVersionInfo())
260
            ->will($this->returnValue($versionInfoMock));
261
262
        $permissionResolver->expects($this->once())
263
            ->method('canUser')
264
            ->with(
265
                $this->equalTo('content'),
266
                $this->equalTo('read'),
267
                $this->equalTo($versionInfoMock)
268
            )->will($this->returnValue(true));
269
270
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
271
272
        $this->assertEquals($versionInfoMock, $result);
273
    }
274
275
    /**
276
     * Test for the loadVersionInfo() method.
277
     *
278
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
279
     */
280 View Code Duplication
    public function testLoadVersionInfoByIdNonPublishedVersion()
281
    {
282
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
283
        $contentServiceMock = $this->getPartlyMockedContentService();
284
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
285
        $contentHandler = $this->getPersistenceMock()->contentHandler();
286
        $domainMapperMock = $this->getDomainMapperMock();
287
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
288
        $permissionResolver = $this->getPermissionResolverMock();
289
290
        $versionInfoMock->expects($this->once())
291
            ->method('isPublished')
292
            ->willReturn(false);
293
294
        $contentHandler->expects($this->once())
295
            ->method('loadVersionInfo')
296
            ->with(
297
                $this->equalTo(42),
298
                $this->equalTo(24)
299
            )->will(
300
                $this->returnValue(new SPIVersionInfo())
301
            );
302
303
        $domainMapperMock->expects($this->once())
304
            ->method('buildVersionInfoDomainObject')
305
            ->with(new SPIVersionInfo())
306
            ->will($this->returnValue($versionInfoMock));
307
308
        $permissionResolver->expects($this->once())
309
            ->method('canUser')
310
            ->with(
311
                $this->equalTo('content'),
312
                $this->equalTo('versionread'),
313
                $this->equalTo($versionInfoMock)
314
            )->will($this->returnValue(true));
315
316
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
317
318
        $this->assertEquals($versionInfoMock, $result);
319
    }
320
321
    /**
322
     * Test for the loadVersionInfo() method.
323
     *
324
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfo
325
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoById
326
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsNotFoundException
327
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion
328
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdPublishedVersion
329
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdNonPublishedVersion
330
     */
331
    public function testLoadVersionInfo()
332
    {
333
        $contentServiceMock = $this->getPartlyMockedContentService(
334
            ['loadVersionInfoById']
335
        );
336
        $contentServiceMock->expects(
337
            $this->once()
338
        )->method(
339
            'loadVersionInfoById'
340
        )->with(
341
            $this->equalTo(42),
342
            $this->equalTo(7)
343
        )->will(
344
            $this->returnValue('result')
345
        );
346
347
        $result = $contentServiceMock->loadVersionInfo(
348
            new ContentInfo(['id' => 42]),
349
            7
350
        );
351
352
        $this->assertEquals('result', $result);
353
    }
354
355
    public function testLoadContent()
356
    {
357
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
358
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
359
        $content = $this->createMock(APIContent::class);
360
        $versionInfo = $this->createMock(APIVersionInfo::class);
361
        $permissionResolver = $this->getPermissionResolverMock();
362
363
        $content
364
            ->expects($this->once())
365
            ->method('getVersionInfo')
366
            ->will($this->returnValue($versionInfo));
367
        $versionInfo
368
            ->expects($this->once())
369
            ->method('isPublished')
370
            ->willReturn(true);
371
        $contentId = 123;
372
        $contentService
373
            ->expects($this->once())
374
            ->method('internalLoadContent')
375
            ->with($contentId)
376
            ->will($this->returnValue($content));
377
378
        $permissionResolver
379
            ->expects($this->once())
380
            ->method('canUser')
381
            ->with('content', 'read', $content)
382
            ->will($this->returnValue(true));
383
384
        $this->assertSame($content, $contentService->loadContent($contentId));
385
    }
386
387 View Code Duplication
    public function testLoadContentNonPublished()
388
    {
389
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
390
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
391
        $content = $this->createMock(APIContent::class);
392
        $versionInfo = $this->createMock(APIVersionInfo::class);
393
        $permissionResolver = $this->getPermissionResolverMock();
394
395
        $content
396
            ->expects($this->once())
397
            ->method('getVersionInfo')
398
            ->will($this->returnValue($versionInfo));
399
        $contentId = 123;
400
        $contentService
401
            ->expects($this->once())
402
            ->method('internalLoadContent')
403
            ->with($contentId)
404
            ->will($this->returnValue($content));
405
406
        $permissionResolver
407
            ->expects($this->exactly(2))
408
            ->method('canUser')
409
            ->will(
410
                $this->returnValueMap(
411
                    [
412
                        ['content', 'read', $content, [], true],
413
                        ['content', 'versionread', $content, [], true],
414
                    ]
415
                )
416
            );
417
418
        $this->assertSame($content, $contentService->loadContent($contentId));
419
    }
420
421
    public function testLoadContentUnauthorized()
422
    {
423
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
424
425
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
426
        $permissionResolver = $this->getPermissionResolverMock();
427
428
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
429
        $content = $this->createMock(APIContent::class);
430
        $contentId = 123;
431
        $contentService
432
            ->expects($this->once())
433
            ->method('internalLoadContent')
434
            ->with($contentId)
435
            ->will($this->returnValue($content));
436
437
        $permissionResolver
438
            ->expects($this->once())
439
            ->method('canUser')
440
            ->with('content', 'read', $content)
441
            ->will($this->returnValue(false));
442
443
        $contentService->loadContent($contentId);
444
    }
445
446 View Code Duplication
    public function testLoadContentNotPublishedStatusUnauthorized()
447
    {
448
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
449
450
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
451
        $permissionResolver = $this->getPermissionResolverMock();
452
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
453
        $content = $this->createMock(APIContent::class);
454
        $versionInfo = $this
455
            ->getMockBuilder(APIVersionInfo::class)
456
            ->getMockForAbstractClass();
457
        $content
458
            ->expects($this->once())
459
            ->method('getVersionInfo')
460
            ->will($this->returnValue($versionInfo));
461
        $contentId = 123;
462
        $contentService
463
            ->expects($this->once())
464
            ->method('internalLoadContent')
465
            ->with($contentId)
466
            ->will($this->returnValue($content));
467
468
        $permissionResolver
469
            ->expects($this->exactly(2))
470
            ->method('canUser')
471
            ->will(
472
                $this->returnValueMap(
473
                    [
474
                        ['content', 'read', $content, [], true],
475
                        ['content', 'versionread', $content, [], false],
476
                    ]
477
                )
478
            );
479
480
        $contentService->loadContent($contentId);
481
    }
482
483
    /**
484
     * @dataProvider internalLoadContentProvider
485
     */
486
    public function testInternalLoadContent($id, $languages, $versionNo, $isRemoteId, $useAlwaysAvailable)
487
    {
488
        $contentService = $this->getPartlyMockedContentService();
489
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
490
        $contentHandler = $this->getPersistenceMock()->contentHandler();
491
        $realId = $id;
492
493
        if ($isRemoteId) {
494
            $realId = 123;
495
            $spiContentInfo = new SPIContentInfo(['currentVersionNo' => $versionNo ?: 7, 'id' => $realId]);
496
            $contentHandler
497
                ->expects($this->once())
498
                ->method('loadContentInfoByRemoteId')
499
                ->with($id)
500
                ->will($this->returnValue($spiContentInfo));
501
        } elseif (!empty($languages) && $useAlwaysAvailable) {
502
            $spiContentInfo = new SPIContentInfo(['alwaysAvailable' => false]);
503
            $contentHandler
504
                ->expects($this->once())
505
                ->method('loadContentInfo')
506
                ->with($id)
507
                ->will($this->returnValue($spiContentInfo));
508
        }
509
510
        $spiContent = new SPIContent([
511
            'versionInfo' => new VersionInfo([
512
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
513
            ]),
514
        ]);
515
        $contentHandler
516
            ->expects($this->once())
517
            ->method('load')
518
            ->with($realId, $versionNo, $languages)
519
            ->willReturn($spiContent);
520
521
        $content = $this->mockBuildContentDomainObject($spiContent, $languages);
522
523
        $this->assertSame(
524
            $content,
525
            $contentService->internalLoadContent($id, $languages, $versionNo, $isRemoteId, $useAlwaysAvailable)
526
        );
527
    }
528
529
    public function internalLoadContentProvider()
530
    {
531
        return [
532
            [123, null, null, false, false],
533
            [123, null, 456, false, false],
534
            [456, null, 123, false, true],
535
            [456, null, 2, false, false],
536
            [456, ['eng-GB'], 2, false, true],
537
            [456, ['eng-GB', 'fre-FR'], null, false, false],
538
            [456, ['eng-GB', 'fre-FR', 'nor-NO'], 2, false, false],
539
            // With remoteId
540
            [123, null, null, true, false],
541
            ['someRemoteId', null, 456, true, false],
542
            [456, null, 123, true, false],
543
            ['someRemoteId', null, 2, true, false],
544
            ['someRemoteId', ['eng-GB'], 2, true, false],
545
            [456, ['eng-GB', 'fre-FR'], null, true, false],
546
            ['someRemoteId', ['eng-GB', 'fre-FR', 'nor-NO'], 2, true, false],
547
        ];
548
    }
549
550
    public function testInternalLoadContentNotFound()
551
    {
552
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class);
553
554
        $contentService = $this->getPartlyMockedContentService();
555
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
556
        $contentHandler = $this->getPersistenceMock()->contentHandler();
557
        $id = 123;
558
        $versionNo = 7;
559
        $languages = null;
560
        $contentHandler
561
            ->expects($this->once())
562
            ->method('load')
563
            ->with($id, $versionNo, $languages)
564
            ->will(
565
                $this->throwException(
566
                    $this->createMock(APINotFoundException::class)
567
                )
568
            );
569
570
        $contentService->internalLoadContent($id, $languages, $versionNo);
571
    }
572
573
    /**
574
     * Test for the loadContentByContentInfo() method.
575
     *
576
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByContentInfo
577
     */
578
    public function testLoadContentByContentInfo()
579
    {
580
        $contentServiceMock = $this->getPartlyMockedContentService(
581
            ['loadContent']
582
        );
583
        $contentServiceMock->expects(
584
            $this->once()
585
        )->method(
586
            'loadContent'
587
        )->with(
588
            $this->equalTo(42),
589
            $this->equalTo(['cro-HR']),
590
            $this->equalTo(7),
591
            $this->equalTo(false)
592
        )->will(
593
            $this->returnValue('result')
594
        );
595
596
        $result = $contentServiceMock->loadContentByContentInfo(
597
            new ContentInfo(['id' => 42]),
598
            ['cro-HR'],
599
            7
600
        );
601
602
        $this->assertEquals('result', $result);
603
    }
604
605
    /**
606
     * Test for the loadContentByVersionInfo() method.
607
     *
608
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByVersionInfo
609
     */
610
    public function testLoadContentByVersionInfo()
611
    {
612
        $contentServiceMock = $this->getPartlyMockedContentService(
613
            ['loadContent']
614
        );
615
        $contentServiceMock->expects(
616
            $this->once()
617
        )->method(
618
            'loadContent'
619
        )->with(
620
            $this->equalTo(42),
621
            $this->equalTo(['cro-HR']),
622
            $this->equalTo(7),
623
            $this->equalTo(false)
624
        )->will(
625
            $this->returnValue('result')
626
        );
627
628
        $result = $contentServiceMock->loadContentByVersionInfo(
629
            new VersionInfo(
630
                [
631
                    'contentInfo' => new ContentInfo(['id' => 42]),
632
                    'versionNo' => 7,
633
                ]
634
            ),
635
            ['cro-HR']
636
        );
637
638
        $this->assertEquals('result', $result);
639
    }
640
641
    /**
642
     * Test for the deleteContent() method.
643
     *
644
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
645
     */
646
    public function testDeleteContentThrowsUnauthorizedException()
647
    {
648
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
649
650
        $repository = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repository is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
651
        $permissionResolver = $this->getPermissionResolverMock();
652
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
653
        $contentInfo = $this->createMock(APIContentInfo::class);
654
655
        $contentInfo->expects($this->any())
656
            ->method('__get')
657
            ->with('id')
658
            ->will($this->returnValue(42));
659
660
        $contentService->expects($this->once())
661
            ->method('internalLoadContentInfo')
662
            ->with(42)
663
            ->will($this->returnValue($contentInfo));
664
665
        $permissionResolver->expects($this->once())
666
            ->method('canUser')
667
            ->with('content', 'remove')
668
            ->will($this->returnValue(false));
669
670
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
671
        $contentService->deleteContent($contentInfo);
672
    }
673
674
    /**
675
     * Test for the deleteContent() method.
676
     *
677
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
678
     */
679
    public function testDeleteContent()
680
    {
681
        $repository = $this->getRepositoryMock();
682
        $permissionResolver = $this->getPermissionResolverMock();
683
684
        $permissionResolver->expects($this->once())
685
            ->method('canUser')
686
            ->with('content', 'remove')
687
            ->will($this->returnValue(true));
688
689
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
690
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandler */
691
        $urlAliasHandler = $this->getPersistenceMock()->urlAliasHandler();
692
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
693
        $locationHandler = $this->getPersistenceMock()->locationHandler();
694
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
695
        $contentHandler = $this->getPersistenceMock()->contentHandler();
696
697
        $contentInfo = $this->createMock(APIContentInfo::class);
698
699
        $contentService->expects($this->once())
700
            ->method('internalLoadContentInfo')
701
            ->with(42)
702
            ->will($this->returnValue($contentInfo));
703
704
        $contentInfo->expects($this->any())
705
            ->method('__get')
706
            ->with('id')
707
            ->will($this->returnValue(42));
708
709
        $repository->expects($this->once())->method('beginTransaction');
710
711
        $spiLocations = [
712
            new SPILocation(['id' => 1]),
713
            new SPILocation(['id' => 2]),
714
        ];
715
        $locationHandler->expects($this->once())
716
            ->method('loadLocationsByContent')
717
            ->with(42)
718
            ->will($this->returnValue($spiLocations));
719
720
        $contentHandler->expects($this->once())
721
            ->method('deleteContent')
722
            ->with(42);
723
724
        foreach ($spiLocations as $index => $spiLocation) {
725
            $urlAliasHandler->expects($this->at($index))
726
                ->method('locationDeleted')
727
                ->with($spiLocation->id);
728
        }
729
730
        $repository->expects($this->once())->method('commit');
731
732
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
733
        $contentService->deleteContent($contentInfo);
734
    }
735
736
    /**
737
     * Test for the deleteContent() method.
738
     *
739
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
740
     */
741
    public function testDeleteContentWithRollback()
742
    {
743
        $this->expectException(\Exception::class);
744
745
        $repository = $this->getRepositoryMock();
746
        $permissionResolver = $this->getPermissionResolverMock();
747
748
        $permissionResolver->expects($this->once())
749
            ->method('canUser')
750
            ->with('content', 'remove')
751
            ->will($this->returnValue(true));
752
753
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
754
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
755
        $locationHandler = $this->getPersistenceMock()->locationHandler();
756
757
        $contentInfo = $this->createMock(APIContentInfo::class);
758
759
        $contentService->expects($this->once())
760
            ->method('internalLoadContentInfo')
761
            ->with(42)
762
            ->will($this->returnValue($contentInfo));
763
764
        $contentInfo->expects($this->any())
765
            ->method('__get')
766
            ->with('id')
767
            ->will($this->returnValue(42));
768
769
        $repository->expects($this->once())->method('beginTransaction');
770
771
        $locationHandler->expects($this->once())
772
            ->method('loadLocationsByContent')
773
            ->with(42)
774
            ->will($this->throwException(new \Exception()));
775
776
        $repository->expects($this->once())->method('rollback');
777
778
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
779
        $contentService->deleteContent($contentInfo);
780
    }
781
782
    /**
783
     * Test for the deleteVersion() method.
784
     *
785
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion
786
     */
787
    public function testDeleteVersionThrowsBadStateExceptionLastVersion()
788
    {
789
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
790
791
        $repository = $this->getRepositoryMock();
792
        $permissionResolver = $this->getPermissionResolverMock();
793
794
        $permissionResolver
795
            ->expects($this->once())
796
            ->method('canUser')
797
            ->with('content', 'versionremove')
798
            ->will($this->returnValue(true));
799
        $repository
800
            ->expects($this->never())
801
            ->method('beginTransaction');
802
803
        $contentService = $this->getPartlyMockedContentService();
804
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
805
        $contentHandler = $this->getPersistenceMock()->contentHandler();
806
        $contentInfo = $this->createMock(APIContentInfo::class);
807
        $versionInfo = $this->createMock(APIVersionInfo::class);
808
809
        $contentInfo
810
            ->expects($this->any())
811
            ->method('__get')
812
            ->with('id')
813
            ->will($this->returnValue(42));
814
815
        $versionInfo
816
            ->expects($this->any())
817
            ->method('__get')
818
            ->will(
819
                $this->returnValueMap(
820
                    [
821
                        ['versionNo', 123],
822
                        ['contentInfo', $contentInfo],
823
                    ]
824
                )
825
            );
826
        $versionInfo
827
            ->expects($this->once())
828
            ->method('isPublished')
829
            ->willReturn(false);
830
831
        $contentHandler
832
            ->expects($this->once())
833
            ->method('listVersions')
834
            ->with(42)
835
            ->will($this->returnValue(['version']));
836
837
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo */
838
        $contentService->deleteVersion($versionInfo);
839
    }
840
841
    /**
842
     * Test for the createContent() method.
843
     *
844
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
845
     */
846 View Code Duplication
    public function testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet()
847
    {
848
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
849
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'mainLanguageCode\' property must be set');
850
851
        $mockedService = $this->getPartlyMockedContentService();
852
        $mockedService->createContent(new ContentCreateStruct(), []);
853
    }
854
855
    /**
856
     * Test for the createContent() method.
857
     *
858
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
859
     */
860 View Code Duplication
    public function testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet()
861
    {
862
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
863
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'contentType\' property must be set');
864
865
        $mockedService = $this->getPartlyMockedContentService();
866
        $mockedService->createContent(
867
            new ContentCreateStruct(['mainLanguageCode' => 'eng-US']),
868
            []
869
        );
870
    }
871
872
    /**
873
     * Test for the createContent() method.
874
     *
875
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
876
     */
877
    public function testCreateContentThrowsUnauthorizedException()
878
    {
879
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
880
881
        $repositoryMock = $this->getRepositoryMock();
882
883
        $permissionResolver = $this->getPermissionResolverMock();
884
        $permissionResolver->expects($this->once())
885
            ->method('getCurrentUserReference')
886
            ->will($this->returnValue(new UserReference(169)));
887
888
        $mockedService = $this->getPartlyMockedContentService();
889
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
890
        $contentType = new ContentType(
891
            [
892
                'id' => 123,
893
                'fieldDefinitions' => [],
894
            ]
895
        );
896
        $contentCreateStruct = new ContentCreateStruct(
897
            [
898
                'ownerId' => 169,
899
                'alwaysAvailable' => false,
900
                'mainLanguageCode' => 'eng-US',
901
                'contentType' => $contentType,
902
            ]
903
        );
904
905
        $contentTypeServiceMock->expects($this->once())
906
            ->method('loadContentType')
907
            ->with($this->equalTo(123))
908
            ->will($this->returnValue($contentType));
909
910
        $repositoryMock->expects($this->once())
911
            ->method('getContentTypeService')
912
            ->will($this->returnValue($contentTypeServiceMock));
913
914
        $permissionResolver->expects($this->once())
915
            ->method('canUser')
916
            ->with(
917
                $this->equalTo('content'),
918
                $this->equalTo('create'),
919
                $this->isInstanceOf(get_class($contentCreateStruct)),
920
                $this->equalTo([])
921
            )->will($this->returnValue(false));
922
923
        $mockedService->createContent(
924
            new ContentCreateStruct(
925
                [
926
                    'mainLanguageCode' => 'eng-US',
927
                    'contentType' => $contentType,
928
                ]
929
            ),
930
            []
931
        );
932
    }
933
934
    /**
935
     * Test for the createContent() method.
936
     *
937
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
938
     * @exceptionMessage Argument '$contentCreateStruct' is invalid: Another content with remoteId 'faraday' exists
939
     */
940
    public function testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId()
941
    {
942
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
943
944
        $repositoryMock = $this->getRepositoryMock();
945
        $permissionResolverMock = $this->getPermissionResolverMock();
946
        $permissionResolverMock
947
            ->expects($this->once())
948
            ->method('getCurrentUserReference')
949
            ->willReturn($this->createMock(UserReference::class));
950
951
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
952
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
953
        $contentType = new ContentType(
954
            [
955
                'id' => 123,
956
                'fieldDefinitions' => [],
957
            ]
958
        );
959
        $contentCreateStruct = new ContentCreateStruct(
960
            [
961
                'ownerId' => 169,
962
                'alwaysAvailable' => false,
963
                'remoteId' => 'faraday',
964
                'mainLanguageCode' => 'eng-US',
965
                'contentType' => $contentType,
966
            ]
967
        );
968
969
        $contentTypeServiceMock->expects($this->once())
970
            ->method('loadContentType')
971
            ->with($this->equalTo(123))
972
            ->will($this->returnValue($contentType));
973
974
        $repositoryMock->expects($this->once())
975
            ->method('getContentTypeService')
976
            ->will($this->returnValue($contentTypeServiceMock));
977
978
        $permissionResolverMock->expects($this->once())
979
            ->method('canUser')
980
            ->with(
981
                $this->equalTo('content'),
982
                $this->equalTo('create'),
983
                $this->isInstanceOf(get_class($contentCreateStruct)),
984
                $this->equalTo([])
985
            )->will($this->returnValue(true));
986
987
        $mockedService->expects($this->once())
988
            ->method('loadContentByRemoteId')
989
            ->with($contentCreateStruct->remoteId)
990
            ->will($this->returnValue('Hello...'));
991
992
        $mockedService->createContent(
993
            new ContentCreateStruct(
994
                [
995
                    'remoteId' => 'faraday',
996
                    'mainLanguageCode' => 'eng-US',
997
                    'contentType' => $contentType,
998
                ]
999
            ),
1000
            []
1001
        );
1002
    }
1003
1004
    /**
1005
     * @param string $mainLanguageCode
1006
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1007
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1008
     *
1009
     * @return array
1010
     */
1011
    protected function mapStructFieldsForCreate($mainLanguageCode, $structFields, $fieldDefinitions)
1012
    {
1013
        $mappedFieldDefinitions = [];
1014
        foreach ($fieldDefinitions as $fieldDefinition) {
1015
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
1016
        }
1017
1018
        $mappedStructFields = [];
1019
        foreach ($structFields as $structField) {
1020
            if ($structField->languageCode === null) {
1021
                $languageCode = $mainLanguageCode;
1022
            } else {
1023
                $languageCode = $structField->languageCode;
1024
            }
1025
1026
            $mappedStructFields[$structField->fieldDefIdentifier][$languageCode] = (string)$structField->value;
1027
        }
1028
1029
        return $mappedStructFields;
1030
    }
1031
1032
    /**
1033
     * Returns full, possibly redundant array of field values, indexed by field definition
1034
     * identifier and language code.
1035
     *
1036
     * @throws \RuntimeException Method is intended to be used only with consistent fixtures
1037
     *
1038
     * @param string $mainLanguageCode
1039
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1040
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1041
     * @param array $languageCodes
1042
     *
1043
     * @return array
1044
     */
1045
    protected function determineValuesForCreate(
1046
        $mainLanguageCode,
1047
        array $structFields,
1048
        array $fieldDefinitions,
1049
        array $languageCodes
1050
    ) {
1051
        $mappedStructFields = $this->mapStructFieldsForCreate(
1052
            $mainLanguageCode,
1053
            $structFields,
1054
            $fieldDefinitions
1055
        );
1056
1057
        $values = [];
1058
1059
        foreach ($fieldDefinitions as $fieldDefinition) {
1060
            $identifier = $fieldDefinition->identifier;
1061
            foreach ($languageCodes as $languageCode) {
1062 View Code Duplication
                if (!$fieldDefinition->isTranslatable) {
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...
1063
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
1064
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
1065
                    } else {
1066
                        $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1067
                    }
1068
                    continue;
1069
                }
1070
1071 View Code Duplication
                if (isset($mappedStructFields[$identifier][$languageCode])) {
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...
1072
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
1073
                    continue;
1074
                }
1075
1076
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1077
            }
1078
        }
1079
1080
        return $this->stubValues($values);
1081
    }
1082
1083
    /**
1084
     * @param string $mainLanguageCode
1085
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1086
     *
1087
     * @return string[]
1088
     */
1089
    protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields)
1090
    {
1091
        $languageCodes = [];
1092
1093
        foreach ($structFields as $field) {
1094
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1095
                continue;
1096
            }
1097
1098
            $languageCodes[$field->languageCode] = true;
1099
        }
1100
1101
        $languageCodes[$mainLanguageCode] = true;
1102
1103
        return array_keys($languageCodes);
1104
    }
1105
1106
    /**
1107
     * Asserts that calling createContent() with given API field set causes calling
1108
     * Handler::createContent() with given SPI field set.
1109
     *
1110
     * @param string $mainLanguageCode
1111
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1112
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
1113
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1114
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
1115
     * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] $objectStateGroups
0 ignored issues
show
Bug introduced by
There is no parameter named $objectStateGroups. 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...
1116
     * @param bool $execute
1117
     *
1118
     * @return mixed
1119
     */
1120
    protected function assertForTestCreateContentNonRedundantFieldSet(
1121
        $mainLanguageCode,
1122
        array $structFields,
1123
        array $spiFields,
1124
        array $fieldDefinitions,
1125
        array $locationCreateStructs = [],
1126
        $withObjectStates = false,
1127
        $execute = true
1128
    ) {
1129
        $repositoryMock = $this->getRepositoryMock();
1130
        $mockedService = $this->getPartlyMockedContentService();
1131
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
1132
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
1133
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1134
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1135
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
1136
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
1137
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1138
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1139
        $domainMapperMock = $this->getDomainMapperMock();
1140
        $relationProcessorMock = $this->getRelationProcessorMock();
1141
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
1142
        $permissionResolverMock = $this->getPermissionResolverMock();
1143
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
1144
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
1145
        $contentType = new ContentType(
1146
            [
1147
                'id' => 123,
1148
                'fieldDefinitions' => $fieldDefinitions,
1149
                'nameSchema' => '<nameSchema>',
1150
            ]
1151
        );
1152
        $contentCreateStruct = new ContentCreateStruct(
1153
            [
1154
                'fields' => $structFields,
1155
                'mainLanguageCode' => $mainLanguageCode,
1156
                'contentType' => $contentType,
1157
                'alwaysAvailable' => false,
1158
                'ownerId' => 169,
1159
                'sectionId' => 1,
1160
            ]
1161
        );
1162
1163
        $languageHandlerMock->expects($this->any())
1164
            ->method('loadByLanguageCode')
1165
            ->with($this->isType('string'))
1166
            ->will(
1167
                $this->returnCallback(
1168
                    function () {
1169
                        return new Language(['id' => 4242]);
1170
                    }
1171
                )
1172
            );
1173
1174
        $repositoryMock->expects($this->once())->method('beginTransaction');
1175
1176
        $contentTypeServiceMock->expects($this->once())
1177
            ->method('loadContentType')
1178
            ->with($this->equalTo($contentType->id))
1179
            ->will($this->returnValue($contentType));
1180
1181
        $repositoryMock->expects($this->once())
1182
            ->method('getContentTypeService')
1183
            ->will($this->returnValue($contentTypeServiceMock));
1184
1185
        $that = $this;
1186
        $permissionResolverMock->expects($this->once())
1187
            ->method('canUser')
1188
            ->with(
1189
                $this->equalTo('content'),
1190
                $this->equalTo('create'),
1191
                $this->isInstanceOf(APIContentCreateStruct::class),
1192
                $this->equalTo($locationCreateStructs)
1193
            )->will(
1194
                $this->returnCallback(
1195
                    function () use ($that, $contentCreateStruct) {
1196
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1197
1198
                        return true;
1199
                    }
1200
                )
1201
            );
1202
1203
        $domainMapperMock->expects($this->once())
1204
            ->method('getUniqueHash')
1205
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1206
            ->will(
1207
                $this->returnCallback(
1208
                    function ($object) use ($that, $contentCreateStruct) {
1209
                        $that->assertEquals($contentCreateStruct, $object);
1210
1211
                        return 'hash';
1212
                    }
1213
                )
1214
            );
1215
1216
        $fieldTypeMock->expects($this->any())
1217
            ->method('acceptValue')
1218
            ->will(
1219
                $this->returnCallback(
1220
                    function ($valueString) {
1221
                        return new ValueStub($valueString);
1222
                    }
1223
                )
1224
            );
1225
1226
        $fieldTypeMock->expects($this->any())
1227
            ->method('toPersistenceValue')
1228
            ->will(
1229
                $this->returnCallback(
1230
                    function (ValueStub $value) {
1231
                        return (string)$value;
1232
                    }
1233
                )
1234
            );
1235
1236
        $emptyValue = self::EMPTY_FIELD_VALUE;
1237
        $fieldTypeMock->expects($this->any())
1238
            ->method('isEmptyValue')
1239
            ->will(
1240
                $this->returnCallback(
1241
                    function (ValueStub $value) use ($emptyValue) {
1242
                        return $emptyValue === (string)$value;
1243
                    }
1244
                )
1245
            );
1246
1247
        $fieldTypeMock->expects($this->any())
1248
            ->method('validate')
1249
            ->will($this->returnValue([]));
1250
1251
        $this->getFieldTypeRegistryMock()->expects($this->any())
1252
            ->method('getFieldType')
1253
            ->will($this->returnValue($fieldTypeMock));
1254
1255
        $relationProcessorMock
1256
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
1257
            ->method('appendFieldRelations')
1258
            ->with(
1259
                $this->isType('array'),
1260
                $this->isType('array'),
1261
                $this->isInstanceOf(SPIFieldType::class),
1262
                $this->isInstanceOf(Value::class),
1263
                $this->anything()
1264
            );
1265
1266
        $values = $this->determineValuesForCreate(
1267
            $mainLanguageCode,
1268
            $structFields,
1269
            $fieldDefinitions,
1270
            $languageCodes
1271
        );
1272
        $nameSchemaServiceMock->expects($this->once())
1273
            ->method('resolve')
1274
            ->with(
1275
                $this->equalTo($contentType->nameSchema),
1276
                $this->equalTo($contentType),
1277
                $this->equalTo($values),
1278
                $this->equalTo($languageCodes)
1279
            )->will($this->returnValue([]));
1280
1281
        $relationProcessorMock->expects($this->any())
1282
            ->method('processFieldRelations')
1283
            ->with(
1284
                $this->isType('array'),
1285
                $this->equalTo(42),
1286
                $this->isType('int'),
1287
                $this->equalTo($contentType),
1288
                $this->equalTo([])
1289
            );
1290
1291
        if (!$withObjectStates) {
1292
            $objectStateHandlerMock->expects($this->once())
1293
                ->method('loadAllGroups')
1294
                ->will($this->returnValue([]));
1295
        }
1296
1297
        if ($execute) {
1298
            $spiContentCreateStruct = new SPIContentCreateStruct(
1299
                [
1300
                    'name' => [],
1301
                    'typeId' => 123,
1302
                    'sectionId' => 1,
1303
                    'ownerId' => 169,
1304
                    'remoteId' => 'hash',
1305
                    'fields' => $spiFields,
1306
                    'modified' => time(),
1307
                    'initialLanguageId' => 4242,
1308
                ]
1309
            );
1310
            $spiContentCreateStruct2 = clone $spiContentCreateStruct;
1311
            ++$spiContentCreateStruct2->modified;
1312
1313
            $spiContent = new SPIContent(
1314
                [
1315
                    'versionInfo' => new SPIContent\VersionInfo(
1316
                        [
1317
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
1318
                            'versionNo' => 7,
1319
                        ]
1320
                    ),
1321
                ]
1322
            );
1323
1324
            $contentHandlerMock->expects($this->once())
1325
                ->method('create')
1326
                ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
1327
                ->will($this->returnValue($spiContent));
1328
1329
            $repositoryMock->expects($this->once())->method('commit');
1330
            $domainMapperMock->expects($this->once())
1331
                ->method('buildContentDomainObject')
1332
                ->with(
1333
                    $this->isInstanceOf(SPIContent::class),
1334
                    $this->equalTo($contentType)
1335
                );
1336
1337
            $mockedService->createContent($contentCreateStruct, []);
1338
        }
1339
1340
        return $contentCreateStruct;
1341
    }
1342
1343
    public function providerForTestCreateContentNonRedundantFieldSet1()
1344
    {
1345
        $spiFields = [
1346
            new SPIField(
1347
                [
1348
                    'fieldDefinitionId' => 'fieldDefinitionId',
1349
                    'type' => 'fieldTypeIdentifier',
1350
                    'value' => 'newValue',
1351
                    'languageCode' => 'eng-US',
1352
                ]
1353
            ),
1354
        ];
1355
1356
        return [
1357
            // 0. Without language set
1358
            [
1359
                'eng-US',
1360
                [
1361
                    new Field(
1362
                        [
1363
                            'fieldDefIdentifier' => 'identifier',
1364
                            'value' => 'newValue',
1365
                            'languageCode' => 'eng-US',
1366
                        ]
1367
                    ),
1368
                ],
1369
                $spiFields,
1370
            ],
1371
            // 1. Without language set
1372
            [
1373
                'eng-US',
1374
                [
1375
                    new Field(
1376
                        [
1377
                            'fieldDefIdentifier' => 'identifier',
1378
                            'value' => 'newValue',
1379
                            'languageCode' => null,
1380
                        ]
1381
                    ),
1382
                ],
1383
                $spiFields,
1384
            ],
1385
        ];
1386
    }
1387
1388
    /**
1389
     * Test for the createContent() method.
1390
     *
1391
     * Testing the simplest use case.
1392
     *
1393
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1394
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1395
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1396
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1397
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1398
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet1
1399
     */
1400
    public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields)
1401
    {
1402
        $fieldDefinitions = [
1403
            new FieldDefinition(
1404
                [
1405
                    'id' => 'fieldDefinitionId',
1406
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1407
                    'isTranslatable' => false,
1408
                    'identifier' => 'identifier',
1409
                    'isRequired' => false,
1410
                    'defaultValue' => 'defaultValue',
1411
                ]
1412
            ),
1413
        ];
1414
1415
        $this->assertForTestCreateContentNonRedundantFieldSet(
1416
            $mainLanguageCode,
1417
            $structFields,
1418
            $spiFields,
1419
            $fieldDefinitions
1420
        );
1421
    }
1422
1423
    public function providerForTestCreateContentNonRedundantFieldSet2()
1424
    {
1425
        $spiFields = [
1426
            new SPIField(
1427
                [
1428
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1429
                    'type' => 'fieldTypeIdentifier',
1430
                    'value' => 'newValue1',
1431
                    'languageCode' => 'eng-US',
1432
                ]
1433
            ),
1434
            new SPIField(
1435
                [
1436
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1437
                    'type' => 'fieldTypeIdentifier',
1438
                    'value' => 'newValue2',
1439
                    'languageCode' => 'ger-DE',
1440
                ]
1441
            ),
1442
        ];
1443
1444
        return [
1445
            // 0. With language set
1446
            [
1447
                'eng-US',
1448
                [
1449
                    new Field(
1450
                        [
1451
                            'fieldDefIdentifier' => 'identifier1',
1452
                            'value' => 'newValue1',
1453
                            'languageCode' => 'eng-US',
1454
                        ]
1455
                    ),
1456
                    new Field(
1457
                        [
1458
                            'fieldDefIdentifier' => 'identifier2',
1459
                            'value' => 'newValue2',
1460
                            'languageCode' => 'ger-DE',
1461
                        ]
1462
                    ),
1463
                ],
1464
                $spiFields,
1465
            ],
1466
            // 1. Without language set
1467
            [
1468
                'eng-US',
1469
                [
1470
                    new Field(
1471
                        [
1472
                            'fieldDefIdentifier' => 'identifier1',
1473
                            'value' => 'newValue1',
1474
                            'languageCode' => null,
1475
                        ]
1476
                    ),
1477
                    new Field(
1478
                        [
1479
                            'fieldDefIdentifier' => 'identifier2',
1480
                            'value' => 'newValue2',
1481
                            'languageCode' => 'ger-DE',
1482
                        ]
1483
                    ),
1484
                ],
1485
                $spiFields,
1486
            ],
1487
        ];
1488
    }
1489
1490
    /**
1491
     * Test for the createContent() method.
1492
     *
1493
     * Testing multiple languages with multiple translatable fields with empty default value.
1494
     *
1495
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1496
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1497
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1498
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1499
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1500
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet2
1501
     */
1502
    public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields)
1503
    {
1504
        $fieldDefinitions = [
1505
            new FieldDefinition(
1506
                [
1507
                    'id' => 'fieldDefinitionId1',
1508
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1509
                    'isTranslatable' => true,
1510
                    'identifier' => 'identifier1',
1511
                    'isRequired' => false,
1512
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1513
                ]
1514
            ),
1515
            new FieldDefinition(
1516
                [
1517
                    'id' => 'fieldDefinitionId2',
1518
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1519
                    'isTranslatable' => true,
1520
                    'identifier' => 'identifier2',
1521
                    'isRequired' => false,
1522
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1523
                ]
1524
            ),
1525
        ];
1526
1527
        $this->assertForTestCreateContentNonRedundantFieldSet(
1528
            $mainLanguageCode,
1529
            $structFields,
1530
            $spiFields,
1531
            $fieldDefinitions
1532
        );
1533
    }
1534
1535
    public function providerForTestCreateContentNonRedundantFieldSetComplex()
1536
    {
1537
        $spiFields0 = [
1538
            new SPIField(
1539
                [
1540
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1541
                    'type' => 'fieldTypeIdentifier',
1542
                    'value' => 'defaultValue2',
1543
                    'languageCode' => 'eng-US',
1544
                ]
1545
            ),
1546
            new SPIField(
1547
                [
1548
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1549
                    'type' => 'fieldTypeIdentifier',
1550
                    'value' => 'defaultValue4',
1551
                    'languageCode' => 'eng-US',
1552
                ]
1553
            ),
1554
        ];
1555
        $spiFields1 = [
1556
            new SPIField(
1557
                [
1558
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1559
                    'type' => 'fieldTypeIdentifier',
1560
                    'value' => 'newValue1',
1561
                    'languageCode' => 'ger-DE',
1562
                ]
1563
            ),
1564
            new SPIField(
1565
                [
1566
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1567
                    'type' => 'fieldTypeIdentifier',
1568
                    'value' => 'defaultValue2',
1569
                    'languageCode' => 'ger-DE',
1570
                ]
1571
            ),
1572
            new SPIField(
1573
                [
1574
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1575
                    'type' => 'fieldTypeIdentifier',
1576
                    'value' => 'newValue2',
1577
                    'languageCode' => 'eng-US',
1578
                ]
1579
            ),
1580
            new SPIField(
1581
                [
1582
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1583
                    'type' => 'fieldTypeIdentifier',
1584
                    'value' => 'newValue4',
1585
                    'languageCode' => 'eng-US',
1586
                ]
1587
            ),
1588
        ];
1589
1590
        return [
1591
            // 0. Creating by default values only
1592
            [
1593
                'eng-US',
1594
                [],
1595
                $spiFields0,
1596
            ],
1597
            // 1. Multiple languages with language set
1598
            [
1599
                'eng-US',
1600
                [
1601
                    new Field(
1602
                        [
1603
                            'fieldDefIdentifier' => 'identifier1',
1604
                            'value' => 'newValue1',
1605
                            'languageCode' => 'ger-DE',
1606
                        ]
1607
                    ),
1608
                    new Field(
1609
                        [
1610
                            'fieldDefIdentifier' => 'identifier2',
1611
                            'value' => 'newValue2',
1612
                            'languageCode' => 'eng-US',
1613
                        ]
1614
                    ),
1615
                    new Field(
1616
                        [
1617
                            'fieldDefIdentifier' => 'identifier4',
1618
                            'value' => 'newValue4',
1619
                            'languageCode' => 'eng-US',
1620
                        ]
1621
                    ),
1622
                ],
1623
                $spiFields1,
1624
            ],
1625
            // 2. Multiple languages without language set
1626
            [
1627
                'eng-US',
1628
                [
1629
                    new Field(
1630
                        [
1631
                            'fieldDefIdentifier' => 'identifier1',
1632
                            'value' => 'newValue1',
1633
                            'languageCode' => 'ger-DE',
1634
                        ]
1635
                    ),
1636
                    new Field(
1637
                        [
1638
                            'fieldDefIdentifier' => 'identifier2',
1639
                            'value' => 'newValue2',
1640
                            'languageCode' => null,
1641
                        ]
1642
                    ),
1643
                    new Field(
1644
                        [
1645
                            'fieldDefIdentifier' => 'identifier4',
1646
                            'value' => 'newValue4',
1647
                            'languageCode' => null,
1648
                        ]
1649
                    ),
1650
                ],
1651
                $spiFields1,
1652
            ],
1653
        ];
1654
    }
1655
1656
    protected function fixturesForTestCreateContentNonRedundantFieldSetComplex()
1657
    {
1658
        return [
1659
            new FieldDefinition(
1660
                [
1661
                    'id' => 'fieldDefinitionId1',
1662
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1663
                    'isTranslatable' => true,
1664
                    'identifier' => 'identifier1',
1665
                    'isRequired' => false,
1666
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1667
                ]
1668
            ),
1669
            new FieldDefinition(
1670
                [
1671
                    'id' => 'fieldDefinitionId2',
1672
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1673
                    'isTranslatable' => true,
1674
                    'identifier' => 'identifier2',
1675
                    'isRequired' => false,
1676
                    'defaultValue' => 'defaultValue2',
1677
                ]
1678
            ),
1679
            new FieldDefinition(
1680
                [
1681
                    'id' => 'fieldDefinitionId3',
1682
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1683
                    'isTranslatable' => false,
1684
                    'identifier' => 'identifier3',
1685
                    'isRequired' => false,
1686
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1687
                ]
1688
            ),
1689
            new FieldDefinition(
1690
                [
1691
                    'id' => 'fieldDefinitionId4',
1692
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1693
                    'isTranslatable' => false,
1694
                    'identifier' => 'identifier4',
1695
                    'isRequired' => false,
1696
                    'defaultValue' => 'defaultValue4',
1697
                ]
1698
            ),
1699
        ];
1700
    }
1701
1702
    /**
1703
     * Test for the createContent() method.
1704
     *
1705
     * Testing multiple languages with multiple translatable fields with empty default value.
1706
     *
1707
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1708
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1709
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1710
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1711
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1712
     * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex
1713
     */
1714
    public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields)
1715
    {
1716
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
1717
1718
        $this->assertForTestCreateContentNonRedundantFieldSet(
1719
            $mainLanguageCode,
1720
            $structFields,
1721
            $spiFields,
1722
            $fieldDefinitions
1723
        );
1724
    }
1725
1726 View Code Duplication
    public function providerForTestCreateContentWithInvalidLanguage()
1727
    {
1728
        return [
1729
            [
1730
                'eng-GB',
1731
                [
1732
                    new Field(
1733
                        [
1734
                            'fieldDefIdentifier' => 'identifier',
1735
                            'value' => 'newValue',
1736
                            'languageCode' => 'Klingon',
1737
                        ]
1738
                    ),
1739
                ],
1740
            ],
1741
            [
1742
                'Klingon',
1743
                [
1744
                    new Field(
1745
                        [
1746
                            'fieldDefIdentifier' => 'identifier',
1747
                            'value' => 'newValue',
1748
                            'languageCode' => 'eng-GB',
1749
                        ]
1750
                    ),
1751
                ],
1752
            ],
1753
        ];
1754
    }
1755
1756
    /**
1757
     * Test for the updateContent() method.
1758
     *
1759
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1760
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1761
     * @dataProvider providerForTestCreateContentWithInvalidLanguage
1762
     */
1763
    public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields)
1764
    {
1765
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
1766
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
1767
1768
        $repositoryMock = $this->getRepositoryMock();
1769
        $mockedService = $this->getPartlyMockedContentService();
1770
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1771
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1772
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1773
        $domainMapperMock = $this->getDomainMapperMock();
1774
        $permissionResolver = $this->getPermissionResolverMock();
1775
1776
        $contentType = new ContentType(
1777
            [
1778
                'id' => 123,
1779
                'fieldDefinitions' => [],
1780
            ]
1781
        );
1782
        $contentCreateStruct = new ContentCreateStruct(
1783
            [
1784
                'fields' => $structFields,
1785
                'mainLanguageCode' => $mainLanguageCode,
1786
                'contentType' => $contentType,
1787
                'alwaysAvailable' => false,
1788
                'ownerId' => 169,
1789
                'sectionId' => 1,
1790
            ]
1791
        );
1792
1793
        $languageHandlerMock->expects($this->any())
1794
            ->method('loadByLanguageCode')
1795
            ->with($this->isType('string'))
1796
            ->will(
1797
                $this->returnCallback(
1798 View Code Duplication
                    function ($languageCode) {
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...
1799
                        if ($languageCode === 'Klingon') {
1800
                            throw new NotFoundException('Language', 'Klingon');
1801
                        }
1802
1803
                        return new Language(['id' => 4242]);
1804
                    }
1805
                )
1806
            );
1807
1808
        $contentTypeServiceMock->expects($this->once())
1809
            ->method('loadContentType')
1810
            ->with($this->equalTo($contentType->id))
1811
            ->will($this->returnValue($contentType));
1812
1813
        $repositoryMock->expects($this->once())
1814
            ->method('getContentTypeService')
1815
            ->will($this->returnValue($contentTypeServiceMock));
1816
1817
        $that = $this;
1818
        $permissionResolver->expects($this->once())
1819
            ->method('canUser')
1820
            ->with(
1821
                $this->equalTo('content'),
1822
                $this->equalTo('create'),
1823
                $this->isInstanceOf(APIContentCreateStruct::class),
1824
                $this->equalTo([])
1825
            )->will(
1826
                $this->returnCallback(
1827
                    function () use ($that, $contentCreateStruct) {
1828
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1829
1830
                        return true;
1831
                    }
1832
                )
1833
            );
1834
1835
        $domainMapperMock->expects($this->once())
1836
            ->method('getUniqueHash')
1837
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1838
            ->will(
1839
                $this->returnCallback(
1840
                    function ($object) use ($that, $contentCreateStruct) {
1841
                        $that->assertEquals($contentCreateStruct, $object);
1842
1843
                        return 'hash';
1844
                    }
1845
                )
1846
            );
1847
1848
        $mockedService->createContent($contentCreateStruct, []);
1849
    }
1850
1851
    protected function assertForCreateContentContentValidationException(
1852
        $mainLanguageCode,
1853
        $structFields,
1854
        $fieldDefinitions = []
1855
    ) {
1856
        $repositoryMock = $this->getRepositoryMock();
1857
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
1858
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1859
        $permissionResolver = $this->getPermissionResolverMock();
1860
1861
        $contentType = new ContentType(
1862
            [
1863
                'id' => 123,
1864
                'fieldDefinitions' => $fieldDefinitions,
1865
            ]
1866
        );
1867
        $contentCreateStruct = new ContentCreateStruct(
1868
            [
1869
                'ownerId' => 169,
1870
                'alwaysAvailable' => false,
1871
                'remoteId' => 'faraday',
1872
                'mainLanguageCode' => $mainLanguageCode,
1873
                'fields' => $structFields,
1874
                'contentType' => $contentType,
1875
            ]
1876
        );
1877
1878
        $contentTypeServiceMock->expects($this->once())
1879
            ->method('loadContentType')
1880
            ->with($this->equalTo(123))
1881
            ->will($this->returnValue($contentType));
1882
1883
        $repositoryMock->expects($this->once())
1884
            ->method('getContentTypeService')
1885
            ->will($this->returnValue($contentTypeServiceMock));
1886
1887
        $permissionResolver->expects($this->once())
1888
            ->method('canUser')
1889
            ->with(
1890
                $this->equalTo('content'),
1891
                $this->equalTo('create'),
1892
                $this->isInstanceOf(get_class($contentCreateStruct)),
1893
                $this->equalTo([])
1894
            )->will($this->returnValue(true));
1895
1896
        $mockedService->expects($this->once())
1897
            ->method('loadContentByRemoteId')
1898
            ->with($contentCreateStruct->remoteId)
1899
            ->will(
1900
                $this->throwException(new NotFoundException('Content', 'faraday'))
1901
            );
1902
1903
        $mockedService->createContent($contentCreateStruct, []);
1904
    }
1905
1906 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition()
1907
    {
1908
        return [
1909
            [
1910
                'eng-GB',
1911
                [
1912
                    new Field(
1913
                        [
1914
                            'fieldDefIdentifier' => 'identifier',
1915
                            'value' => 'newValue',
1916
                            'languageCode' => 'eng-GB',
1917
                        ]
1918
                    ),
1919
                ],
1920
            ],
1921
        ];
1922
    }
1923
1924
    /**
1925
     * Test for the createContent() method.
1926
     *
1927
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1928
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1929
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1930
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition
1931
     */
1932
    public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields)
1933
    {
1934
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
1935
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
1936
1937
        $this->assertForCreateContentContentValidationException(
1938
            $mainLanguageCode,
1939
            $structFields,
1940
            []
1941
        );
1942
    }
1943
1944 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionTranslation()
1945
    {
1946
        return [
1947
            [
1948
                'eng-GB',
1949
                [
1950
                    new Field(
1951
                        [
1952
                            'fieldDefIdentifier' => 'identifier',
1953
                            'value' => 'newValue',
1954
                            'languageCode' => 'eng-US',
1955
                        ]
1956
                    ),
1957
                ],
1958
            ],
1959
        ];
1960
    }
1961
1962
    /**
1963
     * Test for the createContent() method.
1964
     *
1965
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1966
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1967
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1968
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
1969
     */
1970 View Code Duplication
    public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields)
1971
    {
1972
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
1973
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
1974
1975
        $fieldDefinitions = [
1976
            new FieldDefinition(
1977
                [
1978
                    'id' => 'fieldDefinitionId1',
1979
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1980
                    'isTranslatable' => false,
1981
                    'identifier' => 'identifier',
1982
                    'isRequired' => false,
1983
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1984
                ]
1985
            ),
1986
        ];
1987
1988
        $this->assertForCreateContentContentValidationException(
1989
            $mainLanguageCode,
1990
            $structFields,
1991
            $fieldDefinitions
1992
        );
1993
    }
1994
1995
    /**
1996
     * Asserts behaviour necessary for testing ContentFieldValidationException because of required
1997
     * field being empty.
1998
     *
1999
     * @param string $mainLanguageCode
2000
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2001
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2002
     *
2003
     * @return mixed
2004
     */
2005
    protected function assertForTestCreateContentRequiredField(
2006
        $mainLanguageCode,
2007
        array $structFields,
2008
        array $fieldDefinitions
2009
    ) {
2010
        $repositoryMock = $this->getRepositoryMock();
2011
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2012
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2013
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2014
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2015
        $domainMapperMock = $this->getDomainMapperMock();
2016
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2017
        $permissionResolver = $this->getPermissionResolverMock();
2018
2019
        $contentType = new ContentType(
2020
            [
2021
                'id' => 123,
2022
                'fieldDefinitions' => $fieldDefinitions,
2023
                'nameSchema' => '<nameSchema>',
2024
            ]
2025
        );
2026
        $contentCreateStruct = new ContentCreateStruct(
2027
            [
2028
                'fields' => $structFields,
2029
                'mainLanguageCode' => $mainLanguageCode,
2030
                'contentType' => $contentType,
2031
                'alwaysAvailable' => false,
2032
                'ownerId' => 169,
2033
                'sectionId' => 1,
2034
            ]
2035
        );
2036
2037
        $languageHandlerMock->expects($this->any())
2038
            ->method('loadByLanguageCode')
2039
            ->with($this->isType('string'))
2040
            ->will(
2041
                $this->returnCallback(
2042
                    function () {
2043
                        return new Language(['id' => 4242]);
2044
                    }
2045
                )
2046
            );
2047
2048
        $contentTypeServiceMock->expects($this->once())
2049
            ->method('loadContentType')
2050
            ->with($this->equalTo($contentType->id))
2051
            ->will($this->returnValue($contentType));
2052
2053
        $repositoryMock->expects($this->once())
2054
            ->method('getContentTypeService')
2055
            ->will($this->returnValue($contentTypeServiceMock));
2056
2057
        $that = $this;
2058
        $permissionResolver->expects($this->once())
2059
            ->method('canUser')
2060
            ->with(
2061
                $this->equalTo('content'),
2062
                $this->equalTo('create'),
2063
                $this->isInstanceOf(APIContentCreateStruct::class),
2064
                $this->equalTo([])
2065
            )->will(
2066
                $this->returnCallback(
2067
                    function () use ($that, $contentCreateStruct) {
2068
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2069
2070
                        return true;
2071
                    }
2072
                )
2073
            );
2074
2075
        $domainMapperMock->expects($this->once())
2076
            ->method('getUniqueHash')
2077
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2078
            ->will(
2079
                $this->returnCallback(
2080
                    function ($object) use ($that, $contentCreateStruct) {
2081
                        $that->assertEquals($contentCreateStruct, $object);
2082
2083
                        return 'hash';
2084
                    }
2085
                )
2086
            );
2087
2088
        $fieldTypeMock->expects($this->any())
2089
            ->method('acceptValue')
2090
            ->will(
2091
                $this->returnCallback(
2092
                    function ($valueString) {
2093
                        return new ValueStub($valueString);
2094
                    }
2095
                )
2096
            );
2097
2098
        $emptyValue = self::EMPTY_FIELD_VALUE;
2099
        $fieldTypeMock->expects($this->any())
2100
            ->method('isEmptyValue')
2101
            ->will(
2102
                $this->returnCallback(
2103
                    function (ValueStub $value) use ($emptyValue) {
2104
                        return $emptyValue === (string)$value;
2105
                    }
2106
                )
2107
            );
2108
2109
        $fieldTypeMock->expects($this->any())
2110
            ->method('validate')
2111
            ->will($this->returnValue([]));
2112
2113
        $this->getFieldTypeRegistryMock()->expects($this->any())
2114
            ->method('getFieldType')
2115
            ->will($this->returnValue($fieldTypeMock));
2116
2117
        return $contentCreateStruct;
2118
    }
2119
2120 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField()
2121
    {
2122
        return [
2123
            [
2124
                'eng-US',
2125
                [
2126
                    new Field(
2127
                        [
2128
                            'fieldDefIdentifier' => 'identifier',
2129
                            'value' => self::EMPTY_FIELD_VALUE,
2130
                            'languageCode' => null,
2131
                        ]
2132
                    ),
2133
                ],
2134
                'identifier',
2135
                'eng-US',
2136
            ],
2137
        ];
2138
    }
2139
2140
    /**
2141
     * Test for the createContent() method.
2142
     *
2143
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2144
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2145
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2146
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField
2147
     */
2148
    public function testCreateContentRequiredField(
2149
        $mainLanguageCode,
2150
        $structFields,
2151
        $identifier,
2152
        $languageCode
2153
    ) {
2154
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2155
2156
        $fieldDefinitions = [
2157
            new FieldDefinition(
2158
                [
2159
                    'id' => 'fieldDefinitionId',
2160
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2161
                    'isTranslatable' => true,
2162
                    'identifier' => 'identifier',
2163
                    'isRequired' => true,
2164
                    'defaultValue' => 'defaultValue',
2165
                ]
2166
            ),
2167
        ];
2168
        $contentCreateStruct = $this->assertForTestCreateContentRequiredField(
2169
            $mainLanguageCode,
2170
            $structFields,
2171
            $fieldDefinitions
2172
        );
2173
2174
        $mockedService = $this->getPartlyMockedContentService();
2175
2176
        try {
2177
            $mockedService->createContent($contentCreateStruct, []);
2178
        } catch (ContentValidationException $e) {
2179
            $this->assertEquals(
2180
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
2181
                $e->getMessage()
2182
            );
2183
2184
            throw $e;
2185
        }
2186
    }
2187
2188
    /**
2189
     * Asserts behaviour necessary for testing ContentFieldValidationException because of
2190
     * field not being valid.
2191
     *
2192
     * @param string $mainLanguageCode
2193
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2194
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2195
     *
2196
     * @return mixed
2197
     */
2198
    protected function assertForTestCreateContentThrowsContentFieldValidationException(
2199
        $mainLanguageCode,
2200
        array $structFields,
2201
        array $fieldDefinitions
2202
    ) {
2203
        $repositoryMock = $this->getRepositoryMock();
2204
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2205
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2206
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2207
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2208
        $domainMapperMock = $this->getDomainMapperMock();
2209
        $relationProcessorMock = $this->getRelationProcessorMock();
2210
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2211
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
2212
        $permissionResolver = $this->getPermissionResolverMock();
2213
2214
        $contentType = new ContentType(
2215
            [
2216
                'id' => 123,
2217
                'fieldDefinitions' => $fieldDefinitions,
2218
                'nameSchema' => '<nameSchema>',
2219
            ]
2220
        );
2221
        $contentCreateStruct = new ContentCreateStruct(
2222
            [
2223
                'fields' => $structFields,
2224
                'mainLanguageCode' => $mainLanguageCode,
2225
                'contentType' => $contentType,
2226
                'alwaysAvailable' => false,
2227
                'ownerId' => 169,
2228
                'sectionId' => 1,
2229
            ]
2230
        );
2231
2232
        $languageHandlerMock->expects($this->any())
2233
            ->method('loadByLanguageCode')
2234
            ->with($this->isType('string'))
2235
            ->will(
2236
                $this->returnCallback(
2237
                    function () {
2238
                        return new Language(['id' => 4242]);
2239
                    }
2240
                )
2241
            );
2242
2243
        $contentTypeServiceMock->expects($this->once())
2244
            ->method('loadContentType')
2245
            ->with($this->equalTo($contentType->id))
2246
            ->will($this->returnValue($contentType));
2247
2248
        $repositoryMock->expects($this->once())
2249
            ->method('getContentTypeService')
2250
            ->will($this->returnValue($contentTypeServiceMock));
2251
2252
        $that = $this;
2253
        $permissionResolver->expects($this->once())
2254
            ->method('canUser')
2255
            ->with(
2256
                $this->equalTo('content'),
2257
                $this->equalTo('create'),
2258
                $this->isInstanceOf(APIContentCreateStruct::class),
2259
                $this->equalTo([])
2260
            )->will(
2261
                $this->returnCallback(
2262
                    function () use ($that, $contentCreateStruct) {
2263
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2264
2265
                        return true;
2266
                    }
2267
                )
2268
            );
2269
2270
        $domainMapperMock->expects($this->once())
2271
            ->method('getUniqueHash')
2272
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2273
            ->will(
2274
                $this->returnCallback(
2275
                    function ($object) use ($that, $contentCreateStruct) {
2276
                        $that->assertEquals($contentCreateStruct, $object);
2277
2278
                        return 'hash';
2279
                    }
2280
                )
2281
            );
2282
2283
        $this->getFieldTypeRegistryMock()->expects($this->any())
2284
            ->method('getFieldType')
2285
            ->will($this->returnValue($fieldTypeMock));
2286
2287
        $relationProcessorMock
2288
            ->expects($this->any())
2289
            ->method('appendFieldRelations')
2290
            ->with(
2291
                $this->isType('array'),
2292
                $this->isType('array'),
2293
                $this->isInstanceOf(SPIFieldType::class),
2294
                $this->isInstanceOf(Value::class),
2295
                $this->anything()
2296
            );
2297
2298
        $fieldValues = $this->determineValuesForCreate(
2299
            $mainLanguageCode,
2300
            $structFields,
2301
            $fieldDefinitions,
2302
            $languageCodes
2303
        );
2304
        $allFieldErrors = [];
2305
        $validateCount = 0;
2306
        $emptyValue = self::EMPTY_FIELD_VALUE;
2307
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
2308
            foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) {
2309
                $fieldTypeMock->expects($this->at($validateCount++))
2310
                    ->method('acceptValue')
2311
                    ->will(
2312
                        $this->returnCallback(
2313
                            function ($valueString) {
2314
                                return new ValueStub($valueString);
2315
                            }
2316
                        )
2317
                    );
2318
2319
                $fieldTypeMock->expects($this->at($validateCount++))
2320
                    ->method('isEmptyValue')
2321
                    ->will(
2322
                        $this->returnCallback(
2323
                            function (ValueStub $value) use ($emptyValue) {
2324
                                return $emptyValue === (string)$value;
2325
                            }
2326
                        )
2327
                    );
2328
2329
                if (self::EMPTY_FIELD_VALUE === (string)$value) {
2330
                    continue;
2331
                }
2332
2333
                $fieldTypeMock->expects($this->at($validateCount++))
2334
                    ->method('validate')
2335
                    ->with(
2336
                        $this->equalTo($fieldDefinition),
2337
                        $this->equalTo($value)
2338
                    )->will($this->returnArgument(1));
2339
2340
                $allFieldErrors[$fieldDefinition->id][$languageCode] = $value;
2341
            }
2342
        }
2343
2344
        return [$contentCreateStruct, $allFieldErrors];
2345
    }
2346
2347
    public function providerForTestCreateContentThrowsContentFieldValidationException()
2348
    {
2349
        return $this->providerForTestCreateContentNonRedundantFieldSetComplex();
2350
    }
2351
2352
    /**
2353
     * Test for the createContent() method.
2354
     *
2355
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2356
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2357
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2358
     * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException
2359
     */
2360 View Code Duplication
    public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields)
2361
    {
2362
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2363
        $this->expectExceptionMessage('Content fields did not validate');
2364
2365
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
2366
        list($contentCreateStruct, $allFieldErrors) =
2367
            $this->assertForTestCreateContentThrowsContentFieldValidationException(
2368
                $mainLanguageCode,
2369
                $structFields,
2370
                $fieldDefinitions
2371
            );
2372
2373
        $mockedService = $this->getPartlyMockedContentService();
2374
2375
        try {
2376
            $mockedService->createContent($contentCreateStruct);
2377
        } catch (ContentFieldValidationException $e) {
2378
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
2379
            throw $e;
2380
        }
2381
    }
2382
2383
    /**
2384
     * Test for the createContent() method.
2385
     *
2386
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2387
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2388
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2389
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2390
     */
2391
    public function testCreateContentWithLocations()
2392
    {
2393
        $spiFields = [
2394
            new SPIField(
2395
                [
2396
                    'fieldDefinitionId' => 'fieldDefinitionId',
2397
                    'type' => 'fieldTypeIdentifier',
2398
                    'value' => 'defaultValue',
2399
                    'languageCode' => 'eng-US',
2400
                ]
2401
            ),
2402
        ];
2403
        $fieldDefinitions = [
2404
            new FieldDefinition(
2405
                [
2406
                    'id' => 'fieldDefinitionId',
2407
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2408
                    'isTranslatable' => false,
2409
                    'identifier' => 'identifier',
2410
                    'isRequired' => false,
2411
                    'defaultValue' => 'defaultValue',
2412
                ]
2413
            ),
2414
        ];
2415
2416
        // Set up a simple case that will pass
2417
        $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]);
2418
        $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]);
2419
        $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2];
2420
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2421
            'eng-US',
2422
            [],
2423
            $spiFields,
2424
            $fieldDefinitions,
2425
            $locationCreateStructs,
2426
            false,
2427
            // Do not execute
2428
            false
2429
        );
2430
2431
        $repositoryMock = $this->getRepositoryMock();
2432
        $mockedService = $this->getPartlyMockedContentService();
2433
        $locationServiceMock = $this->getLocationServiceMock();
2434
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2435
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2436
        $domainMapperMock = $this->getDomainMapperMock();
2437
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2438
        $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]);
2439
2440
        $locationServiceMock->expects($this->at(0))
2441
            ->method('loadLocation')
2442
            ->with($this->equalTo(321))
2443
            ->will($this->returnValue($parentLocation));
2444
2445
        $locationServiceMock->expects($this->at(1))
2446
            ->method('loadLocation')
2447
            ->with($this->equalTo(654))
2448
            ->will($this->returnValue($parentLocation));
2449
2450
        $repositoryMock->expects($this->atLeastOnce())
2451
            ->method('getLocationService')
2452
            ->will($this->returnValue($locationServiceMock));
2453
2454
        $domainMapperMock->expects($this->at(1))
2455
            ->method('buildSPILocationCreateStruct')
2456
            ->with(
2457
                $this->equalTo($locationCreateStruct1),
2458
                $this->equalTo($parentLocation),
2459
                $this->equalTo(true),
2460
                $this->equalTo(null),
2461
                $this->equalTo(null)
2462
            )->will($this->returnValue($spiLocationCreateStruct));
2463
2464
        $domainMapperMock->expects($this->at(2))
2465
            ->method('buildSPILocationCreateStruct')
2466
            ->with(
2467
                $this->equalTo($locationCreateStruct2),
2468
                $this->equalTo($parentLocation),
2469
                $this->equalTo(false),
2470
                $this->equalTo(null),
2471
                $this->equalTo(null)
2472
            )->will($this->returnValue($spiLocationCreateStruct));
2473
2474
        $spiContentCreateStruct = new SPIContentCreateStruct(
2475
            [
2476
                'name' => [],
2477
                'typeId' => 123,
2478
                'sectionId' => 1,
2479
                'ownerId' => 169,
2480
                'remoteId' => 'hash',
2481
                'fields' => $spiFields,
2482
                'modified' => time(),
2483
                'initialLanguageId' => 4242,
2484
                'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct],
2485
            ]
2486
        );
2487
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2488
        ++$spiContentCreateStruct2->modified;
2489
2490
        $spiContent = new SPIContent(
2491
            [
2492
                'versionInfo' => new SPIContent\VersionInfo(
2493
                    [
2494
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2495
                        'versionNo' => 7,
2496
                    ]
2497
                ),
2498
            ]
2499
        );
2500
2501
        $handlerMock->expects($this->once())
2502
            ->method('create')
2503
            ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
2504
            ->will($this->returnValue($spiContent));
2505
2506
        $domainMapperMock->expects($this->once())
2507
            ->method('buildContentDomainObject')
2508
            ->with(
2509
                $this->isInstanceOf(SPIContent::class),
2510
                $this->isInstanceOf(APIContentType::class)
2511
            );
2512
2513
        $repositoryMock->expects($this->once())->method('commit');
2514
2515
        // Execute
2516
        $mockedService->createContent($contentCreateStruct, $locationCreateStructs);
2517
    }
2518
2519
    /**
2520
     * Test for the createContent() method.
2521
     *
2522
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2523
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2524
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2525
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2526
     */
2527
    public function testCreateContentWithLocationsDuplicateUnderParent()
2528
    {
2529
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
2530
        $this->expectExceptionMessage('Multiple LocationCreateStructs with the same parent Location \'321\' are given');
2531
2532
        $fieldDefinitions = [
2533
            new FieldDefinition(
2534
                [
2535
                    'id' => 'fieldDefinitionId',
2536
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2537
                    'isTranslatable' => false,
2538
                    'identifier' => 'identifier',
2539
                    'isRequired' => false,
2540
                    'defaultValue' => 'defaultValue',
2541
                ]
2542
            ),
2543
        ];
2544
2545
        $repositoryMock = $this->getRepositoryMock();
2546
        $mockedService = $this->getPartlyMockedContentService();
2547
        $locationServiceMock = $this->getLocationServiceMock();
2548
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2549
        $domainMapperMock = $this->getDomainMapperMock();
2550
        $permissionResolver = $this->getPermissionResolverMock();
2551
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2552
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2553
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2554
        $parentLocation = new Location(['id' => 321]);
2555
        $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]);
2556
        $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct];
2557
        $contentType = new ContentType(
2558
            [
2559
                'id' => 123,
2560
                'fieldDefinitions' => $fieldDefinitions,
2561
                'nameSchema' => '<nameSchema>',
2562
            ]
2563
        );
2564
        $contentCreateStruct = new ContentCreateStruct(
2565
            [
2566
                'fields' => [],
2567
                'mainLanguageCode' => 'eng-US',
2568
                'contentType' => $contentType,
2569
                'alwaysAvailable' => false,
2570
                'ownerId' => 169,
2571
                'sectionId' => 1,
2572
            ]
2573
        );
2574
2575
        $languageHandlerMock->expects($this->any())
2576
            ->method('loadByLanguageCode')
2577
            ->with($this->isType('string'))
2578
            ->will(
2579
                $this->returnCallback(
2580
                    function () {
2581
                        return new Language(['id' => 4242]);
2582
                    }
2583
                )
2584
            );
2585
2586
        $contentTypeServiceMock->expects($this->once())
2587
            ->method('loadContentType')
2588
            ->with($this->equalTo($contentType->id))
2589
            ->will($this->returnValue($contentType));
2590
2591
        $repositoryMock->expects($this->once())
2592
            ->method('getContentTypeService')
2593
            ->will($this->returnValue($contentTypeServiceMock));
2594
2595
        $that = $this;
2596
        $permissionResolver->expects($this->once())
2597
            ->method('canUser')
2598
            ->with(
2599
                $this->equalTo('content'),
2600
                $this->equalTo('create'),
2601
                $this->isInstanceOf(APIContentCreateStruct::class),
2602
                $this->equalTo($locationCreateStructs)
2603
            )->will(
2604
                $this->returnCallback(
2605
                    function () use ($that, $contentCreateStruct) {
2606
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2607
2608
                        return true;
2609
                    }
2610
                )
2611
            );
2612
2613
        $domainMapperMock->expects($this->once())
2614
            ->method('getUniqueHash')
2615
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2616
            ->will(
2617
                $this->returnCallback(
2618
                    function ($object) use ($that, $contentCreateStruct) {
2619
                        $that->assertEquals($contentCreateStruct, $object);
2620
2621
                        return 'hash';
2622
                    }
2623
                )
2624
            );
2625
2626
        $locationServiceMock->expects($this->once())
2627
            ->method('loadLocation')
2628
            ->with($this->equalTo(321))
2629
            ->will($this->returnValue($parentLocation));
2630
2631
        $repositoryMock->expects($this->any())
2632
            ->method('getLocationService')
2633
            ->will($this->returnValue($locationServiceMock));
2634
2635
        $domainMapperMock->expects($this->any())
2636
            ->method('buildSPILocationCreateStruct')
2637
            ->with(
2638
                $this->equalTo($locationCreateStruct),
2639
                $this->equalTo($parentLocation),
2640
                $this->equalTo(true),
2641
                $this->equalTo(null),
2642
                $this->equalTo(null)
2643
            )->will($this->returnValue($spiLocationCreateStruct));
2644
2645
        $mockedService->createContent(
2646
            $contentCreateStruct,
2647
            $locationCreateStructs
2648
        );
2649
    }
2650
2651
    /**
2652
     * Test for the createContent() method.
2653
     *
2654
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2655
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2656
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2657
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2658
     */
2659
    public function testCreateContentObjectStates()
2660
    {
2661
        $spiFields = [
2662
            new SPIField(
2663
                [
2664
                    'fieldDefinitionId' => 'fieldDefinitionId',
2665
                    'type' => 'fieldTypeIdentifier',
2666
                    'value' => 'defaultValue',
2667
                    'languageCode' => 'eng-US',
2668
                ]
2669
            ),
2670
        ];
2671
        $fieldDefinitions = [
2672
            new FieldDefinition(
2673
                [
2674
                    'id' => 'fieldDefinitionId',
2675
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2676
                    'isTranslatable' => false,
2677
                    'identifier' => 'identifier',
2678
                    'isRequired' => false,
2679
                    'defaultValue' => 'defaultValue',
2680
                ]
2681
            ),
2682
        ];
2683
        $objectStateGroups = [
0 ignored issues
show
Unused Code introduced by
$objectStateGroups is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2684
            new SPIObjectStateGroup(['id' => 10]),
2685
            new SPIObjectStateGroup(['id' => 20]),
2686
        ];
2687
2688
        // Set up a simple case that will pass
2689
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2690
            'eng-US',
2691
            [],
2692
            $spiFields,
2693
            $fieldDefinitions,
2694
            [],
2695
            true,
2696
            // Do not execute
2697
            false
2698
        );
2699
        $timestamp = time();
2700
        $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}");
2701
2702
        $repositoryMock = $this->getRepositoryMock();
2703
        $mockedService = $this->getPartlyMockedContentService();
2704
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2705
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2706
        $domainMapperMock = $this->getDomainMapperMock();
2707
2708
        $this->mockGetDefaultObjectStates();
2709
        $this->mockSetDefaultObjectStates();
2710
2711
        $spiContentCreateStruct = new SPIContentCreateStruct(
2712
            [
2713
                'name' => [],
2714
                'typeId' => 123,
2715
                'sectionId' => 1,
2716
                'ownerId' => 169,
2717
                'remoteId' => 'hash',
2718
                'fields' => $spiFields,
2719
                'modified' => $timestamp,
2720
                'initialLanguageId' => 4242,
2721
                'locations' => [],
2722
            ]
2723
        );
2724
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2725
        ++$spiContentCreateStruct2->modified;
2726
2727
        $spiContent = new SPIContent(
2728
            [
2729
                'versionInfo' => new SPIContent\VersionInfo(
2730
                    [
2731
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2732
                        'versionNo' => 7,
2733
                    ]
2734
                ),
2735
            ]
2736
        );
2737
2738
        $handlerMock->expects($this->once())
2739
            ->method('create')
2740
            ->with($this->equalTo($spiContentCreateStruct))
2741
            ->will($this->returnValue($spiContent));
2742
2743
        $domainMapperMock->expects($this->once())
2744
            ->method('buildContentDomainObject')
2745
            ->with(
2746
                $this->isInstanceOf(SPIContent::class),
2747
                $this->isInstanceOf(APIContentType::class)
2748
            );
2749
2750
        $repositoryMock->expects($this->once())->method('commit');
2751
2752
        // Execute
2753
        $mockedService->createContent($contentCreateStruct, []);
2754
    }
2755
2756
    /**
2757
     * Test for the createContent() method.
2758
     *
2759
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2760
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2761
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2762
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2763
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2764
     */
2765
    public function testCreateContentWithRollback()
2766
    {
2767
        $this->expectException(\Exception::class);
2768
        $this->expectExceptionMessage('Store failed');
2769
2770
        $fieldDefinitions = [
2771
            new FieldDefinition(
2772
                [
2773
                    'id' => 'fieldDefinitionId',
2774
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2775
                    'isTranslatable' => false,
2776
                    'identifier' => 'identifier',
2777
                    'isRequired' => false,
2778
                    'defaultValue' => 'defaultValue',
2779
                ]
2780
            ),
2781
        ];
2782
2783
        // Setup a simple case that will pass
2784
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2785
            'eng-US',
2786
            [],
2787
            [],
2788
            $fieldDefinitions,
2789
            [],
2790
            false,
2791
            // Do not execute test
2792
            false
2793
        );
2794
2795
        $repositoryMock = $this->getRepositoryMock();
2796
        $repositoryMock->expects($this->never())->method('commit');
2797
        $repositoryMock->expects($this->once())->method('rollback');
2798
2799
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
2800
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
2801
        $contentHandlerMock->expects($this->once())
2802
            ->method('create')
2803
            ->with($this->anything())
2804
            ->will($this->throwException(new \Exception('Store failed')));
2805
2806
        // Execute
2807
        $this->partlyMockedContentService->createContent($contentCreateStruct, []);
2808
    }
2809
2810
    public function providerForTestUpdateContentThrowsBadStateException()
2811
    {
2812
        return [
2813
            [VersionInfo::STATUS_PUBLISHED],
2814
            [VersionInfo::STATUS_ARCHIVED],
2815
        ];
2816
    }
2817
2818
    /**
2819
     * Test for the updateContent() method.
2820
     *
2821
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2822
     * @dataProvider providerForTestUpdateContentThrowsBadStateException
2823
     */
2824
    public function testUpdateContentThrowsBadStateException($status)
2825
    {
2826
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
2827
2828
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2829
        $contentUpdateStruct = new ContentUpdateStruct();
2830
        $versionInfo = new VersionInfo(
2831
            [
2832
                'contentInfo' => new ContentInfo(['id' => 42]),
2833
                'versionNo' => 7,
2834
                'status' => $status,
2835
            ]
2836
        );
2837
        $content = new Content(
2838
            [
2839
                'versionInfo' => $versionInfo,
2840
                'internalFields' => [],
2841
            ]
2842
        );
2843
2844
        $mockedService->expects($this->once())
2845
            ->method('loadContent')
2846
            ->with(
2847
                $this->equalTo(42),
2848
                $this->equalTo(null),
2849
                $this->equalTo(7)
2850
            )->will(
2851
                $this->returnValue($content)
2852
            );
2853
2854
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2855
    }
2856
2857
    /**
2858
     * Test for the updateContent() method.
2859
     *
2860
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2861
     */
2862
    public function testUpdateContentThrowsUnauthorizedException()
2863
    {
2864
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
2865
2866
        $permissionResolverMock = $this->getPermissionResolverMock();
2867
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2868
        $contentUpdateStruct = new ContentUpdateStruct();
2869
        $versionInfo = new VersionInfo(
2870
            [
2871
                'contentInfo' => new ContentInfo(['id' => 42]),
2872
                'versionNo' => 7,
2873
                'status' => VersionInfo::STATUS_DRAFT,
2874
            ]
2875
        );
2876
        $content = new Content(
2877
            [
2878
                'versionInfo' => $versionInfo,
2879
                'internalFields' => [],
2880
            ]
2881
        );
2882
2883
        $mockedService->expects($this->once())
2884
            ->method('loadContent')
2885
            ->with(
2886
                $this->equalTo(42),
2887
                $this->equalTo(null),
2888
                $this->equalTo(7)
2889
            )->will(
2890
                $this->returnValue($content)
2891
            );
2892
2893
        $permissionResolverMock->expects($this->once())
2894
            ->method('canUser')
2895
            ->with(
2896
                $this->equalTo('content'),
2897
                $this->equalTo('edit'),
2898
                $this->equalTo($content),
2899
                $this->isType('array')
2900
            )->will($this->returnValue(false));
2901
2902
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2903
    }
2904
2905
    /**
2906
     * @param string $initialLanguageCode
2907
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2908
     * @param string[] $existingLanguages
2909
     *
2910
     * @return string[]
2911
     */
2912
    protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages)
2913
    {
2914
        $languageCodes = array_fill_keys($existingLanguages, true);
2915
        if ($initialLanguageCode !== null) {
2916
            $languageCodes[$initialLanguageCode] = true;
2917
        }
2918
2919
        foreach ($structFields as $field) {
2920
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
2921
                continue;
2922
            }
2923
2924
            $languageCodes[$field->languageCode] = true;
2925
        }
2926
2927
        return array_keys($languageCodes);
2928
    }
2929
2930
    /**
2931
     * @param string $initialLanguageCode
2932
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2933
     * @param string $mainLanguageCode
2934
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2935
     *
2936
     * @return array
2937
     */
2938
    protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions)
2939
    {
2940
        $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode;
2941
2942
        $mappedFieldDefinitions = [];
2943
        foreach ($fieldDefinitions as $fieldDefinition) {
2944
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
2945
        }
2946
2947
        $mappedStructFields = [];
2948
        foreach ($structFields as $structField) {
2949
            $identifier = $structField->fieldDefIdentifier;
2950
2951
            if ($structField->languageCode !== null) {
2952
                $languageCode = $structField->languageCode;
2953
            } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) {
2954
                $languageCode = $initialLanguageCode;
2955
            } else {
2956
                $languageCode = $mainLanguageCode;
2957
            }
2958
2959
            $mappedStructFields[$identifier][$languageCode] = (string)$structField->value;
2960
        }
2961
2962
        return $mappedStructFields;
2963
    }
2964
2965
    /**
2966
     * Returns full, possibly redundant array of field values, indexed by field definition
2967
     * identifier and language code.
2968
     *
2969
     * @param string $initialLanguageCode
2970
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2971
     * @param \eZ\Publish\Core\Repository\Values\Content\Content $content
2972
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2973
     * @param array $languageCodes
2974
     *
2975
     * @return array
2976
     */
2977
    protected function determineValuesForUpdate(
2978
        $initialLanguageCode,
2979
        array $structFields,
2980
        Content $content,
2981
        array $fieldDefinitions,
2982
        array $languageCodes
2983
    ) {
2984
        $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode;
2985
2986
        $mappedStructFields = $this->mapStructFieldsForUpdate(
2987
            $initialLanguageCode,
2988
            $structFields,
2989
            $mainLanguageCode,
2990
            $fieldDefinitions
2991
        );
2992
2993
        $values = [];
2994
2995
        foreach ($fieldDefinitions as $fieldDefinition) {
2996
            $identifier = $fieldDefinition->identifier;
2997
            foreach ($languageCodes as $languageCode) {
2998 View Code Duplication
                if (!$fieldDefinition->isTranslatable) {
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...
2999
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
3000
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
3001
                    } else {
3002
                        $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode];
3003
                    }
3004
                    continue;
3005
                }
3006
3007 View Code Duplication
                if (isset($mappedStructFields[$identifier][$languageCode])) {
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...
3008
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
3009
                    continue;
3010
                }
3011
3012
                if (isset($content->fields[$identifier][$languageCode])) {
3013
                    $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode];
3014
                    continue;
3015
                }
3016
3017
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
3018
            }
3019
        }
3020
3021
        return $this->stubValues($values);
3022
    }
3023
3024
    protected function stubValues(array $fieldValues)
3025
    {
3026
        foreach ($fieldValues as &$languageValues) {
3027
            foreach ($languageValues as &$value) {
3028
                $value = new ValueStub($value);
3029
            }
3030
        }
3031
3032
        return $fieldValues;
3033
    }
3034
3035
    /**
3036
     * Asserts that calling updateContent() with given API field set causes calling
3037
     * Handler::updateContent() with given SPI field set.
3038
     *
3039
     * @param string $initialLanguageCode
3040
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3041
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
3042
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $existingFields
3043
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3044
     * @param bool $execute
3045
     *
3046
     * @return mixed
3047
     */
3048
    protected function assertForTestUpdateContentNonRedundantFieldSet(
3049
        $initialLanguageCode,
3050
        array $structFields,
3051
        array $spiFields,
3052
        array $existingFields,
3053
        array $fieldDefinitions,
3054
        $execute = true
3055
    ) {
3056
        $repositoryMock = $this->getRepositoryMock();
3057
        $permissionResolverMock = $this->getPermissionResolverMock();
3058
        $permissionResolverMock
3059
            ->expects($this->once())
3060
            ->method('getCurrentUserReference')
3061
            ->willReturn(new UserReference(169));
3062
        $mockedService = $this->getPartlyMockedContentService(['loadContent', 'loadRelations'], $permissionResolverMock);
3063
        $permissionResolverMock = $this->getPermissionResolverMock();
3064
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
3065
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
3066
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
3067
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
3068
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
3069
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3070
        $domainMapperMock = $this->getDomainMapperMock();
3071
        $relationProcessorMock = $this->getRelationProcessorMock();
3072
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
3073
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
3074
        $existingLanguageCodes = array_map(
3075
            function (Field $field) {
3076
                return $field->languageCode;
3077
            },
3078
            $existingFields
3079
        );
3080
        $languageCodes = $this->determineLanguageCodesForUpdate(
3081
            $initialLanguageCode,
3082
            $structFields,
3083
            $existingLanguageCodes
3084
        );
3085
        $versionInfo = new VersionInfo(
3086
            [
3087
                'contentInfo' => new ContentInfo(
3088
                    [
3089
                        'id' => 42,
3090
                        'contentTypeId' => 24,
3091
                        'mainLanguageCode' => 'eng-GB',
3092
                    ]
3093
                ),
3094
                'versionNo' => 7,
3095
                'languageCodes' => $existingLanguageCodes,
3096
                'status' => VersionInfo::STATUS_DRAFT,
3097
            ]
3098
        );
3099
        $content = new Content(
3100
            [
3101
                'versionInfo' => $versionInfo,
3102
                'internalFields' => $existingFields,
3103
            ]
3104
        );
3105
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
3106
3107
        $languageHandlerMock->expects($this->any())
3108
            ->method('loadByLanguageCode')
3109
            ->with($this->isType('string'))
3110
            ->will(
3111
                $this->returnCallback(
3112
                    function () {
3113
                        return new Language(['id' => 4242]);
3114
                    }
3115
                )
3116
            );
3117
3118
        $mockedService->expects($this->once())
3119
            ->method('loadContent')
3120
            ->with(
3121
                $this->equalTo(42),
3122
                $this->equalTo(null),
3123
                $this->equalTo(7)
3124
            )->will(
3125
                $this->returnValue($content)
3126
            );
3127
3128
        $repositoryMock->expects($this->once())->method('beginTransaction');
3129
3130
        $permissionResolverMock->expects($this->once())
3131
            ->method('canUser')
3132
            ->with(
3133
                $this->equalTo('content'),
3134
                $this->equalTo('edit'),
3135
                $this->equalTo($content),
3136
                $this->isType('array')
3137
            )->will($this->returnValue(true));
3138
3139
        $contentTypeServiceMock->expects($this->once())
3140
            ->method('loadContentType')
3141
            ->with($this->equalTo(24))
3142
            ->will($this->returnValue($contentType));
3143
3144
        $repositoryMock->expects($this->once())
3145
            ->method('getContentTypeService')
3146
            ->will($this->returnValue($contentTypeServiceMock));
3147
3148
        $fieldTypeMock->expects($this->any())
3149
            ->method('acceptValue')
3150
            ->will(
3151
                $this->returnCallback(
3152
                    function ($valueString) {
3153
                        return new ValueStub($valueString);
3154
                    }
3155
                )
3156
            );
3157
3158
        $emptyValue = self::EMPTY_FIELD_VALUE;
3159
        $fieldTypeMock->expects($this->any())
3160
            ->method('toPersistenceValue')
3161
            ->will(
3162
                $this->returnCallback(
3163
                    function (ValueStub $value) {
3164
                        return (string)$value;
3165
                    }
3166
                )
3167
            );
3168
3169
        $fieldTypeMock->expects($this->any())
3170
            ->method('isEmptyValue')
3171
            ->will(
3172
                $this->returnCallback(
3173
                    function (ValueStub $value) use ($emptyValue) {
3174
                        return $emptyValue === (string)$value;
3175
                    }
3176
                )
3177
            );
3178
3179
        $fieldTypeMock->expects($this->any())
3180
            ->method('validate')
3181
            ->will($this->returnValue([]));
3182
3183
        $this->getFieldTypeRegistryMock()->expects($this->any())
3184
            ->method('getFieldType')
3185
            ->will($this->returnValue($fieldTypeMock));
3186
3187
        $relationProcessorMock
3188
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
3189
            ->method('appendFieldRelations')
3190
            ->with(
3191
                $this->isType('array'),
3192
                $this->isType('array'),
3193
                $this->isInstanceOf(SPIFieldType::class),
3194
                $this->isInstanceOf(Value::class),
3195
                $this->anything()
3196
            );
3197
3198
        $values = $this->determineValuesForUpdate(
3199
            $initialLanguageCode,
3200
            $structFields,
3201
            $content,
3202
            $fieldDefinitions,
3203
            $languageCodes
3204
        );
3205
        $nameSchemaServiceMock->expects($this->once())
3206
            ->method('resolveNameSchema')
3207
            ->with(
3208
                $this->equalTo($content),
3209
                $this->equalTo($values),
3210
                $this->equalTo($languageCodes)
3211
            )->will($this->returnValue([]));
3212
3213
        $existingRelations = ['RELATIONS!!!'];
3214
        $mockedService->expects($this->once())
3215
            ->method('loadRelations')
3216
            ->with($content->versionInfo)
3217
            ->will($this->returnValue($existingRelations));
3218
        $relationProcessorMock->expects($this->any())
3219
            ->method('processFieldRelations')
3220
            ->with(
3221
                $this->isType('array'),
3222
                $this->equalTo(42),
3223
                $this->isType('int'),
3224
                $this->equalTo($contentType),
3225
                $this->equalTo($existingRelations)
3226
            );
3227
3228
        $contentUpdateStruct = new ContentUpdateStruct(
3229
            [
3230
                'fields' => $structFields,
3231
                'initialLanguageCode' => $initialLanguageCode,
3232
            ]
3233
        );
3234
3235
        if ($execute) {
3236
            $spiContentUpdateStruct = new SPIContentUpdateStruct(
3237
                [
3238
                    'creatorId' => 169,
3239
                    'fields' => $spiFields,
3240
                    'modificationDate' => time(),
3241
                    'initialLanguageId' => 4242,
3242
                ]
3243
            );
3244
3245
            // During code coverage runs, timestamp might differ 1-3 seconds
3246
            $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct;
3247
            ++$spiContentUpdateStructTs1->modificationDate;
3248
3249
            $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1;
3250
            ++$spiContentUpdateStructTs2->modificationDate;
3251
3252
            $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2;
3253
            ++$spiContentUpdateStructTs3->modificationDate;
3254
3255
            $spiContent = new SPIContent(
3256
                [
3257
                    'versionInfo' => new SPIContent\VersionInfo(
3258
                        [
3259
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
3260
                            'versionNo' => 7,
3261
                        ]
3262
                    ),
3263
                ]
3264
            );
3265
3266
            $contentHandlerMock->expects($this->once())
3267
                ->method('updateContent')
3268
                ->with(
3269
                    42,
3270
                    7,
3271
                    $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3)
3272
                )
3273
                ->will($this->returnValue($spiContent));
3274
3275
            $repositoryMock->expects($this->once())->method('commit');
3276
            $domainMapperMock->expects($this->once())
3277
                ->method('buildContentDomainObject')
3278
                ->with(
3279
                    $this->isInstanceOf(SPIContent::class),
3280
                    $this->isInstanceOf(APIContentType::class)
3281
                );
3282
3283
            $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
3284
        }
3285
3286
        return [$content->versionInfo, $contentUpdateStruct];
3287
    }
3288
3289
    public function providerForTestUpdateContentNonRedundantFieldSet1()
3290
    {
3291
        $spiFields = [
3292
            new SPIField(
3293
                [
3294
                    'id' => '100',
3295
                    'fieldDefinitionId' => 'fieldDefinitionId',
3296
                    'type' => 'fieldTypeIdentifier',
3297
                    'value' => 'newValue',
3298
                    'languageCode' => 'eng-GB',
3299
                    'versionNo' => 7,
3300
                ]
3301
            ),
3302
        ];
3303
3304
        return [
3305
            // With languages set
3306
            [
3307
                'eng-GB',
3308
                [
3309
                    new Field(
3310
                        [
3311
                            'fieldDefIdentifier' => 'identifier',
3312
                            'value' => 'newValue',
3313
                            'languageCode' => 'eng-GB',
3314
                        ]
3315
                    ),
3316
                ],
3317
                $spiFields,
3318
            ],
3319
            // Without languages set
3320
            [
3321
                null,
3322
                [
3323
                    new Field(
3324
                        [
3325
                            'fieldDefIdentifier' => 'identifier',
3326
                            'value' => 'newValue',
3327
                            'languageCode' => null,
3328
                        ]
3329
                    ),
3330
                ],
3331
                $spiFields,
3332
            ],
3333
            // Adding new language without fields
3334
            [
3335
                'eng-US',
3336
                [],
3337
                [],
3338
            ],
3339
        ];
3340
    }
3341
3342
    /**
3343
     * Test for the updateContent() method.
3344
     *
3345
     * Testing the simplest use case.
3346
     *
3347
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3348
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3349
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3350
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1
3351
     */
3352 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields)
3353
    {
3354
        $existingFields = [
3355
            new Field(
3356
                [
3357
                    'id' => '100',
3358
                    'fieldDefIdentifier' => 'identifier',
3359
                    'value' => 'initialValue',
3360
                    'languageCode' => 'eng-GB',
3361
                ]
3362
            ),
3363
        ];
3364
3365
        $fieldDefinitions = [
3366
            new FieldDefinition(
3367
                [
3368
                    'id' => 'fieldDefinitionId',
3369
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3370
                    'isTranslatable' => false,
3371
                    'identifier' => 'identifier',
3372
                    'isRequired' => false,
3373
                    'defaultValue' => 'defaultValue',
3374
                ]
3375
            ),
3376
        ];
3377
3378
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3379
            $initialLanguageCode,
3380
            $structFields,
3381
            $spiFields,
3382
            $existingFields,
3383
            $fieldDefinitions
3384
        );
3385
    }
3386
3387
    public function providerForTestUpdateContentNonRedundantFieldSet2()
3388
    {
3389
        $spiFields0 = [
3390
            new SPIField(
3391
                [
3392
                    'id' => '100',
3393
                    'fieldDefinitionId' => 'fieldDefinitionId',
3394
                    'type' => 'fieldTypeIdentifier',
3395
                    'value' => 'newValue',
3396
                    'languageCode' => 'eng-GB',
3397
                    'versionNo' => 7,
3398
                ]
3399
            ),
3400
        ];
3401
        $spiFields1 = [
3402
            new SPIField(
3403
                [
3404
                    'id' => null,
3405
                    'fieldDefinitionId' => 'fieldDefinitionId',
3406
                    'type' => 'fieldTypeIdentifier',
3407
                    'value' => 'newValue',
3408
                    'languageCode' => 'eng-US',
3409
                    'versionNo' => 7,
3410
                ]
3411
            ),
3412
        ];
3413
        $spiFields2 = [
3414
            new SPIField(
3415
                [
3416
                    'id' => 100,
3417
                    'fieldDefinitionId' => 'fieldDefinitionId',
3418
                    'type' => 'fieldTypeIdentifier',
3419
                    'value' => 'newValue2',
3420
                    'languageCode' => 'eng-GB',
3421
                    'versionNo' => 7,
3422
                ]
3423
            ),
3424
            new SPIField(
3425
                [
3426
                    'id' => null,
3427
                    'fieldDefinitionId' => 'fieldDefinitionId',
3428
                    'type' => 'fieldTypeIdentifier',
3429
                    'value' => 'newValue1',
3430
                    'languageCode' => 'eng-US',
3431
                    'versionNo' => 7,
3432
                ]
3433
            ),
3434
        ];
3435
3436
        return [
3437
            // 0. With languages set
3438
            [
3439
                'eng-GB',
3440
                [
3441
                    new Field(
3442
                        [
3443
                            'fieldDefIdentifier' => 'identifier',
3444
                            'value' => 'newValue',
3445
                            'languageCode' => 'eng-GB',
3446
                        ]
3447
                    ),
3448
                ],
3449
                $spiFields0,
3450
            ],
3451
            // 1. Without languages set
3452
            [
3453
                null,
3454
                [
3455
                    new Field(
3456
                        [
3457
                            'fieldDefIdentifier' => 'identifier',
3458
                            'value' => 'newValue',
3459
                            'languageCode' => null,
3460
                        ]
3461
                    ),
3462
                ],
3463
                $spiFields0,
3464
            ],
3465
            // 2. New language with language set
3466
            [
3467
                'eng-GB',
3468
                [
3469
                    new Field(
3470
                        [
3471
                            'fieldDefIdentifier' => 'identifier',
3472
                            'value' => 'newValue',
3473
                            'languageCode' => 'eng-US',
3474
                        ]
3475
                    ),
3476
                ],
3477
                $spiFields1,
3478
            ],
3479
            // 3. New language without language set
3480
            [
3481
                'eng-US',
3482
                [
3483
                    new Field(
3484
                        [
3485
                            'fieldDefIdentifier' => 'identifier',
3486
                            'value' => 'newValue',
3487
                            'languageCode' => null,
3488
                        ]
3489
                    ),
3490
                ],
3491
                $spiFields1,
3492
            ],
3493
            // 4. New language and existing language with language set
3494
            [
3495
                'eng-GB',
3496
                [
3497
                    new Field(
3498
                        [
3499
                            'fieldDefIdentifier' => 'identifier',
3500
                            'value' => 'newValue1',
3501
                            'languageCode' => 'eng-US',
3502
                        ]
3503
                    ),
3504
                    new Field(
3505
                        [
3506
                            'fieldDefIdentifier' => 'identifier',
3507
                            'value' => 'newValue2',
3508
                            'languageCode' => 'eng-GB',
3509
                        ]
3510
                    ),
3511
                ],
3512
                $spiFields2,
3513
            ],
3514
            // 5. New language and existing language without language set
3515
            [
3516
                'eng-US',
3517
                [
3518
                    new Field(
3519
                        [
3520
                            'fieldDefIdentifier' => 'identifier',
3521
                            'value' => 'newValue1',
3522
                            'languageCode' => null,
3523
                        ]
3524
                    ),
3525
                    new Field(
3526
                        [
3527
                            'fieldDefIdentifier' => 'identifier',
3528
                            'value' => 'newValue2',
3529
                            'languageCode' => 'eng-GB',
3530
                        ]
3531
                    ),
3532
                ],
3533
                $spiFields2,
3534
            ],
3535
            // 6. Adding new language without fields
3536
            [
3537
                'eng-US',
3538
                [],
3539
                [
3540
                    new SPIField(
3541
                        [
3542
                            'id' => null,
3543
                            'fieldDefinitionId' => 'fieldDefinitionId',
3544
                            'type' => 'fieldTypeIdentifier',
3545
                            'value' => 'defaultValue',
3546
                            'languageCode' => 'eng-US',
3547
                            'versionNo' => 7,
3548
                        ]
3549
                    ),
3550
                ],
3551
            ],
3552
        ];
3553
    }
3554
3555
    /**
3556
     * Test for the updateContent() method.
3557
     *
3558
     * Testing with translatable field.
3559
     *
3560
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3561
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3562
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3563
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2
3564
     */
3565 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields)
3566
    {
3567
        $existingFields = [
3568
            new Field(
3569
                [
3570
                    'id' => '100',
3571
                    'fieldDefIdentifier' => 'identifier',
3572
                    'value' => 'initialValue',
3573
                    'languageCode' => 'eng-GB',
3574
                ]
3575
            ),
3576
        ];
3577
3578
        $fieldDefinitions = [
3579
            new FieldDefinition(
3580
                [
3581
                    'id' => 'fieldDefinitionId',
3582
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3583
                    'isTranslatable' => true,
3584
                    'identifier' => 'identifier',
3585
                    'isRequired' => false,
3586
                    'defaultValue' => 'defaultValue',
3587
                ]
3588
            ),
3589
        ];
3590
3591
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3592
            $initialLanguageCode,
3593
            $structFields,
3594
            $spiFields,
3595
            $existingFields,
3596
            $fieldDefinitions
3597
        );
3598
    }
3599
3600
    public function providerForTestUpdateContentNonRedundantFieldSet3()
3601
    {
3602
        $spiFields0 = [
3603
            new SPIField(
3604
                [
3605
                    'id' => null,
3606
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3607
                    'type' => 'fieldTypeIdentifier',
3608
                    'value' => 'newValue1',
3609
                    'languageCode' => 'eng-US',
3610
                    'versionNo' => 7,
3611
                ]
3612
            ),
3613
        ];
3614
        $spiFields1 = [
3615
            new SPIField(
3616
                [
3617
                    'id' => 100,
3618
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3619
                    'type' => 'fieldTypeIdentifier',
3620
                    'value' => 'newValue2',
3621
                    'languageCode' => 'eng-GB',
3622
                    'versionNo' => 7,
3623
                ]
3624
            ),
3625
            new SPIField(
3626
                [
3627
                    'id' => null,
3628
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3629
                    'type' => 'fieldTypeIdentifier',
3630
                    'value' => 'newValue1',
3631
                    'languageCode' => 'eng-US',
3632
                    'versionNo' => 7,
3633
                ]
3634
            ),
3635
        ];
3636
        $spiFields2 = [
3637
            new SPIField(
3638
                [
3639
                    'id' => 100,
3640
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3641
                    'type' => 'fieldTypeIdentifier',
3642
                    'value' => 'newValue2',
3643
                    'languageCode' => 'eng-GB',
3644
                    'versionNo' => 7,
3645
                ]
3646
            ),
3647
            new SPIField(
3648
                [
3649
                    'id' => null,
3650
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3651
                    'type' => 'fieldTypeIdentifier',
3652
                    'value' => 'newValue1',
3653
                    'languageCode' => 'eng-US',
3654
                    'versionNo' => 7,
3655
                ]
3656
            ),
3657
            new SPIField(
3658
                [
3659
                    'id' => 101,
3660
                    'fieldDefinitionId' => 'fieldDefinitionId2',
3661
                    'type' => 'fieldTypeIdentifier',
3662
                    'value' => 'newValue3',
3663
                    'languageCode' => 'eng-GB',
3664
                    'versionNo' => 7,
3665
                ]
3666
            ),
3667
        ];
3668
        $spiFields3 = [
3669
            new SPIField(
3670
                [
3671
                    'id' => null,
3672
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3673
                    'type' => 'fieldTypeIdentifier',
3674
                    'value' => 'defaultValue1',
3675
                    'languageCode' => 'eng-US',
3676
                    'versionNo' => 7,
3677
                ]
3678
            ),
3679
        ];
3680
3681
        return [
3682
            // 0. ew language with language set
3683
            [
3684
                'eng-US',
3685
                [
3686
                    new Field(
3687
                        [
3688
                            'fieldDefIdentifier' => 'identifier1',
3689
                            'value' => 'newValue1',
3690
                            'languageCode' => 'eng-US',
3691
                        ]
3692
                    ),
3693
                ],
3694
                $spiFields0,
3695
            ],
3696
            // 1. New language without language set
3697
            [
3698
                'eng-US',
3699
                [
3700
                    new Field(
3701
                        [
3702
                            'fieldDefIdentifier' => 'identifier1',
3703
                            'value' => 'newValue1',
3704
                            'languageCode' => null,
3705
                        ]
3706
                    ),
3707
                ],
3708
                $spiFields0,
3709
            ],
3710
            // 2. New language and existing language with language set
3711
            [
3712
                'eng-US',
3713
                [
3714
                    new Field(
3715
                        [
3716
                            'fieldDefIdentifier' => 'identifier1',
3717
                            'value' => 'newValue1',
3718
                            'languageCode' => 'eng-US',
3719
                        ]
3720
                    ),
3721
                    new Field(
3722
                        [
3723
                            'fieldDefIdentifier' => 'identifier1',
3724
                            'value' => 'newValue2',
3725
                            'languageCode' => 'eng-GB',
3726
                        ]
3727
                    ),
3728
                ],
3729
                $spiFields1,
3730
            ],
3731
            // 3. New language and existing language without language set
3732
            [
3733
                'eng-US',
3734
                [
3735
                    new Field(
3736
                        [
3737
                            'fieldDefIdentifier' => 'identifier1',
3738
                            'value' => 'newValue1',
3739
                            'languageCode' => null,
3740
                        ]
3741
                    ),
3742
                    new Field(
3743
                        [
3744
                            'fieldDefIdentifier' => 'identifier1',
3745
                            'value' => 'newValue2',
3746
                            'languageCode' => 'eng-GB',
3747
                        ]
3748
                    ),
3749
                ],
3750
                $spiFields1,
3751
            ],
3752
            // 4. New language and existing language with untranslatable field, with language set
3753
            [
3754
                'eng-US',
3755
                [
3756
                    new Field(
3757
                        [
3758
                            'fieldDefIdentifier' => 'identifier1',
3759
                            'value' => 'newValue1',
3760
                            'languageCode' => 'eng-US',
3761
                        ]
3762
                    ),
3763
                    new Field(
3764
                        [
3765
                            'fieldDefIdentifier' => 'identifier1',
3766
                            'value' => 'newValue2',
3767
                            'languageCode' => 'eng-GB',
3768
                        ]
3769
                    ),
3770
                    new Field(
3771
                        [
3772
                            'fieldDefIdentifier' => 'identifier2',
3773
                            'value' => 'newValue3',
3774
                            'languageCode' => 'eng-GB',
3775
                        ]
3776
                    ),
3777
                ],
3778
                $spiFields2,
3779
            ],
3780
            // 5. New language and existing language with untranslatable field, without language set
3781
            [
3782
                'eng-US',
3783
                [
3784
                    new Field(
3785
                        [
3786
                            'fieldDefIdentifier' => 'identifier1',
3787
                            'value' => 'newValue1',
3788
                            'languageCode' => null,
3789
                        ]
3790
                    ),
3791
                    new Field(
3792
                        [
3793
                            'fieldDefIdentifier' => 'identifier1',
3794
                            'value' => 'newValue2',
3795
                            'languageCode' => 'eng-GB',
3796
                        ]
3797
                    ),
3798
                    new Field(
3799
                        [
3800
                            'fieldDefIdentifier' => 'identifier2',
3801
                            'value' => 'newValue3',
3802
                            'languageCode' => null,
3803
                        ]
3804
                    ),
3805
                ],
3806
                $spiFields2,
3807
            ],
3808
            // 6. Adding new language without fields
3809
            [
3810
                'eng-US',
3811
                [],
3812
                $spiFields3,
3813
            ],
3814
        ];
3815
    }
3816
3817
    /**
3818
     * Test for the updateContent() method.
3819
     *
3820
     * Testing with new language and untranslatable field.
3821
     *
3822
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3823
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3824
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3825
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3
3826
     */
3827
    public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields)
3828
    {
3829
        $existingFields = [
3830
            new Field(
3831
                [
3832
                    'id' => '100',
3833
                    'fieldDefIdentifier' => 'identifier1',
3834
                    'value' => 'initialValue1',
3835
                    'languageCode' => 'eng-GB',
3836
                ]
3837
            ),
3838
            new Field(
3839
                [
3840
                    'id' => '101',
3841
                    'fieldDefIdentifier' => 'identifier2',
3842
                    'value' => 'initialValue2',
3843
                    'languageCode' => 'eng-GB',
3844
                ]
3845
            ),
3846
        ];
3847
3848
        $fieldDefinitions = [
3849
            new FieldDefinition(
3850
                [
3851
                    'id' => 'fieldDefinitionId1',
3852
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3853
                    'isTranslatable' => true,
3854
                    'identifier' => 'identifier1',
3855
                    'isRequired' => false,
3856
                    'defaultValue' => 'defaultValue1',
3857
                ]
3858
            ),
3859
            new FieldDefinition(
3860
                [
3861
                    'id' => 'fieldDefinitionId2',
3862
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3863
                    'isTranslatable' => false,
3864
                    'identifier' => 'identifier2',
3865
                    'isRequired' => false,
3866
                    'defaultValue' => 'defaultValue2',
3867
                ]
3868
            ),
3869
        ];
3870
3871
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3872
            $initialLanguageCode,
3873
            $structFields,
3874
            $spiFields,
3875
            $existingFields,
3876
            $fieldDefinitions
3877
        );
3878
    }
3879
3880
    public function providerForTestUpdateContentNonRedundantFieldSet4()
3881
    {
3882
        $spiFields0 = [
3883
            new SPIField(
3884
                [
3885
                    'id' => null,
3886
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3887
                    'type' => 'fieldTypeIdentifier',
3888
                    'value' => 'newValue1',
3889
                    'languageCode' => 'eng-US',
3890
                    'versionNo' => 7,
3891
                ]
3892
            ),
3893
        ];
3894
        $spiFields1 = [
3895
            new SPIField(
3896
                [
3897
                    'id' => 100,
3898
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3899
                    'type' => 'fieldTypeIdentifier',
3900
                    'value' => self::EMPTY_FIELD_VALUE,
3901
                    'languageCode' => 'eng-GB',
3902
                    'versionNo' => 7,
3903
                ]
3904
            ),
3905
            new SPIField(
3906
                [
3907
                    'id' => null,
3908
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3909
                    'type' => 'fieldTypeIdentifier',
3910
                    'value' => 'newValue1',
3911
                    'languageCode' => 'eng-US',
3912
                    'versionNo' => 7,
3913
                ]
3914
            ),
3915
        ];
3916
        $spiFields2 = [
3917
            new SPIField(
3918
                [
3919
                    'id' => 100,
3920
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3921
                    'type' => 'fieldTypeIdentifier',
3922
                    'value' => self::EMPTY_FIELD_VALUE,
3923
                    'languageCode' => 'eng-GB',
3924
                    'versionNo' => 7,
3925
                ]
3926
            ),
3927
        ];
3928
3929
        return [
3930
            // 0. New translation with empty field by default
3931
            [
3932
                'eng-US',
3933
                [
3934
                    new Field(
3935
                        [
3936
                            'fieldDefIdentifier' => 'identifier1',
3937
                            'value' => 'newValue1',
3938
                            'languageCode' => 'eng-US',
3939
                        ]
3940
                    ),
3941
                ],
3942
                $spiFields0,
3943
            ],
3944
            // 1. New translation with empty field by default, without language set
3945
            [
3946
                'eng-US',
3947
                [
3948
                    new Field(
3949
                        [
3950
                            'fieldDefIdentifier' => 'identifier1',
3951
                            'value' => 'newValue1',
3952
                            'languageCode' => null,
3953
                        ]
3954
                    ),
3955
                ],
3956
                $spiFields0,
3957
            ],
3958
            // 2. New translation with empty field given
3959
            [
3960
                'eng-US',
3961
                [
3962
                    new Field(
3963
                        [
3964
                            'fieldDefIdentifier' => 'identifier1',
3965
                            'value' => 'newValue1',
3966
                            'languageCode' => 'eng-US',
3967
                        ]
3968
                    ),
3969
                    new Field(
3970
                        [
3971
                            'fieldDefIdentifier' => 'identifier2',
3972
                            'value' => self::EMPTY_FIELD_VALUE,
3973
                            'languageCode' => 'eng-US',
3974
                        ]
3975
                    ),
3976
                ],
3977
                $spiFields0,
3978
            ],
3979
            // 3. New translation with empty field given, without language set
3980
            [
3981
                'eng-US',
3982
                [
3983
                    new Field(
3984
                        [
3985
                            'fieldDefIdentifier' => 'identifier1',
3986
                            'value' => 'newValue1',
3987
                            'languageCode' => null,
3988
                        ]
3989
                    ),
3990
                    new Field(
3991
                        [
3992
                            'fieldDefIdentifier' => 'identifier2',
3993
                            'value' => self::EMPTY_FIELD_VALUE,
3994
                            'languageCode' => null,
3995
                        ]
3996
                    ),
3997
                ],
3998
                $spiFields0,
3999
            ],
4000
            // 4. Updating existing language with empty value
4001
            [
4002
                'eng-US',
4003
                [
4004
                    new Field(
4005
                        [
4006
                            'fieldDefIdentifier' => 'identifier1',
4007
                            'value' => 'newValue1',
4008
                            'languageCode' => 'eng-US',
4009
                        ]
4010
                    ),
4011
                    new Field(
4012
                        [
4013
                            'fieldDefIdentifier' => 'identifier1',
4014
                            'value' => self::EMPTY_FIELD_VALUE,
4015
                            'languageCode' => 'eng-GB',
4016
                        ]
4017
                    ),
4018
                ],
4019
                $spiFields1,
4020
            ],
4021
            // 5. Updating existing language with empty value, without language set
4022
            [
4023
                'eng-US',
4024
                [
4025
                    new Field(
4026
                        [
4027
                            'fieldDefIdentifier' => 'identifier1',
4028
                            'value' => 'newValue1',
4029
                            'languageCode' => null,
4030
                        ]
4031
                    ),
4032
                    new Field(
4033
                        [
4034
                            'fieldDefIdentifier' => 'identifier1',
4035
                            'value' => self::EMPTY_FIELD_VALUE,
4036
                            'languageCode' => 'eng-GB',
4037
                        ]
4038
                    ),
4039
                ],
4040
                $spiFields1,
4041
            ],
4042
            // 6. Updating existing language with empty value and adding new language with empty value
4043
            [
4044
                'eng-US',
4045
                [
4046
                    new Field(
4047
                        [
4048
                            'fieldDefIdentifier' => 'identifier1',
4049
                            'value' => self::EMPTY_FIELD_VALUE,
4050
                            'languageCode' => 'eng-US',
4051
                        ]
4052
                    ),
4053
                    new Field(
4054
                        [
4055
                            'fieldDefIdentifier' => 'identifier1',
4056
                            'value' => self::EMPTY_FIELD_VALUE,
4057
                            'languageCode' => 'eng-GB',
4058
                        ]
4059
                    ),
4060
                ],
4061
                $spiFields2,
4062
            ],
4063
            // 7. Updating existing language with empty value and adding new language with empty value,
4064
            // without language set
4065
            [
4066
                'eng-US',
4067
                [
4068
                    new Field(
4069
                        [
4070
                            'fieldDefIdentifier' => 'identifier1',
4071
                            'value' => self::EMPTY_FIELD_VALUE,
4072
                            'languageCode' => null,
4073
                        ]
4074
                    ),
4075
                    new Field(
4076
                        [
4077
                            'fieldDefIdentifier' => 'identifier1',
4078
                            'value' => self::EMPTY_FIELD_VALUE,
4079
                            'languageCode' => 'eng-GB',
4080
                        ]
4081
                    ),
4082
                ],
4083
                $spiFields2,
4084
            ],
4085
            // 8. Adding new language with no fields given
4086
            [
4087
                'eng-US',
4088
                [],
4089
                [],
4090
            ],
4091
            // 9. Adding new language with fields
4092
            [
4093
                'eng-US',
4094
                [
4095
                    new Field(
4096
                        [
4097
                            'fieldDefIdentifier' => 'identifier1',
4098
                            'value' => self::EMPTY_FIELD_VALUE,
4099
                            'languageCode' => 'eng-US',
4100
                        ]
4101
                    ),
4102
                ],
4103
                [],
4104
            ],
4105
            // 10. Adding new language with fields, without language set
4106
            [
4107
                'eng-US',
4108
                [
4109
                    new Field(
4110
                        [
4111
                            'fieldDefIdentifier' => 'identifier1',
4112
                            'value' => self::EMPTY_FIELD_VALUE,
4113
                            'languageCode' => null,
4114
                        ]
4115
                    ),
4116
                ],
4117
                [],
4118
            ],
4119
        ];
4120
    }
4121
4122
    /**
4123
     * Test for the updateContent() method.
4124
     *
4125
     * Testing with empty values.
4126
     *
4127
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4128
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4129
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4130
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4
4131
     */
4132
    public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields)
4133
    {
4134
        $existingFields = [
4135
            new Field(
4136
                [
4137
                    'id' => '100',
4138
                    'fieldDefIdentifier' => 'identifier1',
4139
                    'value' => 'initialValue1',
4140
                    'languageCode' => 'eng-GB',
4141
                ]
4142
            ),
4143
            new Field(
4144
                [
4145
                    'id' => '101',
4146
                    'fieldDefIdentifier' => 'identifier2',
4147
                    'value' => 'initialValue2',
4148
                    'languageCode' => 'eng-GB',
4149
                ]
4150
            ),
4151
        ];
4152
4153
        $fieldDefinitions = [
4154
            new FieldDefinition(
4155
                [
4156
                    'id' => 'fieldDefinitionId1',
4157
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4158
                    'isTranslatable' => true,
4159
                    'identifier' => 'identifier1',
4160
                    'isRequired' => false,
4161
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4162
                ]
4163
            ),
4164
            new FieldDefinition(
4165
                [
4166
                    'id' => 'fieldDefinitionId2',
4167
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4168
                    'isTranslatable' => true,
4169
                    'identifier' => 'identifier2',
4170
                    'isRequired' => false,
4171
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4172
                ]
4173
            ),
4174
        ];
4175
4176
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4177
            $initialLanguageCode,
4178
            $structFields,
4179
            $spiFields,
4180
            $existingFields,
4181
            $fieldDefinitions
4182
        );
4183
    }
4184
4185
    /**
4186
     * @todo add first field empty
4187
     *
4188
     * @return array
4189
     */
4190
    public function providerForTestUpdateContentNonRedundantFieldSetComplex()
4191
    {
4192
        $spiFields0 = [
4193
            new SPIField(
4194
                [
4195
                    'id' => 100,
4196
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4197
                    'type' => 'fieldTypeIdentifier',
4198
                    'value' => 'newValue1-eng-GB',
4199
                    'languageCode' => 'eng-GB',
4200
                    'versionNo' => 7,
4201
                ]
4202
            ),
4203
            new SPIField(
4204
                [
4205
                    'id' => null,
4206
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4207
                    'type' => 'fieldTypeIdentifier',
4208
                    'value' => 'newValue4',
4209
                    'languageCode' => 'eng-US',
4210
                    'versionNo' => 7,
4211
                ]
4212
            ),
4213
        ];
4214
        $spiFields1 = [
4215
            new SPIField(
4216
                [
4217
                    'id' => 100,
4218
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4219
                    'type' => 'fieldTypeIdentifier',
4220
                    'value' => 'newValue1-eng-GB',
4221
                    'languageCode' => 'eng-GB',
4222
                    'versionNo' => 7,
4223
                ]
4224
            ),
4225
            new SPIField(
4226
                [
4227
                    'id' => null,
4228
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4229
                    'type' => 'fieldTypeIdentifier',
4230
                    'value' => 'newValue2',
4231
                    'languageCode' => 'eng-US',
4232
                    'versionNo' => 7,
4233
                ]
4234
            ),
4235
            new SPIField(
4236
                [
4237
                    'id' => null,
4238
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4239
                    'type' => 'fieldTypeIdentifier',
4240
                    'value' => 'defaultValue4',
4241
                    'languageCode' => 'eng-US',
4242
                    'versionNo' => 7,
4243
                ]
4244
            ),
4245
        ];
4246
        $spiFields2 = [
4247
            new SPIField(
4248
                [
4249
                    'id' => 100,
4250
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4251
                    'type' => 'fieldTypeIdentifier',
4252
                    'value' => 'newValue1-eng-GB',
4253
                    'languageCode' => 'eng-GB',
4254
                    'versionNo' => 7,
4255
                ]
4256
            ),
4257
            new SPIField(
4258
                [
4259
                    'id' => null,
4260
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4261
                    'type' => 'fieldTypeIdentifier',
4262
                    'value' => 'newValue2',
4263
                    'languageCode' => 'eng-US',
4264
                    'versionNo' => 7,
4265
                ]
4266
            ),
4267
            new SPIField(
4268
                [
4269
                    'id' => null,
4270
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4271
                    'type' => 'fieldTypeIdentifier',
4272
                    'value' => 'defaultValue4',
4273
                    'languageCode' => 'ger-DE',
4274
                    'versionNo' => 7,
4275
                ]
4276
            ),
4277
            new SPIField(
4278
                [
4279
                    'id' => null,
4280
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4281
                    'type' => 'fieldTypeIdentifier',
4282
                    'value' => 'defaultValue4',
4283
                    'languageCode' => 'eng-US',
4284
                    'versionNo' => 7,
4285
                ]
4286
            ),
4287
        ];
4288
4289
        return [
4290
            // 0. Add new language and update existing
4291
            [
4292
                'eng-US',
4293
                [
4294
                    new Field(
4295
                        [
4296
                            'fieldDefIdentifier' => 'identifier4',
4297
                            'value' => 'newValue4',
4298
                            'languageCode' => 'eng-US',
4299
                        ]
4300
                    ),
4301
                    new Field(
4302
                        [
4303
                            'fieldDefIdentifier' => 'identifier1',
4304
                            'value' => 'newValue1-eng-GB',
4305
                            'languageCode' => 'eng-GB',
4306
                        ]
4307
                    ),
4308
                ],
4309
                $spiFields0,
4310
            ],
4311
            // 1. Add new language and update existing, without language set
4312
            [
4313
                'eng-US',
4314
                [
4315
                    new Field(
4316
                        [
4317
                            'fieldDefIdentifier' => 'identifier4',
4318
                            'value' => 'newValue4',
4319
                            'languageCode' => null,
4320
                        ]
4321
                    ),
4322
                    new Field(
4323
                        [
4324
                            'fieldDefIdentifier' => 'identifier1',
4325
                            'value' => 'newValue1-eng-GB',
4326
                            'languageCode' => 'eng-GB',
4327
                        ]
4328
                    ),
4329
                ],
4330
                $spiFields0,
4331
            ],
4332
            // 2. Add new language and update existing variant
4333
            [
4334
                'eng-US',
4335
                [
4336
                    new Field(
4337
                        [
4338
                            'fieldDefIdentifier' => 'identifier2',
4339
                            'value' => 'newValue2',
4340
                            'languageCode' => 'eng-US',
4341
                        ]
4342
                    ),
4343
                    new Field(
4344
                        [
4345
                            'fieldDefIdentifier' => 'identifier1',
4346
                            'value' => 'newValue1-eng-GB',
4347
                            'languageCode' => 'eng-GB',
4348
                        ]
4349
                    ),
4350
                ],
4351
                $spiFields1,
4352
            ],
4353
            // 3. Add new language and update existing variant, without language set
4354
            [
4355
                'eng-US',
4356
                [
4357
                    new Field(
4358
                        [
4359
                            'fieldDefIdentifier' => 'identifier2',
4360
                            'value' => 'newValue2',
4361
                            'languageCode' => null,
4362
                        ]
4363
                    ),
4364
                    new Field(
4365
                        [
4366
                            'fieldDefIdentifier' => 'identifier1',
4367
                            'value' => 'newValue1-eng-GB',
4368
                            'languageCode' => 'eng-GB',
4369
                        ]
4370
                    ),
4371
                ],
4372
                $spiFields1,
4373
            ],
4374
            // 4. Update with multiple languages
4375
            [
4376
                'ger-DE',
4377
                [
4378
                    new Field(
4379
                        [
4380
                            'fieldDefIdentifier' => 'identifier2',
4381
                            'value' => 'newValue2',
4382
                            'languageCode' => 'eng-US',
4383
                        ]
4384
                    ),
4385
                    new Field(
4386
                        [
4387
                            'fieldDefIdentifier' => 'identifier1',
4388
                            'value' => 'newValue1-eng-GB',
4389
                            'languageCode' => 'eng-GB',
4390
                        ]
4391
                    ),
4392
                ],
4393
                $spiFields2,
4394
            ],
4395
            // 5. Update with multiple languages without language set
4396
            [
4397
                'ger-DE',
4398
                [
4399
                    new Field(
4400
                        [
4401
                            'fieldDefIdentifier' => 'identifier2',
4402
                            'value' => 'newValue2',
4403
                            'languageCode' => 'eng-US',
4404
                        ]
4405
                    ),
4406
                    new Field(
4407
                        [
4408
                            'fieldDefIdentifier' => 'identifier1',
4409
                            'value' => 'newValue1-eng-GB',
4410
                            'languageCode' => null,
4411
                        ]
4412
                    ),
4413
                ],
4414
                $spiFields2,
4415
            ],
4416
        ];
4417
    }
4418
4419
    protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex()
4420
    {
4421
        $existingFields = [
4422
            new Field(
4423
                [
4424
                    'id' => '100',
4425
                    'fieldDefIdentifier' => 'identifier1',
4426
                    'value' => 'initialValue1',
4427
                    'languageCode' => 'eng-GB',
4428
                ]
4429
            ),
4430
            new Field(
4431
                [
4432
                    'id' => '101',
4433
                    'fieldDefIdentifier' => 'identifier2',
4434
                    'value' => 'initialValue2',
4435
                    'languageCode' => 'eng-GB',
4436
                ]
4437
            ),
4438
            new Field(
4439
                [
4440
                    'id' => '102',
4441
                    'fieldDefIdentifier' => 'identifier3',
4442
                    'value' => 'initialValue3',
4443
                    'languageCode' => 'eng-GB',
4444
                ]
4445
            ),
4446
            new Field(
4447
                [
4448
                    'id' => '103',
4449
                    'fieldDefIdentifier' => 'identifier4',
4450
                    'value' => 'initialValue4',
4451
                    'languageCode' => 'eng-GB',
4452
                ]
4453
            ),
4454
        ];
4455
4456
        $fieldDefinitions = [
4457
            new FieldDefinition(
4458
                [
4459
                    'id' => 'fieldDefinitionId1',
4460
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4461
                    'isTranslatable' => false,
4462
                    'identifier' => 'identifier1',
4463
                    'isRequired' => false,
4464
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4465
                ]
4466
            ),
4467
            new FieldDefinition(
4468
                [
4469
                    'id' => 'fieldDefinitionId2',
4470
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4471
                    'isTranslatable' => true,
4472
                    'identifier' => 'identifier2',
4473
                    'isRequired' => false,
4474
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4475
                ]
4476
            ),
4477
            new FieldDefinition(
4478
                [
4479
                    'id' => 'fieldDefinitionId3',
4480
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4481
                    'isTranslatable' => false,
4482
                    'identifier' => 'identifier3',
4483
                    'isRequired' => false,
4484
                    'defaultValue' => 'defaultValue3',
4485
                ]
4486
            ),
4487
            new FieldDefinition(
4488
                [
4489
                    'id' => 'fieldDefinitionId4',
4490
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4491
                    'isTranslatable' => true,
4492
                    'identifier' => 'identifier4',
4493
                    'isRequired' => false,
4494
                    'defaultValue' => 'defaultValue4',
4495
                ]
4496
            ),
4497
        ];
4498
4499
        return [$existingFields, $fieldDefinitions];
4500
    }
4501
4502
    /**
4503
     * Test for the updateContent() method.
4504
     *
4505
     * Testing more complex cases.
4506
     *
4507
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4508
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4509
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4510
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex
4511
     */
4512
    public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields)
4513
    {
4514
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
4515
4516
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4517
            $initialLanguageCode,
4518
            $structFields,
4519
            $spiFields,
4520
            $existingFields,
4521
            $fieldDefinitions
4522
        );
4523
    }
4524
4525 View Code Duplication
    public function providerForTestUpdateContentWithInvalidLanguage()
4526
    {
4527
        return [
4528
            [
4529
                'eng-GB',
4530
                [
4531
                    new Field(
4532
                        [
4533
                            'fieldDefIdentifier' => 'identifier',
4534
                            'value' => 'newValue',
4535
                            'languageCode' => 'Klingon',
4536
                        ]
4537
                    ),
4538
                ],
4539
            ],
4540
            [
4541
                'Klingon',
4542
                [
4543
                    new Field(
4544
                        [
4545
                            'fieldDefIdentifier' => 'identifier',
4546
                            'value' => 'newValue',
4547
                            'languageCode' => 'eng-GB',
4548
                        ]
4549
                    ),
4550
                ],
4551
            ],
4552
        ];
4553
    }
4554
4555
    /**
4556
     * Test for the updateContent() method.
4557
     *
4558
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4559
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4560
     * @dataProvider providerForTestUpdateContentWithInvalidLanguage
4561
     */
4562
    public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields)
4563
    {
4564
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
4565
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
4566
4567
        $repositoryMock = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repositoryMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4568
        $permissionResolverMock = $this->getPermissionResolverMock();
4569
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4570
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4571
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4572
        $versionInfo = new VersionInfo(
4573
            [
4574
                'contentInfo' => new ContentInfo(
4575
                    [
4576
                        'id' => 42,
4577
                        'contentTypeId' => 24,
4578
                        'mainLanguageCode' => 'eng-GB',
4579
                    ]
4580
                ),
4581
                'versionNo' => 7,
4582
                'languageCodes' => ['eng-GB'],
4583
                'status' => VersionInfo::STATUS_DRAFT,
4584
            ]
4585
        );
4586
        $content = new Content(
4587
            [
4588
                'versionInfo' => $versionInfo,
4589
                'internalFields' => [],
4590
            ]
4591
        );
4592
4593
        $languageHandlerMock->expects($this->any())
4594
            ->method('loadByLanguageCode')
4595
            ->with($this->isType('string'))
4596
            ->will(
4597
                $this->returnCallback(
4598 View Code Duplication
                    function ($languageCode) {
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...
4599
                        if ($languageCode === 'Klingon') {
4600
                            throw new NotFoundException('Language', 'Klingon');
4601
                        }
4602
4603
                        return new Language(['id' => 4242]);
4604
                    }
4605
                )
4606
            );
4607
4608
        $mockedService->expects($this->once())
4609
            ->method('loadContent')
4610
            ->with(
4611
                $this->equalTo(42),
4612
                $this->equalTo(null),
4613
                $this->equalTo(7)
4614
            )->will(
4615
                $this->returnValue($content)
4616
            );
4617
4618
        $permissionResolverMock->expects($this->once())
4619
            ->method('canUser')
4620
            ->with(
4621
                $this->equalTo('content'),
4622
                $this->equalTo('edit'),
4623
                $this->equalTo($content),
4624
                $this->isType('array')
4625
            )->will($this->returnValue(true));
4626
4627
        $contentUpdateStruct = new ContentUpdateStruct(
4628
            [
4629
                'fields' => $structFields,
4630
                'initialLanguageCode' => $initialLanguageCode,
4631
            ]
4632
        );
4633
4634
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4635
    }
4636
4637
    protected function assertForUpdateContentContentValidationException(
4638
        $initialLanguageCode,
4639
        $structFields,
4640
        $fieldDefinitions = []
4641
    ) {
4642
        $repositoryMock = $this->getRepositoryMock();
4643
        $permissionResolverMock = $this->getPermissionResolverMock();
4644
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4645
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4646
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4647
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4648
        $versionInfo = new VersionInfo(
4649
            [
4650
                'contentInfo' => new ContentInfo(
4651
                    [
4652
                        'id' => 42,
4653
                        'contentTypeId' => 24,
4654
                        'mainLanguageCode' => 'eng-GB',
4655
                    ]
4656
                ),
4657
                'versionNo' => 7,
4658
                'languageCodes' => ['eng-GB'],
4659
                'status' => VersionInfo::STATUS_DRAFT,
4660
            ]
4661
        );
4662
        $content = new Content(
4663
            [
4664
                'versionInfo' => $versionInfo,
4665
                'internalFields' => [],
4666
            ]
4667
        );
4668
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4669
4670
        $languageHandlerMock->expects($this->any())
4671
            ->method('loadByLanguageCode')
4672
            ->with($this->isType('string'))
4673
            ->will(
4674
                $this->returnCallback(
4675 View Code Duplication
                    function ($languageCode) {
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...
4676
                        if ($languageCode === 'Klingon') {
4677
                            throw new NotFoundException('Language', 'Klingon');
4678
                        }
4679
4680
                        return new Language(['id' => 4242]);
4681
                    }
4682
                )
4683
            );
4684
4685
        $mockedService->expects($this->once())
4686
            ->method('loadContent')
4687
            ->with(
4688
                $this->equalTo(42),
4689
                $this->equalTo(null),
4690
                $this->equalTo(7)
4691
            )->will(
4692
                $this->returnValue($content)
4693
            );
4694
4695
        $permissionResolverMock->expects($this->once())
4696
            ->method('canUser')
4697
            ->with(
4698
                $this->equalTo('content'),
4699
                $this->equalTo('edit'),
4700
                $this->equalTo($content),
4701
                $this->isType('array')
4702
            )->will($this->returnValue(true));
4703
4704
        $contentTypeServiceMock->expects($this->once())
4705
            ->method('loadContentType')
4706
            ->with($this->equalTo(24))
4707
            ->will($this->returnValue($contentType));
4708
4709
        $repositoryMock->expects($this->once())
4710
            ->method('getContentTypeService')
4711
            ->will($this->returnValue($contentTypeServiceMock));
4712
4713
        $contentUpdateStruct = new ContentUpdateStruct(
4714
            [
4715
                'fields' => $structFields,
4716
                'initialLanguageCode' => $initialLanguageCode,
4717
            ]
4718
        );
4719
4720
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4721
    }
4722
4723 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition()
4724
    {
4725
        return [
4726
            [
4727
                'eng-GB',
4728
                [
4729
                    new Field(
4730
                        [
4731
                            'fieldDefIdentifier' => 'identifier',
4732
                            'value' => 'newValue',
4733
                            'languageCode' => 'eng-GB',
4734
                        ]
4735
                    ),
4736
                ],
4737
            ],
4738
        ];
4739
    }
4740
4741
    /**
4742
     * Test for the updateContent() method.
4743
     *
4744
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4745
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4746
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4747
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition
4748
     */
4749
    public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields)
4750
    {
4751
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4752
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
4753
4754
        $this->assertForUpdateContentContentValidationException(
4755
            $initialLanguageCode,
4756
            $structFields,
4757
            []
4758
        );
4759
    }
4760
4761 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation()
4762
    {
4763
        return [
4764
            [
4765
                'eng-US',
4766
                [
4767
                    new Field(
4768
                        [
4769
                            'fieldDefIdentifier' => 'identifier',
4770
                            'value' => 'newValue',
4771
                            'languageCode' => 'eng-US',
4772
                        ]
4773
                    ),
4774
                ],
4775
            ],
4776
        ];
4777
    }
4778
4779
    /**
4780
     * Test for the updateContent() method.
4781
     *
4782
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4783
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4784
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4785
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation
4786
     */
4787 View Code Duplication
    public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields)
4788
    {
4789
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4790
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
4791
4792
        $fieldDefinitions = [
4793
            new FieldDefinition(
4794
                [
4795
                    'id' => 'fieldDefinitionId1',
4796
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4797
                    'isTranslatable' => false,
4798
                    'identifier' => 'identifier',
4799
                    'isRequired' => false,
4800
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4801
                ]
4802
            ),
4803
        ];
4804
4805
        $this->assertForUpdateContentContentValidationException(
4806
            $initialLanguageCode,
4807
            $structFields,
4808
            $fieldDefinitions
4809
        );
4810
    }
4811
4812
    public function assertForTestUpdateContentRequiredField(
4813
        $initialLanguageCode,
4814
        $structFields,
4815
        $existingFields,
4816
        $fieldDefinitions
4817
    ) {
4818
        $repositoryMock = $this->getRepositoryMock();
4819
        $permissionResolver = $this->getPermissionResolverMock();
4820
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4821
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4822
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4823
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4824
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4825
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
4826
        $existingLanguageCodes = array_map(
4827
            function (Field $field) {
4828
                return $field->languageCode;
4829
            },
4830
            $existingFields
4831
        );
4832
        $versionInfo = new VersionInfo(
4833
            [
4834
                'contentInfo' => new ContentInfo(
4835
                    [
4836
                        'id' => 42,
4837
                        'contentTypeId' => 24,
4838
                        'mainLanguageCode' => 'eng-GB',
4839
                    ]
4840
                ),
4841
                'versionNo' => 7,
4842
                'languageCodes' => $existingLanguageCodes,
4843
                'status' => VersionInfo::STATUS_DRAFT,
4844
            ]
4845
        );
4846
        $content = new Content(
4847
            [
4848
                'versionInfo' => $versionInfo,
4849
                'internalFields' => $existingFields,
4850
            ]
4851
        );
4852
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4853
4854
        $languageHandlerMock->expects($this->any())
4855
            ->method('loadByLanguageCode')
4856
            ->with($this->isType('string'))
4857
            ->will(
4858
                $this->returnCallback(
4859
                    function () {
4860
                        return new Language(['id' => 4242]);
4861
                    }
4862
                )
4863
            );
4864
4865
        $mockedService->expects($this->once())
4866
            ->method('loadContent')
4867
            ->with(
4868
                $this->equalTo(42),
4869
                $this->equalTo(null),
4870
                $this->equalTo(7)
4871
            )->will(
4872
                $this->returnValue($content)
4873
            );
4874
4875
        $permissionResolver->expects($this->once())
4876
            ->method('canUser')
4877
            ->with(
4878
                $this->equalTo('content'),
4879
                $this->equalTo('edit'),
4880
                $this->equalTo($content),
4881
                $this->isType('array')
4882
            )->will($this->returnValue(true));
4883
4884
        $contentTypeServiceMock->expects($this->once())
4885
            ->method('loadContentType')
4886
            ->with($this->equalTo(24))
4887
            ->will($this->returnValue($contentType));
4888
4889
        $repositoryMock->expects($this->once())
4890
            ->method('getContentTypeService')
4891
            ->will($this->returnValue($contentTypeServiceMock));
4892
4893
        $fieldTypeMock->expects($this->any())
4894
            ->method('acceptValue')
4895
            ->will(
4896
                $this->returnCallback(
4897
                    function ($valueString) {
4898
                        return new ValueStub($valueString);
4899
                    }
4900
                )
4901
            );
4902
4903
        $emptyValue = self::EMPTY_FIELD_VALUE;
4904
        $fieldTypeMock->expects($this->any())
4905
            ->method('isEmptyValue')
4906
            ->will(
4907
                $this->returnCallback(
4908
                    function (ValueStub $value) use ($emptyValue) {
4909
                        return $emptyValue === (string)$value;
4910
                    }
4911
                )
4912
            );
4913
4914
        $fieldTypeMock->expects($this->any())
4915
            ->method('validate')
4916
            ->with(
4917
                $this->isInstanceOf(APIFieldDefinition::class),
4918
                $this->isInstanceOf(Value::class)
4919
            );
4920
4921
        $this->getFieldTypeRegistryMock()->expects($this->any())
4922
            ->method('getFieldType')
4923
            ->will($this->returnValue($fieldTypeMock));
4924
4925
        $contentUpdateStruct = new ContentUpdateStruct(
4926
            [
4927
                'fields' => $structFields,
4928
                'initialLanguageCode' => $initialLanguageCode,
4929
            ]
4930
        );
4931
4932
        return [$content->versionInfo, $contentUpdateStruct];
4933
    }
4934
4935 View Code Duplication
    public function providerForTestUpdateContentRequiredField()
4936
    {
4937
        return [
4938
            [
4939
                'eng-US',
4940
                [
4941
                    new Field(
4942
                        [
4943
                            'fieldDefIdentifier' => 'identifier',
4944
                            'value' => self::EMPTY_FIELD_VALUE,
4945
                            'languageCode' => null,
4946
                        ]
4947
                    ),
4948
                ],
4949
                'identifier',
4950
                'eng-US',
4951
            ],
4952
        ];
4953
    }
4954
4955
    /**
4956
     * Test for the updateContent() method.
4957
     *
4958
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4959
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4960
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4961
     * @dataProvider providerForTestUpdateContentRequiredField
4962
     */
4963
    public function testUpdateContentRequiredField(
4964
        $initialLanguageCode,
4965
        $structFields,
4966
        $identifier,
4967
        $languageCode
4968
    ) {
4969
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
4970
4971
        $existingFields = [
4972
            new Field(
4973
                [
4974
                    'id' => '100',
4975
                    'fieldDefIdentifier' => 'identifier',
4976
                    'value' => 'initialValue',
4977
                    'languageCode' => 'eng-GB',
4978
                ]
4979
            ),
4980
        ];
4981
        $fieldDefinitions = [
4982
            new FieldDefinition(
4983
                [
4984
                    'id' => 'fieldDefinitionId',
4985
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4986
                    'isTranslatable' => true,
4987
                    'identifier' => 'identifier',
4988
                    'isRequired' => true,
4989
                    'defaultValue' => 'defaultValue',
4990
                ]
4991
            ),
4992
        ];
4993
        list($versionInfo, $contentUpdateStruct) =
4994
            $this->assertForTestUpdateContentRequiredField(
4995
                $initialLanguageCode,
4996
                $structFields,
4997
                $existingFields,
4998
                $fieldDefinitions
4999
            );
5000
5001
        try {
5002
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5003
        } catch (ContentValidationException $e) {
5004
            $this->assertEquals(
5005
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
5006
                $e->getMessage()
5007
            );
5008
5009
            throw $e;
5010
        }
5011
    }
5012
5013
    public function assertForTestUpdateContentThrowsContentFieldValidationException(
5014
        $initialLanguageCode,
5015
        $structFields,
5016
        $existingFields,
5017
        $fieldDefinitions
5018
    ) {
5019
        $repositoryMock = $this->getRepositoryMock();
5020
        $permissionResolverMock = $this->getPermissionResolverMock();
5021
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
5022
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
5023
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
5024
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5025
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
5026
        $existingLanguageCodes = array_map(
5027
            function (Field $field) {
5028
                return $field->languageCode;
5029
            },
5030
            $existingFields
5031
        );
5032
        $languageCodes = $this->determineLanguageCodesForUpdate(
5033
            $initialLanguageCode,
5034
            $structFields,
5035
            $existingLanguageCodes
5036
        );
5037
        $versionInfo = new VersionInfo(
5038
            [
5039
                'contentInfo' => new ContentInfo(
5040
                    [
5041
                        'id' => 42,
5042
                        'contentTypeId' => 24,
5043
                        'mainLanguageCode' => 'eng-GB',
5044
                    ]
5045
                ),
5046
                'versionNo' => 7,
5047
                'languageCodes' => $existingLanguageCodes,
5048
                'status' => VersionInfo::STATUS_DRAFT,
5049
            ]
5050
        );
5051
        $content = new Content(
5052
            [
5053
                'versionInfo' => $versionInfo,
5054
                'internalFields' => $existingFields,
5055
            ]
5056
        );
5057
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
5058
5059
        $languageHandlerMock->expects($this->any())
5060
            ->method('loadByLanguageCode')
5061
            ->with($this->isType('string'))
5062
            ->will(
5063
                $this->returnCallback(
5064
                    function () {
5065
                        return new Language(['id' => 4242]);
5066
                    }
5067
                )
5068
            );
5069
5070
        $mockedService->expects($this->once())
5071
            ->method('loadContent')
5072
            ->with(
5073
                $this->equalTo(42),
5074
                $this->equalTo(null),
5075
                $this->equalTo(7)
5076
            )->will(
5077
                $this->returnValue($content)
5078
            );
5079
5080
        $permissionResolverMock->expects($this->once())
5081
            ->method('canUser')
5082
            ->with(
5083
                $this->equalTo('content'),
5084
                $this->equalTo('edit'),
5085
                $this->equalTo($content),
5086
                $this->isType('array')
5087
            )->will($this->returnValue(true));
5088
5089
        $contentTypeServiceMock->expects($this->once())
5090
            ->method('loadContentType')
5091
            ->with($this->equalTo(24))
5092
            ->will($this->returnValue($contentType));
5093
5094
        $repositoryMock->expects($this->once())
5095
            ->method('getContentTypeService')
5096
            ->will($this->returnValue($contentTypeServiceMock));
5097
5098
        $fieldValues = $this->determineValuesForUpdate(
5099
            $initialLanguageCode,
5100
            $structFields,
5101
            $content,
5102
            $fieldDefinitions,
5103
            $languageCodes
5104
        );
5105
        $allFieldErrors = [];
5106
        $emptyValue = self::EMPTY_FIELD_VALUE;
5107
5108
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5109
            ->method('acceptValue')
5110
            ->will(
5111
                $this->returnCallback(
5112
                    function ($valueString) {
5113
                        return new ValueStub($valueString);
5114
                    }
5115
                )
5116
            );
5117
5118
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5119
            ->method('isEmptyValue')
5120
            ->will(
5121
                $this->returnCallback(
5122
                    function (ValueStub $value) use ($emptyValue) {
5123
                        return $emptyValue === (string)$value;
5124
                    }
5125
                )
5126
            );
5127
5128
        $fieldTypeMock
5129
            ->expects($this->any())
5130
            ->method('validate')
5131
            ->willReturnArgument(1);
5132
5133
        $this->getFieldTypeRegistryMock()->expects($this->any())
5134
            ->method('getFieldType')
5135
            ->will($this->returnValue($fieldTypeMock));
5136
5137
        $contentUpdateStruct = new ContentUpdateStruct(
5138
            [
5139
                'fields' => $structFields,
5140
                'initialLanguageCode' => $initialLanguageCode,
5141
            ]
5142
        );
5143
5144
        return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors];
5145
    }
5146
5147
    public function providerForTestUpdateContentThrowsContentFieldValidationException()
5148
    {
5149
        $allFieldErrors = [
5150
            [
5151
                'fieldDefinitionId1' => [
5152
                    'eng-GB' => 'newValue1-eng-GB',
5153
                    'eng-US' => 'newValue1-eng-GB',
5154
                ],
5155
                'fieldDefinitionId2' => [
5156
                    'eng-GB' => 'initialValue2',
5157
                ],
5158
                'fieldDefinitionId3' => [
5159
                    'eng-GB' => 'initialValue3',
5160
                    'eng-US' => 'initialValue3',
5161
                ],
5162
                'fieldDefinitionId4' => [
5163
                    'eng-GB' => 'initialValue4',
5164
                    'eng-US' => 'newValue4',
5165
                ],
5166
            ],
5167
            [
5168
                'fieldDefinitionId1' => [
5169
                    'eng-GB' => 'newValue1-eng-GB',
5170
                    'eng-US' => 'newValue1-eng-GB',
5171
                ],
5172
                'fieldDefinitionId2' => [
5173
                    'eng-GB' => 'initialValue2',
5174
                ],
5175
                'fieldDefinitionId3' => [
5176
                    'eng-GB' => 'initialValue3',
5177
                    'eng-US' => 'initialValue3',
5178
                ],
5179
                'fieldDefinitionId4' => [
5180
                    'eng-GB' => 'initialValue4',
5181
                    'eng-US' => 'newValue4',
5182
                ],
5183
            ],
5184
            [
5185
                'fieldDefinitionId1' => [
5186
                    'eng-GB' => 'newValue1-eng-GB',
5187
                    'eng-US' => 'newValue1-eng-GB',
5188
                ],
5189
                'fieldDefinitionId2' => [
5190
                    'eng-GB' => 'initialValue2',
5191
                    'eng-US' => 'newValue2',
5192
                ],
5193
                'fieldDefinitionId3' => [
5194
                    'eng-GB' => 'initialValue3',
5195
                    'eng-US' => 'initialValue3',
5196
                ],
5197
                'fieldDefinitionId4' => [
5198
                    'eng-GB' => 'initialValue4',
5199
                    'eng-US' => 'defaultValue4',
5200
                ],
5201
            ],
5202
            [
5203
                'fieldDefinitionId1' => [
5204
                    'eng-GB' => 'newValue1-eng-GB',
5205
                    'eng-US' => 'newValue1-eng-GB',
5206
                ],
5207
                'fieldDefinitionId2' => [
5208
                    'eng-GB' => 'initialValue2',
5209
                    'eng-US' => 'newValue2',
5210
                ],
5211
                'fieldDefinitionId3' => [
5212
                    'eng-GB' => 'initialValue3',
5213
                    'eng-US' => 'initialValue3',
5214
                ],
5215
                'fieldDefinitionId4' => [
5216
                    'eng-GB' => 'initialValue4',
5217
                    'eng-US' => 'defaultValue4',
5218
                ],
5219
            ],
5220
            [
5221
                'fieldDefinitionId1' => [
5222
                    'eng-GB' => 'newValue1-eng-GB',
5223
                    'ger-DE' => 'newValue1-eng-GB',
5224
                    'eng-US' => 'newValue1-eng-GB',
5225
                ],
5226
                'fieldDefinitionId2' => [
5227
                    'eng-GB' => 'initialValue2',
5228
                    'eng-US' => 'newValue2',
5229
                ],
5230
                'fieldDefinitionId3' => [
5231
                    'eng-GB' => 'initialValue3',
5232
                    'ger-DE' => 'initialValue3',
5233
                    'eng-US' => 'initialValue3',
5234
                ],
5235
                'fieldDefinitionId4' => [
5236
                    'eng-GB' => 'initialValue4',
5237
                    'eng-US' => 'defaultValue4',
5238
                    'ger-DE' => 'defaultValue4',
5239
                ],
5240
            ],
5241
            [
5242
                'fieldDefinitionId1' => [
5243
                    'eng-US' => 'newValue1-eng-GB',
5244
                    'ger-DE' => 'newValue1-eng-GB',
5245
                ],
5246
                'fieldDefinitionId2' => [
5247
                    'eng-US' => 'newValue2',
5248
                ],
5249
                'fieldDefinitionId3' => [
5250
                    'ger-DE' => 'initialValue3',
5251
                    'eng-US' => 'initialValue3',
5252
                ],
5253
                'fieldDefinitionId4' => [
5254
                    'ger-DE' => 'defaultValue4',
5255
                    'eng-US' => 'defaultValue4',
5256
                ],
5257
            ],
5258
        ];
5259
5260
        $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex();
5261
        $count = count($data);
5262
        for ($i = 0; $i < $count; ++$i) {
5263
            $data[$i][] = $allFieldErrors[$i];
5264
        }
5265
5266
        return $data;
5267
    }
5268
5269
    /**
5270
     * Test for the updateContent() method.
5271
     *
5272
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5273
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5274
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5275
     * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException
5276
     */
5277 View Code Duplication
    public function testUpdateContentThrowsContentFieldValidationException($initialLanguageCode, $structFields, $spiField, $allFieldErrors)
5278
    {
5279
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
5280
        $this->expectExceptionMessage('Content fields did not validate');
5281
5282
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
5283
        list($versionInfo, $contentUpdateStruct) =
5284
            $this->assertForTestUpdateContentThrowsContentFieldValidationException(
5285
                $initialLanguageCode,
5286
                $structFields,
5287
                $existingFields,
5288
                $fieldDefinitions
5289
            );
5290
5291
        try {
5292
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5293
        } catch (ContentFieldValidationException $e) {
5294
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
5295
            throw $e;
5296
        }
5297
    }
5298
5299
    /**
5300
     * Test for the updateContent() method.
5301
     *
5302
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5303
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5304
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5305
     */
5306
    public function testUpdateContentTransactionRollback()
5307
    {
5308
        $this->expectException(\Exception::class);
5309
        $this->expectExceptionMessage('Store failed');
5310
5311
        $existingFields = [
5312
            new Field(
5313
                [
5314
                    'id' => '100',
5315
                    'fieldDefIdentifier' => 'identifier',
5316
                    'value' => 'initialValue',
5317
                    'languageCode' => 'eng-GB',
5318
                ]
5319
            ),
5320
        ];
5321
5322
        $fieldDefinitions = [
5323
            new FieldDefinition(
5324
                [
5325
                    'id' => 'fieldDefinitionId',
5326
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5327
                    'isTranslatable' => false,
5328
                    'identifier' => 'identifier',
5329
                    'isRequired' => false,
5330
                    'defaultValue' => 'defaultValue',
5331
                ]
5332
            ),
5333
        ];
5334
5335
        // Setup a simple case that will pass
5336
        list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet(
5337
            'eng-US',
5338
            [],
5339
            [],
5340
            $existingFields,
5341
            $fieldDefinitions,
5342
            // Do not execute test
5343
            false
5344
        );
5345
5346
        $repositoryMock = $this->getRepositoryMock();
5347
        $repositoryMock->expects($this->never())->method('commit');
5348
        $repositoryMock->expects($this->once())->method('rollback');
5349
5350
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5351
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5352
        $contentHandlerMock->expects($this->once())
5353
            ->method('updateContent')
5354
            ->with(
5355
                $this->anything(),
5356
                $this->anything(),
5357
                $this->anything()
5358
            )->will($this->throwException(new \Exception('Store failed')));
5359
5360
        // Execute
5361
        $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5362
    }
5363
5364
    /**
5365
     * Test for the copyContent() method.
5366
     *
5367
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5368
     */
5369
    public function testCopyContentThrowsUnauthorizedException()
5370
    {
5371
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
5372
5373
        $repository = $this->getRepositoryMock();
5374
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
5375
        $contentInfo = $this->createMock(APIContentInfo::class);
5376
        $locationCreateStruct = new LocationCreateStruct();
5377
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5378
        $locationServiceMock = $this->getLocationServiceMock();
5379
        $permissionResolver = $this->getPermissionResolverMock();
5380
5381
        $repository->expects($this->once())
5382
            ->method('getLocationService')
5383
            ->will($this->returnValue($locationServiceMock))
5384
        ;
5385
5386
        $locationServiceMock->expects($this->once())
5387
            ->method('loadLocation')
5388
            ->with(
5389
                $locationCreateStruct->parentLocationId
5390
            )
5391
            ->will($this->returnValue($location))
5392
        ;
5393
5394
        $contentInfo->expects($this->any())
5395
            ->method('__get')
5396
            ->with('sectionId')
5397
            ->will($this->returnValue(42));
5398
5399
        $permissionResolver
5400
            ->method('canUser')
5401
            ->with(
5402
                'content',
5403
                'create',
5404
                $contentInfo,
5405
                [$location]
5406
            )
5407
            ->will($this->returnValue(false));
5408
5409
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
5410
        $contentService->copyContent($contentInfo, $locationCreateStruct);
5411
    }
5412
5413
    /**
5414
     * Test for the copyContent() method.
5415
     *
5416
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5417
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5418
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5419
     */
5420
    public function testCopyContent()
5421
    {
5422
        $repositoryMock = $this->getRepositoryMock();
5423
        $contentService = $this->getPartlyMockedContentService([
5424
            'internalLoadContentInfo',
5425
            'internalLoadContent',
5426
            'getUnixTimestamp',
5427
        ]);
5428
        $locationServiceMock = $this->getLocationServiceMock();
5429
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5430
        $locationCreateStruct = new LocationCreateStruct();
5431
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5432
        $user = $this->getStubbedUser(14);
5433
5434
        $permissionResolverMock = $this->getPermissionResolverMock();
5435
5436
        $permissionResolverMock
5437
            ->method('getCurrentUserReference')
5438
            ->willReturn($user);
5439
5440
        $repositoryMock
5441
            ->method('getPermissionResolver')
5442
            ->willReturn($permissionResolverMock);
5443
5444
        $repositoryMock->expects($this->exactly(3))
5445
            ->method('getLocationService')
5446
            ->will($this->returnValue($locationServiceMock));
5447
5448
        $locationServiceMock->expects($this->once())
5449
            ->method('loadLocation')
5450
            ->with($locationCreateStruct->parentLocationId)
5451
            ->will($this->returnValue($location))
5452
        ;
5453
5454
        $contentInfoMock->expects($this->any())
5455
            ->method('__get')
5456
            ->with('id')
5457
            ->will($this->returnValue(42));
5458
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5459
5460
        $versionInfoMock->expects($this->any())
5461
            ->method('__get')
5462
            ->will(
5463
                $this->returnValueMap(
5464
                    [
5465
                        ['versionNo', 123],
5466
                    ]
5467
                )
5468
            );
5469
5470
        $versionInfoMock->expects($this->once())
5471
            ->method('isDraft')
5472
            ->willReturn(true);
5473
5474
        $versionInfoMock->expects($this->once())
5475
            ->method('getContentInfo')
5476
            ->will($this->returnValue($contentInfoMock));
5477
5478
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5479
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5480
        $domainMapperMock = $this->getDomainMapperMock();
5481
5482
        $repositoryMock->expects($this->once())->method('beginTransaction');
5483
        $repositoryMock->expects($this->once())->method('commit');
5484
5485
        $permissionResolverMock
5486
            ->method('canUser')
5487
            ->willReturnMap(
5488
                [
5489
                    ['content', 'create', $contentInfoMock, [$location], true],
5490
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5491
                ]
5492
            );
5493
5494
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5495
        $spiVersionInfo = new SPIVersionInfo(
5496
            [
5497
                'contentInfo' => $spiContentInfo,
5498
                'creationDate' => 123456,
5499
            ]
5500
        );
5501
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5502
        $contentHandlerMock->expects($this->once())
5503
            ->method('copy')
5504
            ->with(42, null)
5505
            ->will($this->returnValue($spiContent));
5506
5507
        $this->mockGetDefaultObjectStates();
5508
        $this->mockSetDefaultObjectStates();
5509
5510
        $domainMapperMock->expects($this->once())
5511
            ->method('buildVersionInfoDomainObject')
5512
            ->with($spiVersionInfo)
5513
            ->will($this->returnValue($versionInfoMock));
5514
5515
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5516
        $content = $this->mockPublishVersion(123456, 126666);
5517
        $locationServiceMock->expects($this->once())
5518
            ->method('createLocation')
5519
            ->with(
5520
                $content->getVersionInfo()->getContentInfo(),
5521
                $locationCreateStruct
5522
            );
5523
5524
        $contentService->expects($this->once())
5525
            ->method('internalLoadContent')
5526
            ->with(
5527
                $content->id
5528
            )
5529
            ->will($this->returnValue($content));
5530
5531
        $contentService->expects($this->once())
5532
            ->method('getUnixTimestamp')
5533
            ->will($this->returnValue(126666));
5534
5535
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5536
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5537
    }
5538
5539
    /**
5540
     * Test for the copyContent() method.
5541
     *
5542
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5543
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5544
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5545
     */
5546
    public function testCopyContentWithVersionInfo()
5547
    {
5548
        $repositoryMock = $this->getRepositoryMock();
5549
        $contentService = $this->getPartlyMockedContentService([
5550
            'internalLoadContentInfo',
5551
            'internalLoadContent',
5552
            'getUnixTimestamp',
5553
        ]);
5554
        $locationServiceMock = $this->getLocationServiceMock();
5555
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5556
        $locationCreateStruct = new LocationCreateStruct();
5557
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5558
        $user = $this->getStubbedUser(14);
5559
5560
        $permissionResolverMock = $this->getPermissionResolverMock();
5561
5562
        $permissionResolverMock
5563
            ->method('getCurrentUserReference')
5564
            ->willReturn($user);
5565
5566
        $repositoryMock
5567
            ->method('getPermissionResolver')
5568
            ->willReturn($permissionResolverMock);
5569
5570
        $repositoryMock->expects($this->exactly(3))
5571
            ->method('getLocationService')
5572
            ->will($this->returnValue($locationServiceMock));
5573
5574
        $locationServiceMock->expects($this->once())
5575
            ->method('loadLocation')
5576
            ->with($locationCreateStruct->parentLocationId)
5577
            ->will($this->returnValue($location))
5578
        ;
5579
5580
        $contentInfoMock->expects($this->any())
5581
            ->method('__get')
5582
            ->with('id')
5583
            ->will($this->returnValue(42));
5584
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5585
5586
        $versionInfoMock->expects($this->any())
5587
            ->method('__get')
5588
            ->will(
5589
                $this->returnValueMap(
5590
                    [
5591
                        ['versionNo', 123],
5592
                    ]
5593
                )
5594
            );
5595
        $versionInfoMock->expects($this->once())
5596
            ->method('isDraft')
5597
            ->willReturn(true);
5598
        $versionInfoMock->expects($this->once())
5599
            ->method('getContentInfo')
5600
            ->will($this->returnValue($contentInfoMock));
5601
5602
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5603
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5604
        $domainMapperMock = $this->getDomainMapperMock();
5605
5606
        $repositoryMock->expects($this->once())->method('beginTransaction');
5607
        $repositoryMock->expects($this->once())->method('commit');
5608
5609
        $permissionResolverMock
5610
            ->method('canUser')
5611
            ->willReturnMap(
5612
                [
5613
                    ['content', 'create', $contentInfoMock, [$location], true],
5614
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5615
                ]
5616
            );
5617
5618
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5619
        $spiVersionInfo = new SPIVersionInfo(
5620
            [
5621
                'contentInfo' => $spiContentInfo,
5622
                'creationDate' => 123456,
5623
            ]
5624
        );
5625
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5626
        $contentHandlerMock->expects($this->once())
5627
            ->method('copy')
5628
            ->with(42, 123)
5629
            ->will($this->returnValue($spiContent));
5630
5631
        $this->mockGetDefaultObjectStates();
5632
        $this->mockSetDefaultObjectStates();
5633
5634
        $domainMapperMock->expects($this->once())
5635
            ->method('buildVersionInfoDomainObject')
5636
            ->with($spiVersionInfo)
5637
            ->will($this->returnValue($versionInfoMock));
5638
5639
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5640
        $content = $this->mockPublishVersion(123456, 126666);
5641
        $locationServiceMock->expects($this->once())
5642
            ->method('createLocation')
5643
            ->with(
5644
                $content->getVersionInfo()->getContentInfo(),
5645
                $locationCreateStruct
5646
            );
5647
5648
        $contentService->expects($this->once())
5649
            ->method('internalLoadContent')
5650
            ->with(
5651
                $content->id
5652
            )
5653
            ->will($this->returnValue($content));
5654
5655
        $contentService->expects($this->once())
5656
            ->method('getUnixTimestamp')
5657
            ->will($this->returnValue(126666));
5658
5659
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5660
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock);
5661
    }
5662
5663
    /**
5664
     * Test for the copyContent() method.
5665
     *
5666
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5667
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5668
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5669
     */
5670
    public function testCopyContentWithRollback()
5671
    {
5672
        $this->expectException(\Exception::class);
5673
        $this->expectExceptionMessage('Handler threw an exception');
5674
5675
        $repositoryMock = $this->getRepositoryMock();
5676
        $contentService = $this->getPartlyMockedContentService();
5677
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5678
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5679
        $locationCreateStruct = new LocationCreateStruct();
5680
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5681
        $locationServiceMock = $this->getLocationServiceMock();
5682
        $user = $this->getStubbedUser(14);
5683
5684
        $permissionResolverMock = $this->getPermissionResolverMock();
5685
5686
        $permissionResolverMock
5687
            ->method('getCurrentUserReference')
5688
            ->willReturn($user);
5689
5690
        $repositoryMock
5691
            ->method('getPermissionResolver')
5692
            ->willReturn($permissionResolverMock);
5693
5694
        $repositoryMock->expects($this->once())
5695
            ->method('getLocationService')
5696
            ->will($this->returnValue($locationServiceMock))
5697
        ;
5698
5699
        $locationServiceMock->expects($this->once())
5700
            ->method('loadLocation')
5701
            ->with($locationCreateStruct->parentLocationId)
5702
            ->will($this->returnValue($location))
5703
        ;
5704
5705
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5706
        $contentInfoMock->expects($this->any())
5707
            ->method('__get')
5708
            ->with('id')
5709
            ->will($this->returnValue(42));
5710
5711
        $this->mockGetDefaultObjectStates();
5712
5713
        $repositoryMock->expects($this->once())->method('beginTransaction');
5714
        $repositoryMock->expects($this->once())->method('rollback');
5715
5716
        $permissionResolverMock
5717
            ->method('canUser')
5718
            ->willReturnMap(
5719
                [
5720
                    ['content', 'create', $contentInfoMock, [$location], true],
5721
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5722
                ]
5723
            );
5724
5725
        $contentHandlerMock->expects($this->once())
5726
            ->method('copy')
5727
            ->with(42, null)
5728
            ->will($this->throwException(new Exception('Handler threw an exception')));
5729
5730
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5731
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5732
    }
5733
5734
    /**
5735
     * Reusable method for setting exceptions on buildContentDomainObject usage.
5736
     *
5737
     * Plain usage as in when content type is loaded directly.
5738
     *
5739
     * @param \eZ\Publish\SPI\Persistence\Content $spiContent
5740
     * @param array $translations
5741
     * @param bool $useAlwaysAvailable
5742
     *
5743
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Values\Content\Content
5744
     */
5745
    private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null)
5746
    {
5747
        $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId;
5748
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5749
        $repositoryMock = $this->getRepositoryMock();
5750
5751
        $contentType = new ContentType([
5752
            'id' => $contentTypeId,
5753
            'fieldDefinitions' => [],
5754
        ]);
5755
5756
        $repositoryMock->expects($this->once())
5757
            ->method('getContentTypeService')
5758
            ->willReturn($contentTypeServiceMock);
5759
5760
        $contentTypeServiceMock->expects($this->once())
5761
            ->method('loadContentType')
5762
            ->with($this->equalTo($contentTypeId))
5763
            ->willReturn($contentType);
5764
5765
        $content = $this->createMock(APIContent::class);
5766
5767
        $this->getDomainMapperMock()
5768
            ->expects($this->once())
5769
            ->method('buildContentDomainObject')
5770
            ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable)
5771
            ->willReturn($content);
5772
5773
        return $content;
5774
    }
5775
5776
    protected function mockGetDefaultObjectStates()
5777
    {
5778
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5779
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5780
5781
        $objectStateGroups = [
5782
            new SPIObjectStateGroup(['id' => 10]),
5783
            new SPIObjectStateGroup(['id' => 20]),
5784
        ];
5785
5786
        /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5787
        $objectStateHandlerMock->expects($this->once())
5788
            ->method('loadAllGroups')
5789
            ->will($this->returnValue($objectStateGroups));
5790
5791
        $objectStateHandlerMock->expects($this->at(1))
5792
            ->method('loadObjectStates')
5793
            ->with($this->equalTo(10))
5794
            ->will(
5795
                $this->returnValue(
5796
                    [
5797
                        new SPIObjectState(['id' => 11, 'groupId' => 10]),
5798
                        new SPIObjectState(['id' => 12, 'groupId' => 10]),
5799
                    ]
5800
                )
5801
            );
5802
5803
        $objectStateHandlerMock->expects($this->at(2))
5804
            ->method('loadObjectStates')
5805
            ->with($this->equalTo(20))
5806
            ->will(
5807
                $this->returnValue(
5808
                    [
5809
                        new SPIObjectState(['id' => 21, 'groupId' => 20]),
5810
                        new SPIObjectState(['id' => 22, 'groupId' => 20]),
5811
                    ]
5812
                )
5813
            );
5814
    }
5815
5816
    protected function mockSetDefaultObjectStates()
5817
    {
5818
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5819
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5820
5821
        $defaultObjectStates = [
5822
            new SPIObjectState(['id' => 11, 'groupId' => 10]),
5823
            new SPIObjectState(['id' => 21, 'groupId' => 20]),
5824
        ];
5825
        foreach ($defaultObjectStates as $index => $objectState) {
5826
            $objectStateHandlerMock->expects($this->at($index + 3))
5827
                ->method('setContentState')
5828
                ->with(
5829
                    42,
5830
                    $objectState->groupId,
5831
                    $objectState->id
5832
                );
5833
        }
5834
    }
5835
5836
    /**
5837
     * @param int|null $publicationDate
5838
     * @param int|null $modificationDate
5839
     *
5840
     * @return \eZ\Publish\API\Repository\Values\Content\Content
5841
     */
5842
    protected function mockPublishVersion($publicationDate = null, $modificationDate = null)
5843
    {
5844
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5845
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5846
        /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5847
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5848
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
5849
5850
        $spiContent = new SPIContent([
5851
            'versionInfo' => new VersionInfo([
5852
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
5853
            ]),
5854
        ]);
5855
5856
        $contentMock = $this->mockBuildContentDomainObject($spiContent);
5857
        $contentMock->expects($this->any())
5858
            ->method('__get')
5859
            ->will(
5860
                $this->returnValueMap(
5861
                    [
5862
                        ['id', 42],
5863
                        ['contentInfo', $contentInfoMock],
5864
                        ['versionInfo', $versionInfoMock],
5865
                    ]
5866
                )
5867
            );
5868
        $contentMock->expects($this->any())
5869
            ->method('getVersionInfo')
5870
            ->will($this->returnValue($versionInfoMock));
5871
        $versionInfoMock->expects($this->any())
5872
            ->method('getContentInfo')
5873
            ->will($this->returnValue($contentInfoMock));
5874
        $versionInfoMock->expects($this->any())
5875
            ->method('__get')
5876
            ->will(
5877
                $this->returnValueMap(
5878
                    [
5879
                        ['languageCodes', ['eng-GB']],
5880
                    ]
5881
                )
5882
            );
5883
        $contentInfoMock->expects($this->any())
5884
            ->method('__get')
5885
            ->will(
5886
                $this->returnValueMap(
5887
                    [
5888
                        ['alwaysAvailable', true],
5889
                        ['mainLanguageCode', 'eng-GB'],
5890
                    ]
5891
                )
5892
            );
5893
5894
        $currentTime = time();
5895
        if ($publicationDate === null && $versionInfoMock->versionNo === 1) {
5896
            $publicationDate = $currentTime;
5897
        }
5898
5899
        // Account for 1 second of test execution time
5900
        $metadataUpdateStruct->publicationDate = $publicationDate;
5901
        $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime;
5902
5903
        $contentHandlerMock->expects($this->once())
5904
            ->method('publish')
5905
            ->with(
5906
                42,
5907
                123,
5908
                $metadataUpdateStruct
5909
            )
5910
            ->will($this->returnValue($spiContent));
5911
5912
        /* @var \eZ\Publish\API\Repository\Values\Content\Content $contentMock */
5913
        $this->mockPublishUrlAliasesForContent($contentMock);
5914
5915
        return $contentMock;
5916
    }
5917
5918
    /**
5919
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
5920
     */
5921
    protected function mockPublishUrlAliasesForContent(APIContent $content)
5922
    {
5923
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
5924
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */
5925
        $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler();
5926
        $locationServiceMock = $this->getLocationServiceMock();
5927
        $location = $this->createMock(APILocation::class);
5928
5929
        $location->expects($this->at(0))
5930
            ->method('__get')
5931
            ->with('id')
5932
            ->will($this->returnValue(123));
5933
        $location->expects($this->at(1))
5934
            ->method('__get')
5935
            ->with('parentLocationId')
5936
            ->will($this->returnValue(456));
5937
5938
        $urlAliasNames = ['eng-GB' => 'hello'];
5939
        $nameSchemaServiceMock->expects($this->once())
5940
            ->method('resolveUrlAliasSchema')
5941
            ->with($content)
5942
            ->will($this->returnValue($urlAliasNames));
5943
5944
        $locationServiceMock->expects($this->once())
5945
            ->method('loadLocations')
5946
            ->with($content->getVersionInfo()->getContentInfo())
5947
            ->will($this->returnValue([$location]));
5948
5949
        $urlAliasHandlerMock->expects($this->once())
5950
            ->method('publishUrlAliasForLocation')
5951
            ->with(123, 456, 'hello', 'eng-GB', true, true);
5952
5953
        $location->expects($this->at(2))
5954
            ->method('__get')
5955
            ->with('id')
5956
            ->will($this->returnValue(123));
5957
5958
        $location->expects($this->at(3))
5959
            ->method('__get')
5960
            ->with('parentLocationId')
5961
            ->will($this->returnValue(456));
5962
5963
        $urlAliasHandlerMock->expects($this->once())
5964
            ->method('archiveUrlAliasesForDeletedTranslations')
5965
            ->with(123, 456, ['eng-GB']);
5966
    }
5967
5968
    protected $domainMapperMock;
5969
5970
    /**
5971
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\DomainMapper
5972
     */
5973
    protected function getDomainMapperMock()
5974
    {
5975
        if (!isset($this->domainMapperMock)) {
5976
            $this->domainMapperMock = $this->createMock(DomainMapper::class);
5977
        }
5978
5979
        return $this->domainMapperMock;
5980
    }
5981
5982
    protected $relationProcessorMock;
5983
5984
    /**
5985
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\RelationProcessor
5986
     */
5987
    protected function getRelationProcessorMock()
5988
    {
5989
        if (!isset($this->relationProcessorMock)) {
5990
            $this->relationProcessorMock = $this->createMock(RelationProcessor::class);
5991
        }
5992
5993
        return $this->relationProcessorMock;
5994
    }
5995
5996
    protected $nameSchemaServiceMock;
5997
5998
    /**
5999
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService
6000
     */
6001
    protected function getNameSchemaServiceMock()
6002
    {
6003
        if (!isset($this->nameSchemaServiceMock)) {
6004
            $this->nameSchemaServiceMock = $this->createMock(NameSchemaService::class);
6005
        }
6006
6007
        return $this->nameSchemaServiceMock;
6008
    }
6009
6010
    protected $contentTypeServiceMock;
6011
6012
    /**
6013
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentTypeService
6014
     */
6015
    protected function getContentTypeServiceMock()
6016
    {
6017
        if (!isset($this->contentTypeServiceMock)) {
6018
            $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class);
6019
        }
6020
6021
        return $this->contentTypeServiceMock;
6022
    }
6023
6024
    protected $locationServiceMock;
6025
6026
    /**
6027
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\LocationService
6028
     */
6029
    protected function getLocationServiceMock()
6030
    {
6031
        if (!isset($this->locationServiceMock)) {
6032
            $this->locationServiceMock = $this->createMock(APILocationService::class);
6033
        }
6034
6035
        return $this->locationServiceMock;
6036
    }
6037
6038
    /** @var \eZ\Publish\Core\Repository\ContentService */
6039
    protected $partlyMockedContentService;
6040
6041
    /**
6042
     * Returns the content service to test with $methods mocked.
6043
     *
6044
     * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()}
6045
     *
6046
     * @param string[] $methods
6047
     *
6048
     * @return \eZ\Publish\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject
6049
     */
6050
    protected function getPartlyMockedContentService(array $methods = null)
6051
    {
6052
        if (!isset($this->partlyMockedContentService)) {
6053
            $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class)
6054
                ->setMethods($methods)
6055
                ->setConstructorArgs(
6056
                    [
6057
                        $this->getRepositoryMock(),
6058
                        $this->getPersistenceMock(),
6059
                        $this->getDomainMapperMock(),
6060
                        $this->getRelationProcessorMock(),
6061
                        $this->getNameSchemaServiceMock(),
6062
                        $this->getFieldTypeRegistryMock(),
6063
                        $this->getPermissionResolverMock(),
6064
                        [],
6065
                    ]
6066
                )
6067
                ->getMock();
6068
        }
6069
6070
        return $this->partlyMockedContentService;
6071
    }
6072
6073
    /**
6074
     * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject
6075
     */
6076
    protected function getRepositoryMock(): Repository
6077
    {
6078
        $repositoryMock = parent::getRepositoryMock();
6079
        $repositoryMock
6080
            ->expects($this->any())
6081
            ->method('getPermissionResolver')
6082
            ->willReturn($this->getPermissionResolverMock());
6083
6084
        return $repositoryMock;
6085
    }
6086
}
6087