Completed
Push — ezp-30928-as_a_developer_i_wan... ( 4d81da...f06b29 )
by
unknown
14:36
created

testLoadVersionInfoByIdAndVersionNumber()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 41
rs 9.264
c 0
b 0
f 0
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, of published version.
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->never())
115
            ->method('loadContentInfo');
116
117
        $contentHandler->expects($this->once())
118
            ->method('loadVersionInfo')
119
            ->with(
120
                $this->equalTo(42),
121
                $this->equalTo(null)
122
            )->will(
123
                $this->returnValue(new SPIVersionInfo())
124
            );
125
126
        $domainMapperMock->expects($this->once())
127
            ->method('buildVersionInfoDomainObject')
128
            ->with(new SPIVersionInfo())
129
            ->will($this->returnValue($versionInfoMock));
130
131
        $permissionResolver->expects($this->once())
132
            ->method('canUser')
133
            ->with(
134
                $this->equalTo('content'),
135
                $this->equalTo('read'),
136
                $this->equalTo($versionInfoMock)
137
            )->will($this->returnValue(true));
138
139
        $result = $contentServiceMock->loadVersionInfoById(42);
140
141
        $this->assertEquals($versionInfoMock, $result);
142
    }
143
144
    /**
145
     * Test for the loadVersionInfo() method, of a draft.
146
     *
147
     * @depends testLoadVersionInfoById
148
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
149
     */
150
    public function testLoadVersionInfoByIdAndVersionNumber()
151
    {
152
        $repository = $this->getRepositoryMock();
153
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
154
        /** @var \PHPUnit_Framework_MockObject_MockObject $contentHandler */
155
        $contentHandler = $this->getPersistenceMock()->contentHandler();
156
        $domainMapperMock = $this->getDomainMapperMock();
157
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
158
159
        $versionInfoMock->expects($this->any())
160
            ->method('__get')
161
            ->with('status')
162
            ->willReturn(APIVersionInfo::STATUS_DRAFT);
163
164
        $contentServiceMock->expects($this->never())
165
            ->method('loadContentInfo');
166
167
        $contentHandler->expects($this->once())
168
            ->method('loadVersionInfo')
169
            ->with(
170
                $this->equalTo(42),
171
                $this->equalTo(2)
172
            )->willReturn(new SPIVersionInfo());
173
174
        $domainMapperMock->expects($this->once())
175
            ->method('buildVersionInfoDomainObject')
176
            ->with(new SPIVersionInfo())
177
            ->willReturn($versionInfoMock);
178
179
        $repository->expects($this->once())
180
            ->method('canUser')
181
            ->with(
182
                $this->equalTo('content'),
183
                $this->equalTo('versionread'),
184
                $this->equalTo($versionInfoMock)
185
            )->willReturn(true);
186
187
        $result = $contentServiceMock->loadVersionInfoById(42, 2);
188
189
        $this->assertEquals($versionInfoMock, $result);
190
    }
191
192
    /**
193
     * Test for the loadVersionInfo() method.
194
     *
195
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
196
     */
197
    public function testLoadVersionInfoByIdThrowsNotFoundException()
198
    {
199
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class);
200
201
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
202
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
203
        $contentHandler = $this->getPersistenceMock()->contentHandler();
204
205
        $contentHandler->expects($this->once())
206
            ->method('loadVersionInfo')
207
            ->with(
208
                $this->equalTo(42),
209
                $this->equalTo(24)
210
            )->will(
211
                $this->throwException(
212
                    new NotFoundException(
213
                        'Content',
214
                        [
215
                            'contentId' => 42,
216
                            'versionNo' => 24,
217
                        ]
218
                    )
219
                )
220
            );
221
222
        $contentServiceMock->loadVersionInfoById(42, 24);
223
    }
224
225
    /**
226
     * Test for the loadVersionInfo() method.
227
     *
228
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
229
     */
230
    public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion()
231
    {
232
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
233
234
        $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...
235
        $contentServiceMock = $this->getPartlyMockedContentService();
236
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
237
        $contentHandler = $this->getPersistenceMock()->contentHandler();
238
        $domainMapperMock = $this->getDomainMapperMock();
239
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
240
        $permissionResolver = $this->getPermissionResolverMock();
241
242
        $versionInfoMock->expects($this->any())
243
            ->method('isPublished')
244
            ->willReturn(false);
245
246
        $contentHandler->expects($this->once())
247
            ->method('loadVersionInfo')
248
            ->with(
249
                $this->equalTo(42),
250
                $this->equalTo(24)
251
            )->will(
252
                $this->returnValue(new SPIVersionInfo())
253
            );
254
255
        $domainMapperMock->expects($this->once())
256
            ->method('buildVersionInfoDomainObject')
257
            ->with(new SPIVersionInfo())
258
            ->will($this->returnValue($versionInfoMock));
259
260
        $permissionResolver->expects($this->once())
261
            ->method('canUser')
262
            ->with(
263
                $this->equalTo('content'),
264
                $this->equalTo('versionread'),
265
                $this->equalTo($versionInfoMock)
266
            )->will($this->returnValue(false));
267
268
        $contentServiceMock->loadVersionInfoById(42, 24);
269
    }
270
271
    /**
272
     * Test for the loadVersionInfo() method.
273
     *
274
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
275
     */
276 View Code Duplication
    public function testLoadVersionInfoByIdPublishedVersion()
277
    {
278
        $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...
279
        $contentServiceMock = $this->getPartlyMockedContentService();
280
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
281
        $contentHandler = $this->getPersistenceMock()->contentHandler();
282
        $domainMapperMock = $this->getDomainMapperMock();
283
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
284
        $permissionResolver = $this->getPermissionResolverMock();
285
286
        $versionInfoMock->expects($this->once())
287
            ->method('isPublished')
288
            ->willReturn(true);
289
290
        $contentHandler->expects($this->once())
291
            ->method('loadVersionInfo')
292
            ->with(
293
                $this->equalTo(42),
294
                $this->equalTo(24)
295
            )->will(
296
                $this->returnValue(new SPIVersionInfo())
297
            );
298
299
        $domainMapperMock->expects($this->once())
300
            ->method('buildVersionInfoDomainObject')
301
            ->with(new SPIVersionInfo())
302
            ->will($this->returnValue($versionInfoMock));
303
304
        $permissionResolver->expects($this->once())
305
            ->method('canUser')
306
            ->with(
307
                $this->equalTo('content'),
308
                $this->equalTo('read'),
309
                $this->equalTo($versionInfoMock)
310
            )->will($this->returnValue(true));
311
312
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
313
314
        $this->assertEquals($versionInfoMock, $result);
315
    }
316
317
    /**
318
     * Test for the loadVersionInfo() method.
319
     *
320
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
321
     */
322 View Code Duplication
    public function testLoadVersionInfoByIdNonPublishedVersion()
323
    {
324
        $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...
325
        $contentServiceMock = $this->getPartlyMockedContentService();
326
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
327
        $contentHandler = $this->getPersistenceMock()->contentHandler();
328
        $domainMapperMock = $this->getDomainMapperMock();
329
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
330
        $permissionResolver = $this->getPermissionResolverMock();
331
332
        $versionInfoMock->expects($this->once())
333
            ->method('isPublished')
334
            ->willReturn(false);
335
336
        $contentHandler->expects($this->once())
337
            ->method('loadVersionInfo')
338
            ->with(
339
                $this->equalTo(42),
340
                $this->equalTo(24)
341
            )->will(
342
                $this->returnValue(new SPIVersionInfo())
343
            );
344
345
        $domainMapperMock->expects($this->once())
346
            ->method('buildVersionInfoDomainObject')
347
            ->with(new SPIVersionInfo())
348
            ->will($this->returnValue($versionInfoMock));
349
350
        $permissionResolver->expects($this->once())
351
            ->method('canUser')
352
            ->with(
353
                $this->equalTo('content'),
354
                $this->equalTo('versionread'),
355
                $this->equalTo($versionInfoMock)
356
            )->will($this->returnValue(true));
357
358
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
359
360
        $this->assertEquals($versionInfoMock, $result);
361
    }
362
363
    /**
364
     * Test for the loadVersionInfo() method.
365
     *
366
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfo
367
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoById
368
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsNotFoundException
369
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion
370
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdPublishedVersion
371
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdNonPublishedVersion
372
     */
373
    public function testLoadVersionInfo()
374
    {
375
        $contentServiceMock = $this->getPartlyMockedContentService(
376
            ['loadVersionInfoById']
377
        );
378
        $contentServiceMock->expects(
379
            $this->once()
380
        )->method(
381
            'loadVersionInfoById'
382
        )->with(
383
            $this->equalTo(42),
384
            $this->equalTo(7)
385
        )->will(
386
            $this->returnValue('result')
387
        );
388
389
        $result = $contentServiceMock->loadVersionInfo(
390
            new ContentInfo(['id' => 42]),
391
            7
392
        );
393
394
        $this->assertEquals('result', $result);
395
    }
396
397
    public function testLoadContent()
398
    {
399
        $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...
400
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
401
        $content = $this->createMock(APIContent::class);
402
        $versionInfo = $this->createMock(APIVersionInfo::class);
403
        $permissionResolver = $this->getPermissionResolverMock();
404
405
        $content
406
            ->expects($this->once())
407
            ->method('getVersionInfo')
408
            ->will($this->returnValue($versionInfo));
409
        $versionInfo
410
            ->expects($this->once())
411
            ->method('isPublished')
412
            ->willReturn(true);
413
        $contentId = 123;
414
        $contentService
415
            ->expects($this->once())
416
            ->method('internalLoadContent')
417
            ->with($contentId)
418
            ->will($this->returnValue($content));
419
420
        $permissionResolver
421
            ->expects($this->once())
422
            ->method('canUser')
423
            ->with('content', 'read', $content)
424
            ->will($this->returnValue(true));
425
426
        $this->assertSame($content, $contentService->loadContent($contentId));
427
    }
428
429 View Code Duplication
    public function testLoadContentNonPublished()
430
    {
431
        $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...
432
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
433
        $content = $this->createMock(APIContent::class);
434
        $versionInfo = $this->createMock(APIVersionInfo::class);
435
        $permissionResolver = $this->getPermissionResolverMock();
436
437
        $content
438
            ->expects($this->once())
439
            ->method('getVersionInfo')
440
            ->will($this->returnValue($versionInfo));
441
        $contentId = 123;
442
        $contentService
443
            ->expects($this->once())
444
            ->method('internalLoadContent')
445
            ->with($contentId)
446
            ->will($this->returnValue($content));
447
448
        $permissionResolver
449
            ->expects($this->exactly(2))
450
            ->method('canUser')
451
            ->will(
452
                $this->returnValueMap(
453
                    [
454
                        ['content', 'read', $content, [], true],
455
                        ['content', 'versionread', $content, [], true],
456
                    ]
457
                )
458
            );
459
460
        $this->assertSame($content, $contentService->loadContent($contentId));
461
    }
462
463
    public function testLoadContentUnauthorized()
464
    {
465
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
466
467
        $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...
468
        $permissionResolver = $this->getPermissionResolverMock();
469
470
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
471
        $content = $this->createMock(APIContent::class);
472
        $contentId = 123;
473
        $contentService
474
            ->expects($this->once())
475
            ->method('internalLoadContent')
476
            ->with($contentId)
477
            ->will($this->returnValue($content));
478
479
        $permissionResolver
480
            ->expects($this->once())
481
            ->method('canUser')
482
            ->with('content', 'read', $content)
483
            ->will($this->returnValue(false));
484
485
        $contentService->loadContent($contentId);
486
    }
487
488 View Code Duplication
    public function testLoadContentNotPublishedStatusUnauthorized()
489
    {
490
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
491
492
        $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...
493
        $permissionResolver = $this->getPermissionResolverMock();
494
        $contentService = $this->getPartlyMockedContentService(['internalLoadContent']);
495
        $content = $this->createMock(APIContent::class);
496
        $versionInfo = $this
497
            ->getMockBuilder(APIVersionInfo::class)
498
            ->getMockForAbstractClass();
499
        $content
500
            ->expects($this->once())
501
            ->method('getVersionInfo')
502
            ->will($this->returnValue($versionInfo));
503
        $contentId = 123;
504
        $contentService
505
            ->expects($this->once())
506
            ->method('internalLoadContent')
507
            ->with($contentId)
508
            ->will($this->returnValue($content));
509
510
        $permissionResolver
511
            ->expects($this->exactly(2))
512
            ->method('canUser')
513
            ->will(
514
                $this->returnValueMap(
515
                    [
516
                        ['content', 'read', $content, [], true],
517
                        ['content', 'versionread', $content, [], false],
518
                    ]
519
                )
520
            );
521
522
        $contentService->loadContent($contentId);
523
    }
524
525
    /**
526
     * @dataProvider internalLoadContentProvider
527
     */
528
    public function testInternalLoadContent($id, $languages, $versionNo, $isRemoteId, $useAlwaysAvailable)
529
    {
530
        $contentService = $this->getPartlyMockedContentService();
531
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
532
        $contentHandler = $this->getPersistenceMock()->contentHandler();
533
        $realId = $id;
534
535
        if ($isRemoteId) {
536
            $realId = 123;
537
            $spiContentInfo = new SPIContentInfo(['currentVersionNo' => $versionNo ?: 7, 'id' => $realId]);
538
            $contentHandler
539
                ->expects($this->once())
540
                ->method('loadContentInfoByRemoteId')
541
                ->with($id)
542
                ->will($this->returnValue($spiContentInfo));
543
        } elseif (!empty($languages) && $useAlwaysAvailable) {
544
            $spiContentInfo = new SPIContentInfo(['alwaysAvailable' => false]);
545
            $contentHandler
546
                ->expects($this->once())
547
                ->method('loadContentInfo')
548
                ->with($id)
549
                ->will($this->returnValue($spiContentInfo));
550
        }
551
552
        $spiContent = new SPIContent([
553
            'versionInfo' => new VersionInfo([
554
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
555
            ]),
556
        ]);
557
        $contentHandler
558
            ->expects($this->once())
559
            ->method('load')
560
            ->with($realId, $versionNo, $languages)
561
            ->willReturn($spiContent);
562
563
        $content = $this->mockBuildContentDomainObject($spiContent, $languages);
564
565
        $this->assertSame(
566
            $content,
567
            $contentService->internalLoadContent($id, $languages, $versionNo, $isRemoteId, $useAlwaysAvailable)
568
        );
569
    }
570
571
    public function internalLoadContentProvider()
572
    {
573
        return [
574
            [123, null, null, false, false],
575
            [123, null, 456, false, false],
576
            [456, null, 123, false, true],
577
            [456, null, 2, false, false],
578
            [456, ['eng-GB'], 2, false, true],
579
            [456, ['eng-GB', 'fre-FR'], null, false, false],
580
            [456, ['eng-GB', 'fre-FR', 'nor-NO'], 2, false, false],
581
            // With remoteId
582
            [123, null, null, true, false],
583
            ['someRemoteId', null, 456, true, false],
584
            [456, null, 123, true, false],
585
            ['someRemoteId', null, 2, true, false],
586
            ['someRemoteId', ['eng-GB'], 2, true, false],
587
            [456, ['eng-GB', 'fre-FR'], null, true, false],
588
            ['someRemoteId', ['eng-GB', 'fre-FR', 'nor-NO'], 2, true, false],
589
        ];
590
    }
591
592
    public function testInternalLoadContentNotFound()
593
    {
594
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class);
595
596
        $contentService = $this->getPartlyMockedContentService();
597
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
598
        $contentHandler = $this->getPersistenceMock()->contentHandler();
599
        $id = 123;
600
        $versionNo = 7;
601
        $languages = null;
602
        $contentHandler
603
            ->expects($this->once())
604
            ->method('load')
605
            ->with($id, $versionNo, $languages)
606
            ->will(
607
                $this->throwException(
608
                    $this->createMock(APINotFoundException::class)
609
                )
610
            );
611
612
        $contentService->internalLoadContent($id, $languages, $versionNo);
613
    }
614
615
    /**
616
     * Test for the loadContentByContentInfo() method.
617
     *
618
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByContentInfo
619
     */
620
    public function testLoadContentByContentInfo()
621
    {
622
        $contentServiceMock = $this->getPartlyMockedContentService(
623
            ['loadContent']
624
        );
625
        $contentServiceMock->expects(
626
            $this->once()
627
        )->method(
628
            'loadContent'
629
        )->with(
630
            $this->equalTo(42),
631
            $this->equalTo(['cro-HR']),
632
            $this->equalTo(7),
633
            $this->equalTo(false)
634
        )->will(
635
            $this->returnValue('result')
636
        );
637
638
        $result = $contentServiceMock->loadContentByContentInfo(
639
            new ContentInfo(['id' => 42]),
640
            ['cro-HR'],
641
            7
642
        );
643
644
        $this->assertEquals('result', $result);
645
    }
646
647
    /**
648
     * Test for the loadContentByVersionInfo() method.
649
     *
650
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByVersionInfo
651
     */
652
    public function testLoadContentByVersionInfo()
653
    {
654
        $contentServiceMock = $this->getPartlyMockedContentService(
655
            ['loadContent']
656
        );
657
        $contentServiceMock->expects(
658
            $this->once()
659
        )->method(
660
            'loadContent'
661
        )->with(
662
            $this->equalTo(42),
663
            $this->equalTo(['cro-HR']),
664
            $this->equalTo(7),
665
            $this->equalTo(false)
666
        )->will(
667
            $this->returnValue('result')
668
        );
669
670
        $result = $contentServiceMock->loadContentByVersionInfo(
671
            new VersionInfo(
672
                [
673
                    'contentInfo' => new ContentInfo(['id' => 42]),
674
                    'versionNo' => 7,
675
                ]
676
            ),
677
            ['cro-HR']
678
        );
679
680
        $this->assertEquals('result', $result);
681
    }
682
683
    /**
684
     * Test for the deleteContent() method.
685
     *
686
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
687
     */
688
    public function testDeleteContentThrowsUnauthorizedException()
689
    {
690
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
691
692
        $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...
693
        $permissionResolver = $this->getPermissionResolverMock();
694
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
695
        $contentInfo = $this->createMock(APIContentInfo::class);
696
697
        $contentInfo->expects($this->any())
698
            ->method('__get')
699
            ->with('id')
700
            ->will($this->returnValue(42));
701
702
        $contentService->expects($this->once())
703
            ->method('internalLoadContentInfo')
704
            ->with(42)
705
            ->will($this->returnValue($contentInfo));
706
707
        $permissionResolver->expects($this->once())
708
            ->method('canUser')
709
            ->with('content', 'remove')
710
            ->will($this->returnValue(false));
711
712
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
713
        $contentService->deleteContent($contentInfo);
714
    }
715
716
    /**
717
     * Test for the deleteContent() method.
718
     *
719
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
720
     */
721
    public function testDeleteContent()
722
    {
723
        $repository = $this->getRepositoryMock();
724
        $permissionResolver = $this->getPermissionResolverMock();
725
726
        $permissionResolver->expects($this->once())
727
            ->method('canUser')
728
            ->with('content', 'remove')
729
            ->will($this->returnValue(true));
730
731
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
732
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandler */
733
        $urlAliasHandler = $this->getPersistenceMock()->urlAliasHandler();
734
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
735
        $locationHandler = $this->getPersistenceMock()->locationHandler();
736
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
737
        $contentHandler = $this->getPersistenceMock()->contentHandler();
738
739
        $contentInfo = $this->createMock(APIContentInfo::class);
740
741
        $contentService->expects($this->once())
742
            ->method('internalLoadContentInfo')
743
            ->with(42)
744
            ->will($this->returnValue($contentInfo));
745
746
        $contentInfo->expects($this->any())
747
            ->method('__get')
748
            ->with('id')
749
            ->will($this->returnValue(42));
750
751
        $repository->expects($this->once())->method('beginTransaction');
752
753
        $spiLocations = [
754
            new SPILocation(['id' => 1]),
755
            new SPILocation(['id' => 2]),
756
        ];
757
        $locationHandler->expects($this->once())
758
            ->method('loadLocationsByContent')
759
            ->with(42)
760
            ->will($this->returnValue($spiLocations));
761
762
        $contentHandler->expects($this->once())
763
            ->method('deleteContent')
764
            ->with(42);
765
766
        foreach ($spiLocations as $index => $spiLocation) {
767
            $urlAliasHandler->expects($this->at($index))
768
                ->method('locationDeleted')
769
                ->with($spiLocation->id);
770
        }
771
772
        $repository->expects($this->once())->method('commit');
773
774
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
775
        $contentService->deleteContent($contentInfo);
776
    }
777
778
    /**
779
     * Test for the deleteContent() method.
780
     *
781
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
782
     */
783
    public function testDeleteContentWithRollback()
784
    {
785
        $this->expectException(\Exception::class);
786
787
        $repository = $this->getRepositoryMock();
788
        $permissionResolver = $this->getPermissionResolverMock();
789
790
        $permissionResolver->expects($this->once())
791
            ->method('canUser')
792
            ->with('content', 'remove')
793
            ->will($this->returnValue(true));
794
795
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
796
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
797
        $locationHandler = $this->getPersistenceMock()->locationHandler();
798
799
        $contentInfo = $this->createMock(APIContentInfo::class);
800
801
        $contentService->expects($this->once())
802
            ->method('internalLoadContentInfo')
803
            ->with(42)
804
            ->will($this->returnValue($contentInfo));
805
806
        $contentInfo->expects($this->any())
807
            ->method('__get')
808
            ->with('id')
809
            ->will($this->returnValue(42));
810
811
        $repository->expects($this->once())->method('beginTransaction');
812
813
        $locationHandler->expects($this->once())
814
            ->method('loadLocationsByContent')
815
            ->with(42)
816
            ->will($this->throwException(new \Exception()));
817
818
        $repository->expects($this->once())->method('rollback');
819
820
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
821
        $contentService->deleteContent($contentInfo);
822
    }
823
824
    /**
825
     * Test for the deleteVersion() method.
826
     *
827
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion
828
     */
829
    public function testDeleteVersionThrowsBadStateExceptionLastVersion()
830
    {
831
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
832
833
        $repository = $this->getRepositoryMock();
834
        $permissionResolver = $this->getPermissionResolverMock();
835
836
        $permissionResolver
837
            ->expects($this->once())
838
            ->method('canUser')
839
            ->with('content', 'versionremove')
840
            ->will($this->returnValue(true));
841
        $repository
842
            ->expects($this->never())
843
            ->method('beginTransaction');
844
845
        $contentService = $this->getPartlyMockedContentService();
846
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
847
        $contentHandler = $this->getPersistenceMock()->contentHandler();
848
        $contentInfo = $this->createMock(APIContentInfo::class);
849
        $versionInfo = $this->createMock(APIVersionInfo::class);
850
851
        $contentInfo
852
            ->expects($this->any())
853
            ->method('__get')
854
            ->with('id')
855
            ->will($this->returnValue(42));
856
857
        $versionInfo
858
            ->expects($this->any())
859
            ->method('__get')
860
            ->will(
861
                $this->returnValueMap(
862
                    [
863
                        ['versionNo', 123],
864
                        ['contentInfo', $contentInfo],
865
                    ]
866
                )
867
            );
868
        $versionInfo
869
            ->expects($this->once())
870
            ->method('isPublished')
871
            ->willReturn(false);
872
873
        $contentHandler
874
            ->expects($this->once())
875
            ->method('listVersions')
876
            ->with(42)
877
            ->will($this->returnValue(['version']));
878
879
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo */
880
        $contentService->deleteVersion($versionInfo);
881
    }
882
883
    /**
884
     * Test for the createContent() method.
885
     *
886
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
887
     */
888 View Code Duplication
    public function testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet()
889
    {
890
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
891
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'mainLanguageCode\' property must be set');
892
893
        $mockedService = $this->getPartlyMockedContentService();
894
        $mockedService->createContent(new ContentCreateStruct(), []);
895
    }
896
897
    /**
898
     * Test for the createContent() method.
899
     *
900
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
901
     */
902 View Code Duplication
    public function testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet()
903
    {
904
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
905
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'contentType\' property must be set');
906
907
        $mockedService = $this->getPartlyMockedContentService();
908
        $mockedService->createContent(
909
            new ContentCreateStruct(['mainLanguageCode' => 'eng-US']),
910
            []
911
        );
912
    }
913
914
    /**
915
     * Test for the createContent() method.
916
     *
917
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
918
     */
919
    public function testCreateContentThrowsUnauthorizedException()
920
    {
921
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
922
923
        $repositoryMock = $this->getRepositoryMock();
924
925
        $permissionResolver = $this->getPermissionResolverMock();
926
        $permissionResolver->expects($this->once())
927
            ->method('getCurrentUserReference')
928
            ->will($this->returnValue(new UserReference(169)));
929
930
        $mockedService = $this->getPartlyMockedContentService();
931
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
932
        $contentType = new ContentType(
933
            [
934
                'id' => 123,
935
                'fieldDefinitions' => [],
936
            ]
937
        );
938
        $contentCreateStruct = new ContentCreateStruct(
939
            [
940
                'ownerId' => 169,
941
                'alwaysAvailable' => false,
942
                'mainLanguageCode' => 'eng-US',
943
                'contentType' => $contentType,
944
            ]
945
        );
946
947
        $contentTypeServiceMock->expects($this->once())
948
            ->method('loadContentType')
949
            ->with($this->equalTo(123))
950
            ->will($this->returnValue($contentType));
951
952
        $repositoryMock->expects($this->once())
953
            ->method('getContentTypeService')
954
            ->will($this->returnValue($contentTypeServiceMock));
955
956
        $permissionResolver->expects($this->once())
957
            ->method('canUser')
958
            ->with(
959
                $this->equalTo('content'),
960
                $this->equalTo('create'),
961
                $this->isInstanceOf(get_class($contentCreateStruct)),
962
                $this->equalTo([])
963
            )->will($this->returnValue(false));
964
965
        $mockedService->createContent(
966
            new ContentCreateStruct(
967
                [
968
                    'mainLanguageCode' => 'eng-US',
969
                    'contentType' => $contentType,
970
                ]
971
            ),
972
            []
973
        );
974
    }
975
976
    /**
977
     * Test for the createContent() method.
978
     *
979
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
980
     * @exceptionMessage Argument '$contentCreateStruct' is invalid: Another content with remoteId 'faraday' exists
981
     */
982
    public function testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId()
983
    {
984
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
985
986
        $repositoryMock = $this->getRepositoryMock();
987
        $permissionResolverMock = $this->getPermissionResolverMock();
988
        $permissionResolverMock
989
            ->expects($this->once())
990
            ->method('getCurrentUserReference')
991
            ->willReturn($this->createMock(UserReference::class));
992
993
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
994
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
995
        $contentType = new ContentType(
996
            [
997
                'id' => 123,
998
                'fieldDefinitions' => [],
999
            ]
1000
        );
1001
        $contentCreateStruct = new ContentCreateStruct(
1002
            [
1003
                'ownerId' => 169,
1004
                'alwaysAvailable' => false,
1005
                'remoteId' => 'faraday',
1006
                'mainLanguageCode' => 'eng-US',
1007
                'contentType' => $contentType,
1008
            ]
1009
        );
1010
1011
        $contentTypeServiceMock->expects($this->once())
1012
            ->method('loadContentType')
1013
            ->with($this->equalTo(123))
1014
            ->will($this->returnValue($contentType));
1015
1016
        $repositoryMock->expects($this->once())
1017
            ->method('getContentTypeService')
1018
            ->will($this->returnValue($contentTypeServiceMock));
1019
1020
        $permissionResolverMock->expects($this->once())
1021
            ->method('canUser')
1022
            ->with(
1023
                $this->equalTo('content'),
1024
                $this->equalTo('create'),
1025
                $this->isInstanceOf(get_class($contentCreateStruct)),
1026
                $this->equalTo([])
1027
            )->will($this->returnValue(true));
1028
1029
        $mockedService->expects($this->once())
1030
            ->method('loadContentByRemoteId')
1031
            ->with($contentCreateStruct->remoteId)
1032
            ->will($this->returnValue('Hello...'));
1033
1034
        $mockedService->createContent(
1035
            new ContentCreateStruct(
1036
                [
1037
                    'remoteId' => 'faraday',
1038
                    'mainLanguageCode' => 'eng-US',
1039
                    'contentType' => $contentType,
1040
                ]
1041
            ),
1042
            []
1043
        );
1044
    }
1045
1046
    /**
1047
     * @param string $mainLanguageCode
1048
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1049
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1050
     *
1051
     * @return array
1052
     */
1053
    protected function mapStructFieldsForCreate($mainLanguageCode, $structFields, $fieldDefinitions)
1054
    {
1055
        $mappedFieldDefinitions = [];
1056
        foreach ($fieldDefinitions as $fieldDefinition) {
1057
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
1058
        }
1059
1060
        $mappedStructFields = [];
1061
        foreach ($structFields as $structField) {
1062
            if ($structField->languageCode === null) {
1063
                $languageCode = $mainLanguageCode;
1064
            } else {
1065
                $languageCode = $structField->languageCode;
1066
            }
1067
1068
            $mappedStructFields[$structField->fieldDefIdentifier][$languageCode] = (string)$structField->value;
1069
        }
1070
1071
        return $mappedStructFields;
1072
    }
1073
1074
    /**
1075
     * Returns full, possibly redundant array of field values, indexed by field definition
1076
     * identifier and language code.
1077
     *
1078
     * @throws \RuntimeException Method is intended to be used only with consistent fixtures
1079
     *
1080
     * @param string $mainLanguageCode
1081
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1082
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1083
     * @param array $languageCodes
1084
     *
1085
     * @return array
1086
     */
1087
    protected function determineValuesForCreate(
1088
        $mainLanguageCode,
1089
        array $structFields,
1090
        array $fieldDefinitions,
1091
        array $languageCodes
1092
    ) {
1093
        $mappedStructFields = $this->mapStructFieldsForCreate(
1094
            $mainLanguageCode,
1095
            $structFields,
1096
            $fieldDefinitions
1097
        );
1098
1099
        $values = [];
1100
1101
        foreach ($fieldDefinitions as $fieldDefinition) {
1102
            $identifier = $fieldDefinition->identifier;
1103
            foreach ($languageCodes as $languageCode) {
1104 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...
1105
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
1106
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
1107
                    } else {
1108
                        $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1109
                    }
1110
                    continue;
1111
                }
1112
1113 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...
1114
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
1115
                    continue;
1116
                }
1117
1118
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1119
            }
1120
        }
1121
1122
        return $this->stubValues($values);
1123
    }
1124
1125
    /**
1126
     * @param string $mainLanguageCode
1127
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1128
     *
1129
     * @return string[]
1130
     */
1131
    protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields)
1132
    {
1133
        $languageCodes = [];
1134
1135
        foreach ($structFields as $field) {
1136
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1137
                continue;
1138
            }
1139
1140
            $languageCodes[$field->languageCode] = true;
1141
        }
1142
1143
        $languageCodes[$mainLanguageCode] = true;
1144
1145
        return array_keys($languageCodes);
1146
    }
1147
1148
    /**
1149
     * Asserts that calling createContent() with given API field set causes calling
1150
     * Handler::createContent() with given SPI field set.
1151
     *
1152
     * @param string $mainLanguageCode
1153
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1154
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
1155
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1156
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
1157
     * @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...
1158
     * @param bool $execute
1159
     *
1160
     * @return mixed
1161
     */
1162
    protected function assertForTestCreateContentNonRedundantFieldSet(
1163
        $mainLanguageCode,
1164
        array $structFields,
1165
        array $spiFields,
1166
        array $fieldDefinitions,
1167
        array $locationCreateStructs = [],
1168
        $withObjectStates = false,
1169
        $execute = true
1170
    ) {
1171
        $repositoryMock = $this->getRepositoryMock();
1172
        $mockedService = $this->getPartlyMockedContentService();
1173
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
1174
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
1175
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1176
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1177
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
1178
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
1179
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1180
        $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...
1181
        $domainMapperMock = $this->getDomainMapperMock();
1182
        $relationProcessorMock = $this->getRelationProcessorMock();
1183
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
1184
        $permissionResolverMock = $this->getPermissionResolverMock();
1185
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
1186
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
1187
        $contentType = new ContentType(
1188
            [
1189
                'id' => 123,
1190
                'fieldDefinitions' => $fieldDefinitions,
1191
                'nameSchema' => '<nameSchema>',
1192
            ]
1193
        );
1194
        $contentCreateStruct = new ContentCreateStruct(
1195
            [
1196
                'fields' => $structFields,
1197
                'mainLanguageCode' => $mainLanguageCode,
1198
                'contentType' => $contentType,
1199
                'alwaysAvailable' => false,
1200
                'ownerId' => 169,
1201
                'sectionId' => 1,
1202
            ]
1203
        );
1204
1205
        $languageHandlerMock->expects($this->any())
1206
            ->method('loadByLanguageCode')
1207
            ->with($this->isType('string'))
1208
            ->will(
1209
                $this->returnCallback(
1210
                    function () {
1211
                        return new Language(['id' => 4242]);
1212
                    }
1213
                )
1214
            );
1215
1216
        $repositoryMock->expects($this->once())->method('beginTransaction');
1217
1218
        $contentTypeServiceMock->expects($this->once())
1219
            ->method('loadContentType')
1220
            ->with($this->equalTo($contentType->id))
1221
            ->will($this->returnValue($contentType));
1222
1223
        $repositoryMock->expects($this->once())
1224
            ->method('getContentTypeService')
1225
            ->will($this->returnValue($contentTypeServiceMock));
1226
1227
        $that = $this;
1228
        $permissionResolverMock->expects($this->once())
1229
            ->method('canUser')
1230
            ->with(
1231
                $this->equalTo('content'),
1232
                $this->equalTo('create'),
1233
                $this->isInstanceOf(APIContentCreateStruct::class),
1234
                $this->equalTo($locationCreateStructs)
1235
            )->will(
1236
                $this->returnCallback(
1237
                    function () use ($that, $contentCreateStruct) {
1238
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1239
1240
                        return true;
1241
                    }
1242
                )
1243
            );
1244
1245
        $domainMapperMock->expects($this->once())
1246
            ->method('getUniqueHash')
1247
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1248
            ->will(
1249
                $this->returnCallback(
1250
                    function ($object) use ($that, $contentCreateStruct) {
1251
                        $that->assertEquals($contentCreateStruct, $object);
1252
1253
                        return 'hash';
1254
                    }
1255
                )
1256
            );
1257
1258
        $fieldTypeMock->expects($this->any())
1259
            ->method('acceptValue')
1260
            ->will(
1261
                $this->returnCallback(
1262
                    function ($valueString) {
1263
                        return new ValueStub($valueString);
1264
                    }
1265
                )
1266
            );
1267
1268
        $fieldTypeMock->expects($this->any())
1269
            ->method('toPersistenceValue')
1270
            ->will(
1271
                $this->returnCallback(
1272
                    function (ValueStub $value) {
1273
                        return (string)$value;
1274
                    }
1275
                )
1276
            );
1277
1278
        $emptyValue = self::EMPTY_FIELD_VALUE;
1279
        $fieldTypeMock->expects($this->any())
1280
            ->method('isEmptyValue')
1281
            ->will(
1282
                $this->returnCallback(
1283
                    function (ValueStub $value) use ($emptyValue) {
1284
                        return $emptyValue === (string)$value;
1285
                    }
1286
                )
1287
            );
1288
1289
        $fieldTypeMock->expects($this->any())
1290
            ->method('validate')
1291
            ->will($this->returnValue([]));
1292
1293
        $this->getFieldTypeRegistryMock()->expects($this->any())
1294
            ->method('getFieldType')
1295
            ->will($this->returnValue($fieldTypeMock));
1296
1297
        $relationProcessorMock
1298
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
1299
            ->method('appendFieldRelations')
1300
            ->with(
1301
                $this->isType('array'),
1302
                $this->isType('array'),
1303
                $this->isInstanceOf(SPIFieldType::class),
1304
                $this->isInstanceOf(Value::class),
1305
                $this->anything()
1306
            );
1307
1308
        $values = $this->determineValuesForCreate(
1309
            $mainLanguageCode,
1310
            $structFields,
1311
            $fieldDefinitions,
1312
            $languageCodes
1313
        );
1314
        $nameSchemaServiceMock->expects($this->once())
1315
            ->method('resolve')
1316
            ->with(
1317
                $this->equalTo($contentType->nameSchema),
1318
                $this->equalTo($contentType),
1319
                $this->equalTo($values),
1320
                $this->equalTo($languageCodes)
1321
            )->will($this->returnValue([]));
1322
1323
        $relationProcessorMock->expects($this->any())
1324
            ->method('processFieldRelations')
1325
            ->with(
1326
                $this->isType('array'),
1327
                $this->equalTo(42),
1328
                $this->isType('int'),
1329
                $this->equalTo($contentType),
1330
                $this->equalTo([])
1331
            );
1332
1333
        if (!$withObjectStates) {
1334
            $objectStateHandlerMock->expects($this->once())
1335
                ->method('loadAllGroups')
1336
                ->will($this->returnValue([]));
1337
        }
1338
1339
        if ($execute) {
1340
            $spiContentCreateStruct = new SPIContentCreateStruct(
1341
                [
1342
                    'name' => [],
1343
                    'typeId' => 123,
1344
                    'sectionId' => 1,
1345
                    'ownerId' => 169,
1346
                    'remoteId' => 'hash',
1347
                    'fields' => $spiFields,
1348
                    'modified' => time(),
1349
                    'initialLanguageId' => 4242,
1350
                ]
1351
            );
1352
            $spiContentCreateStruct2 = clone $spiContentCreateStruct;
1353
            ++$spiContentCreateStruct2->modified;
1354
1355
            $spiContent = new SPIContent(
1356
                [
1357
                    'versionInfo' => new SPIContent\VersionInfo(
1358
                        [
1359
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
1360
                            'versionNo' => 7,
1361
                        ]
1362
                    ),
1363
                ]
1364
            );
1365
1366
            $contentHandlerMock->expects($this->once())
1367
                ->method('create')
1368
                ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
1369
                ->will($this->returnValue($spiContent));
1370
1371
            $repositoryMock->expects($this->once())->method('commit');
1372
            $domainMapperMock->expects($this->once())
1373
                ->method('buildContentDomainObject')
1374
                ->with(
1375
                    $this->isInstanceOf(SPIContent::class),
1376
                    $this->equalTo($contentType)
1377
                );
1378
1379
            $mockedService->createContent($contentCreateStruct, []);
1380
        }
1381
1382
        return $contentCreateStruct;
1383
    }
1384
1385
    public function providerForTestCreateContentNonRedundantFieldSet1()
1386
    {
1387
        $spiFields = [
1388
            new SPIField(
1389
                [
1390
                    'fieldDefinitionId' => 'fieldDefinitionId',
1391
                    'type' => 'fieldTypeIdentifier',
1392
                    'value' => 'newValue',
1393
                    'languageCode' => 'eng-US',
1394
                ]
1395
            ),
1396
        ];
1397
1398
        return [
1399
            // 0. Without language set
1400
            [
1401
                'eng-US',
1402
                [
1403
                    new Field(
1404
                        [
1405
                            'fieldDefIdentifier' => 'identifier',
1406
                            'value' => 'newValue',
1407
                            'languageCode' => 'eng-US',
1408
                        ]
1409
                    ),
1410
                ],
1411
                $spiFields,
1412
            ],
1413
            // 1. Without language set
1414
            [
1415
                'eng-US',
1416
                [
1417
                    new Field(
1418
                        [
1419
                            'fieldDefIdentifier' => 'identifier',
1420
                            'value' => 'newValue',
1421
                            'languageCode' => null,
1422
                        ]
1423
                    ),
1424
                ],
1425
                $spiFields,
1426
            ],
1427
        ];
1428
    }
1429
1430
    /**
1431
     * Test for the createContent() method.
1432
     *
1433
     * Testing the simplest use case.
1434
     *
1435
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1436
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1437
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1438
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1439
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1440
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet1
1441
     */
1442
    public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields)
1443
    {
1444
        $fieldDefinitions = [
1445
            new FieldDefinition(
1446
                [
1447
                    'id' => 'fieldDefinitionId',
1448
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1449
                    'isTranslatable' => false,
1450
                    'identifier' => 'identifier',
1451
                    'isRequired' => false,
1452
                    'defaultValue' => 'defaultValue',
1453
                ]
1454
            ),
1455
        ];
1456
1457
        $this->assertForTestCreateContentNonRedundantFieldSet(
1458
            $mainLanguageCode,
1459
            $structFields,
1460
            $spiFields,
1461
            $fieldDefinitions
1462
        );
1463
    }
1464
1465
    public function providerForTestCreateContentNonRedundantFieldSet2()
1466
    {
1467
        $spiFields = [
1468
            new SPIField(
1469
                [
1470
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1471
                    'type' => 'fieldTypeIdentifier',
1472
                    'value' => 'newValue1',
1473
                    'languageCode' => 'eng-US',
1474
                ]
1475
            ),
1476
            new SPIField(
1477
                [
1478
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1479
                    'type' => 'fieldTypeIdentifier',
1480
                    'value' => 'newValue2',
1481
                    'languageCode' => 'ger-DE',
1482
                ]
1483
            ),
1484
        ];
1485
1486
        return [
1487
            // 0. With language set
1488
            [
1489
                'eng-US',
1490
                [
1491
                    new Field(
1492
                        [
1493
                            'fieldDefIdentifier' => 'identifier1',
1494
                            'value' => 'newValue1',
1495
                            'languageCode' => 'eng-US',
1496
                        ]
1497
                    ),
1498
                    new Field(
1499
                        [
1500
                            'fieldDefIdentifier' => 'identifier2',
1501
                            'value' => 'newValue2',
1502
                            'languageCode' => 'ger-DE',
1503
                        ]
1504
                    ),
1505
                ],
1506
                $spiFields,
1507
            ],
1508
            // 1. Without language set
1509
            [
1510
                'eng-US',
1511
                [
1512
                    new Field(
1513
                        [
1514
                            'fieldDefIdentifier' => 'identifier1',
1515
                            'value' => 'newValue1',
1516
                            'languageCode' => null,
1517
                        ]
1518
                    ),
1519
                    new Field(
1520
                        [
1521
                            'fieldDefIdentifier' => 'identifier2',
1522
                            'value' => 'newValue2',
1523
                            'languageCode' => 'ger-DE',
1524
                        ]
1525
                    ),
1526
                ],
1527
                $spiFields,
1528
            ],
1529
        ];
1530
    }
1531
1532
    /**
1533
     * Test for the createContent() method.
1534
     *
1535
     * Testing multiple languages with multiple translatable fields with empty default value.
1536
     *
1537
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1538
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1539
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1540
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1541
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1542
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet2
1543
     */
1544
    public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields)
1545
    {
1546
        $fieldDefinitions = [
1547
            new FieldDefinition(
1548
                [
1549
                    'id' => 'fieldDefinitionId1',
1550
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1551
                    'isTranslatable' => true,
1552
                    'identifier' => 'identifier1',
1553
                    'isRequired' => false,
1554
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1555
                ]
1556
            ),
1557
            new FieldDefinition(
1558
                [
1559
                    'id' => 'fieldDefinitionId2',
1560
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1561
                    'isTranslatable' => true,
1562
                    'identifier' => 'identifier2',
1563
                    'isRequired' => false,
1564
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1565
                ]
1566
            ),
1567
        ];
1568
1569
        $this->assertForTestCreateContentNonRedundantFieldSet(
1570
            $mainLanguageCode,
1571
            $structFields,
1572
            $spiFields,
1573
            $fieldDefinitions
1574
        );
1575
    }
1576
1577
    public function providerForTestCreateContentNonRedundantFieldSetComplex()
1578
    {
1579
        $spiFields0 = [
1580
            new SPIField(
1581
                [
1582
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1583
                    'type' => 'fieldTypeIdentifier',
1584
                    'value' => 'defaultValue2',
1585
                    'languageCode' => 'eng-US',
1586
                ]
1587
            ),
1588
            new SPIField(
1589
                [
1590
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1591
                    'type' => 'fieldTypeIdentifier',
1592
                    'value' => 'defaultValue4',
1593
                    'languageCode' => 'eng-US',
1594
                ]
1595
            ),
1596
        ];
1597
        $spiFields1 = [
1598
            new SPIField(
1599
                [
1600
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1601
                    'type' => 'fieldTypeIdentifier',
1602
                    'value' => 'newValue1',
1603
                    'languageCode' => 'ger-DE',
1604
                ]
1605
            ),
1606
            new SPIField(
1607
                [
1608
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1609
                    'type' => 'fieldTypeIdentifier',
1610
                    'value' => 'defaultValue2',
1611
                    'languageCode' => 'ger-DE',
1612
                ]
1613
            ),
1614
            new SPIField(
1615
                [
1616
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1617
                    'type' => 'fieldTypeIdentifier',
1618
                    'value' => 'newValue2',
1619
                    'languageCode' => 'eng-US',
1620
                ]
1621
            ),
1622
            new SPIField(
1623
                [
1624
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1625
                    'type' => 'fieldTypeIdentifier',
1626
                    'value' => 'newValue4',
1627
                    'languageCode' => 'eng-US',
1628
                ]
1629
            ),
1630
        ];
1631
1632
        return [
1633
            // 0. Creating by default values only
1634
            [
1635
                'eng-US',
1636
                [],
1637
                $spiFields0,
1638
            ],
1639
            // 1. Multiple languages with language set
1640
            [
1641
                'eng-US',
1642
                [
1643
                    new Field(
1644
                        [
1645
                            'fieldDefIdentifier' => 'identifier1',
1646
                            'value' => 'newValue1',
1647
                            'languageCode' => 'ger-DE',
1648
                        ]
1649
                    ),
1650
                    new Field(
1651
                        [
1652
                            'fieldDefIdentifier' => 'identifier2',
1653
                            'value' => 'newValue2',
1654
                            'languageCode' => 'eng-US',
1655
                        ]
1656
                    ),
1657
                    new Field(
1658
                        [
1659
                            'fieldDefIdentifier' => 'identifier4',
1660
                            'value' => 'newValue4',
1661
                            'languageCode' => 'eng-US',
1662
                        ]
1663
                    ),
1664
                ],
1665
                $spiFields1,
1666
            ],
1667
            // 2. Multiple languages without language set
1668
            [
1669
                'eng-US',
1670
                [
1671
                    new Field(
1672
                        [
1673
                            'fieldDefIdentifier' => 'identifier1',
1674
                            'value' => 'newValue1',
1675
                            'languageCode' => 'ger-DE',
1676
                        ]
1677
                    ),
1678
                    new Field(
1679
                        [
1680
                            'fieldDefIdentifier' => 'identifier2',
1681
                            'value' => 'newValue2',
1682
                            'languageCode' => null,
1683
                        ]
1684
                    ),
1685
                    new Field(
1686
                        [
1687
                            'fieldDefIdentifier' => 'identifier4',
1688
                            'value' => 'newValue4',
1689
                            'languageCode' => null,
1690
                        ]
1691
                    ),
1692
                ],
1693
                $spiFields1,
1694
            ],
1695
        ];
1696
    }
1697
1698
    protected function fixturesForTestCreateContentNonRedundantFieldSetComplex()
1699
    {
1700
        return [
1701
            new FieldDefinition(
1702
                [
1703
                    'id' => 'fieldDefinitionId1',
1704
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1705
                    'isTranslatable' => true,
1706
                    'identifier' => 'identifier1',
1707
                    'isRequired' => false,
1708
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1709
                ]
1710
            ),
1711
            new FieldDefinition(
1712
                [
1713
                    'id' => 'fieldDefinitionId2',
1714
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1715
                    'isTranslatable' => true,
1716
                    'identifier' => 'identifier2',
1717
                    'isRequired' => false,
1718
                    'defaultValue' => 'defaultValue2',
1719
                ]
1720
            ),
1721
            new FieldDefinition(
1722
                [
1723
                    'id' => 'fieldDefinitionId3',
1724
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1725
                    'isTranslatable' => false,
1726
                    'identifier' => 'identifier3',
1727
                    'isRequired' => false,
1728
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1729
                ]
1730
            ),
1731
            new FieldDefinition(
1732
                [
1733
                    'id' => 'fieldDefinitionId4',
1734
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1735
                    'isTranslatable' => false,
1736
                    'identifier' => 'identifier4',
1737
                    'isRequired' => false,
1738
                    'defaultValue' => 'defaultValue4',
1739
                ]
1740
            ),
1741
        ];
1742
    }
1743
1744
    /**
1745
     * Test for the createContent() method.
1746
     *
1747
     * Testing multiple languages with multiple translatable fields with empty default value.
1748
     *
1749
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1750
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1751
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1752
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1753
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1754
     * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex
1755
     */
1756
    public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields)
1757
    {
1758
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
1759
1760
        $this->assertForTestCreateContentNonRedundantFieldSet(
1761
            $mainLanguageCode,
1762
            $structFields,
1763
            $spiFields,
1764
            $fieldDefinitions
1765
        );
1766
    }
1767
1768 View Code Duplication
    public function providerForTestCreateContentWithInvalidLanguage()
1769
    {
1770
        return [
1771
            [
1772
                'eng-GB',
1773
                [
1774
                    new Field(
1775
                        [
1776
                            'fieldDefIdentifier' => 'identifier',
1777
                            'value' => 'newValue',
1778
                            'languageCode' => 'Klingon',
1779
                        ]
1780
                    ),
1781
                ],
1782
            ],
1783
            [
1784
                'Klingon',
1785
                [
1786
                    new Field(
1787
                        [
1788
                            'fieldDefIdentifier' => 'identifier',
1789
                            'value' => 'newValue',
1790
                            'languageCode' => 'eng-GB',
1791
                        ]
1792
                    ),
1793
                ],
1794
            ],
1795
        ];
1796
    }
1797
1798
    /**
1799
     * Test for the updateContent() method.
1800
     *
1801
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1802
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1803
     * @dataProvider providerForTestCreateContentWithInvalidLanguage
1804
     */
1805
    public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields)
1806
    {
1807
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
1808
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
1809
1810
        $repositoryMock = $this->getRepositoryMock();
1811
        $mockedService = $this->getPartlyMockedContentService();
1812
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1813
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1814
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1815
        $domainMapperMock = $this->getDomainMapperMock();
1816
        $permissionResolver = $this->getPermissionResolverMock();
1817
1818
        $contentType = new ContentType(
1819
            [
1820
                'id' => 123,
1821
                'fieldDefinitions' => [],
1822
            ]
1823
        );
1824
        $contentCreateStruct = new ContentCreateStruct(
1825
            [
1826
                'fields' => $structFields,
1827
                'mainLanguageCode' => $mainLanguageCode,
1828
                'contentType' => $contentType,
1829
                'alwaysAvailable' => false,
1830
                'ownerId' => 169,
1831
                'sectionId' => 1,
1832
            ]
1833
        );
1834
1835
        $languageHandlerMock->expects($this->any())
1836
            ->method('loadByLanguageCode')
1837
            ->with($this->isType('string'))
1838
            ->will(
1839
                $this->returnCallback(
1840 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...
1841
                        if ($languageCode === 'Klingon') {
1842
                            throw new NotFoundException('Language', 'Klingon');
1843
                        }
1844
1845
                        return new Language(['id' => 4242]);
1846
                    }
1847
                )
1848
            );
1849
1850
        $contentTypeServiceMock->expects($this->once())
1851
            ->method('loadContentType')
1852
            ->with($this->equalTo($contentType->id))
1853
            ->will($this->returnValue($contentType));
1854
1855
        $repositoryMock->expects($this->once())
1856
            ->method('getContentTypeService')
1857
            ->will($this->returnValue($contentTypeServiceMock));
1858
1859
        $that = $this;
1860
        $permissionResolver->expects($this->once())
1861
            ->method('canUser')
1862
            ->with(
1863
                $this->equalTo('content'),
1864
                $this->equalTo('create'),
1865
                $this->isInstanceOf(APIContentCreateStruct::class),
1866
                $this->equalTo([])
1867
            )->will(
1868
                $this->returnCallback(
1869
                    function () use ($that, $contentCreateStruct) {
1870
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1871
1872
                        return true;
1873
                    }
1874
                )
1875
            );
1876
1877
        $domainMapperMock->expects($this->once())
1878
            ->method('getUniqueHash')
1879
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1880
            ->will(
1881
                $this->returnCallback(
1882
                    function ($object) use ($that, $contentCreateStruct) {
1883
                        $that->assertEquals($contentCreateStruct, $object);
1884
1885
                        return 'hash';
1886
                    }
1887
                )
1888
            );
1889
1890
        $mockedService->createContent($contentCreateStruct, []);
1891
    }
1892
1893
    protected function assertForCreateContentContentValidationException(
1894
        $mainLanguageCode,
1895
        $structFields,
1896
        $fieldDefinitions = []
1897
    ) {
1898
        $repositoryMock = $this->getRepositoryMock();
1899
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
1900
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1901
        $permissionResolver = $this->getPermissionResolverMock();
1902
1903
        $contentType = new ContentType(
1904
            [
1905
                'id' => 123,
1906
                'fieldDefinitions' => $fieldDefinitions,
1907
            ]
1908
        );
1909
        $contentCreateStruct = new ContentCreateStruct(
1910
            [
1911
                'ownerId' => 169,
1912
                'alwaysAvailable' => false,
1913
                'remoteId' => 'faraday',
1914
                'mainLanguageCode' => $mainLanguageCode,
1915
                'fields' => $structFields,
1916
                'contentType' => $contentType,
1917
            ]
1918
        );
1919
1920
        $contentTypeServiceMock->expects($this->once())
1921
            ->method('loadContentType')
1922
            ->with($this->equalTo(123))
1923
            ->will($this->returnValue($contentType));
1924
1925
        $repositoryMock->expects($this->once())
1926
            ->method('getContentTypeService')
1927
            ->will($this->returnValue($contentTypeServiceMock));
1928
1929
        $permissionResolver->expects($this->once())
1930
            ->method('canUser')
1931
            ->with(
1932
                $this->equalTo('content'),
1933
                $this->equalTo('create'),
1934
                $this->isInstanceOf(get_class($contentCreateStruct)),
1935
                $this->equalTo([])
1936
            )->will($this->returnValue(true));
1937
1938
        $mockedService->expects($this->once())
1939
            ->method('loadContentByRemoteId')
1940
            ->with($contentCreateStruct->remoteId)
1941
            ->will(
1942
                $this->throwException(new NotFoundException('Content', 'faraday'))
1943
            );
1944
1945
        $mockedService->createContent($contentCreateStruct, []);
1946
    }
1947
1948 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition()
1949
    {
1950
        return [
1951
            [
1952
                'eng-GB',
1953
                [
1954
                    new Field(
1955
                        [
1956
                            'fieldDefIdentifier' => 'identifier',
1957
                            'value' => 'newValue',
1958
                            'languageCode' => 'eng-GB',
1959
                        ]
1960
                    ),
1961
                ],
1962
            ],
1963
        ];
1964
    }
1965
1966
    /**
1967
     * Test for the createContent() method.
1968
     *
1969
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1970
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1971
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1972
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition
1973
     */
1974
    public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields)
1975
    {
1976
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
1977
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
1978
1979
        $this->assertForCreateContentContentValidationException(
1980
            $mainLanguageCode,
1981
            $structFields,
1982
            []
1983
        );
1984
    }
1985
1986 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionTranslation()
1987
    {
1988
        return [
1989
            [
1990
                'eng-GB',
1991
                [
1992
                    new Field(
1993
                        [
1994
                            'fieldDefIdentifier' => 'identifier',
1995
                            'value' => 'newValue',
1996
                            'languageCode' => 'eng-US',
1997
                        ]
1998
                    ),
1999
                ],
2000
            ],
2001
        ];
2002
    }
2003
2004
    /**
2005
     * Test for the createContent() method.
2006
     *
2007
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2008
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2009
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2010
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2011
     */
2012 View Code Duplication
    public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields)
2013
    {
2014
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
2015
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
2016
2017
        $fieldDefinitions = [
2018
            new FieldDefinition(
2019
                [
2020
                    'id' => 'fieldDefinitionId1',
2021
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2022
                    'isTranslatable' => false,
2023
                    'identifier' => 'identifier',
2024
                    'isRequired' => false,
2025
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
2026
                ]
2027
            ),
2028
        ];
2029
2030
        $this->assertForCreateContentContentValidationException(
2031
            $mainLanguageCode,
2032
            $structFields,
2033
            $fieldDefinitions
2034
        );
2035
    }
2036
2037
    /**
2038
     * Asserts behaviour necessary for testing ContentFieldValidationException because of required
2039
     * field being empty.
2040
     *
2041
     * @param string $mainLanguageCode
2042
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2043
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2044
     *
2045
     * @return mixed
2046
     */
2047
    protected function assertForTestCreateContentRequiredField(
2048
        $mainLanguageCode,
2049
        array $structFields,
2050
        array $fieldDefinitions
2051
    ) {
2052
        $repositoryMock = $this->getRepositoryMock();
2053
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2054
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2055
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2056
        $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...
2057
        $domainMapperMock = $this->getDomainMapperMock();
2058
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2059
        $permissionResolver = $this->getPermissionResolverMock();
2060
2061
        $contentType = new ContentType(
2062
            [
2063
                'id' => 123,
2064
                'fieldDefinitions' => $fieldDefinitions,
2065
                'nameSchema' => '<nameSchema>',
2066
            ]
2067
        );
2068
        $contentCreateStruct = new ContentCreateStruct(
2069
            [
2070
                'fields' => $structFields,
2071
                'mainLanguageCode' => $mainLanguageCode,
2072
                'contentType' => $contentType,
2073
                'alwaysAvailable' => false,
2074
                'ownerId' => 169,
2075
                'sectionId' => 1,
2076
            ]
2077
        );
2078
2079
        $languageHandlerMock->expects($this->any())
2080
            ->method('loadByLanguageCode')
2081
            ->with($this->isType('string'))
2082
            ->will(
2083
                $this->returnCallback(
2084
                    function () {
2085
                        return new Language(['id' => 4242]);
2086
                    }
2087
                )
2088
            );
2089
2090
        $contentTypeServiceMock->expects($this->once())
2091
            ->method('loadContentType')
2092
            ->with($this->equalTo($contentType->id))
2093
            ->will($this->returnValue($contentType));
2094
2095
        $repositoryMock->expects($this->once())
2096
            ->method('getContentTypeService')
2097
            ->will($this->returnValue($contentTypeServiceMock));
2098
2099
        $that = $this;
2100
        $permissionResolver->expects($this->once())
2101
            ->method('canUser')
2102
            ->with(
2103
                $this->equalTo('content'),
2104
                $this->equalTo('create'),
2105
                $this->isInstanceOf(APIContentCreateStruct::class),
2106
                $this->equalTo([])
2107
            )->will(
2108
                $this->returnCallback(
2109
                    function () use ($that, $contentCreateStruct) {
2110
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2111
2112
                        return true;
2113
                    }
2114
                )
2115
            );
2116
2117
        $domainMapperMock->expects($this->once())
2118
            ->method('getUniqueHash')
2119
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2120
            ->will(
2121
                $this->returnCallback(
2122
                    function ($object) use ($that, $contentCreateStruct) {
2123
                        $that->assertEquals($contentCreateStruct, $object);
2124
2125
                        return 'hash';
2126
                    }
2127
                )
2128
            );
2129
2130
        $fieldTypeMock->expects($this->any())
2131
            ->method('acceptValue')
2132
            ->will(
2133
                $this->returnCallback(
2134
                    function ($valueString) {
2135
                        return new ValueStub($valueString);
2136
                    }
2137
                )
2138
            );
2139
2140
        $emptyValue = self::EMPTY_FIELD_VALUE;
2141
        $fieldTypeMock->expects($this->any())
2142
            ->method('isEmptyValue')
2143
            ->will(
2144
                $this->returnCallback(
2145
                    function (ValueStub $value) use ($emptyValue) {
2146
                        return $emptyValue === (string)$value;
2147
                    }
2148
                )
2149
            );
2150
2151
        $fieldTypeMock->expects($this->any())
2152
            ->method('validate')
2153
            ->will($this->returnValue([]));
2154
2155
        $this->getFieldTypeRegistryMock()->expects($this->any())
2156
            ->method('getFieldType')
2157
            ->will($this->returnValue($fieldTypeMock));
2158
2159
        return $contentCreateStruct;
2160
    }
2161
2162 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField()
2163
    {
2164
        return [
2165
            [
2166
                'eng-US',
2167
                [
2168
                    new Field(
2169
                        [
2170
                            'fieldDefIdentifier' => 'identifier',
2171
                            'value' => self::EMPTY_FIELD_VALUE,
2172
                            'languageCode' => null,
2173
                        ]
2174
                    ),
2175
                ],
2176
                'identifier',
2177
                'eng-US',
2178
            ],
2179
        ];
2180
    }
2181
2182
    /**
2183
     * Test for the createContent() method.
2184
     *
2185
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2186
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2187
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2188
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField
2189
     */
2190
    public function testCreateContentRequiredField(
2191
        $mainLanguageCode,
2192
        $structFields,
2193
        $identifier,
2194
        $languageCode
2195
    ) {
2196
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2197
2198
        $fieldDefinitions = [
2199
            new FieldDefinition(
2200
                [
2201
                    'id' => 'fieldDefinitionId',
2202
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2203
                    'isTranslatable' => true,
2204
                    'identifier' => 'identifier',
2205
                    'isRequired' => true,
2206
                    'defaultValue' => 'defaultValue',
2207
                ]
2208
            ),
2209
        ];
2210
        $contentCreateStruct = $this->assertForTestCreateContentRequiredField(
2211
            $mainLanguageCode,
2212
            $structFields,
2213
            $fieldDefinitions
2214
        );
2215
2216
        $mockedService = $this->getPartlyMockedContentService();
2217
2218
        try {
2219
            $mockedService->createContent($contentCreateStruct, []);
2220
        } catch (ContentValidationException $e) {
2221
            $this->assertEquals(
2222
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
2223
                $e->getMessage()
2224
            );
2225
2226
            throw $e;
2227
        }
2228
    }
2229
2230
    /**
2231
     * Asserts behaviour necessary for testing ContentFieldValidationException because of
2232
     * field not being valid.
2233
     *
2234
     * @param string $mainLanguageCode
2235
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2236
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2237
     *
2238
     * @return mixed
2239
     */
2240
    protected function assertForTestCreateContentThrowsContentFieldValidationException(
2241
        $mainLanguageCode,
2242
        array $structFields,
2243
        array $fieldDefinitions
2244
    ) {
2245
        $repositoryMock = $this->getRepositoryMock();
2246
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2247
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2248
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2249
        $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...
2250
        $domainMapperMock = $this->getDomainMapperMock();
2251
        $relationProcessorMock = $this->getRelationProcessorMock();
2252
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2253
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
2254
        $permissionResolver = $this->getPermissionResolverMock();
2255
2256
        $contentType = new ContentType(
2257
            [
2258
                'id' => 123,
2259
                'fieldDefinitions' => $fieldDefinitions,
2260
                'nameSchema' => '<nameSchema>',
2261
            ]
2262
        );
2263
        $contentCreateStruct = new ContentCreateStruct(
2264
            [
2265
                'fields' => $structFields,
2266
                'mainLanguageCode' => $mainLanguageCode,
2267
                'contentType' => $contentType,
2268
                'alwaysAvailable' => false,
2269
                'ownerId' => 169,
2270
                'sectionId' => 1,
2271
            ]
2272
        );
2273
2274
        $languageHandlerMock->expects($this->any())
2275
            ->method('loadByLanguageCode')
2276
            ->with($this->isType('string'))
2277
            ->will(
2278
                $this->returnCallback(
2279
                    function () {
2280
                        return new Language(['id' => 4242]);
2281
                    }
2282
                )
2283
            );
2284
2285
        $contentTypeServiceMock->expects($this->once())
2286
            ->method('loadContentType')
2287
            ->with($this->equalTo($contentType->id))
2288
            ->will($this->returnValue($contentType));
2289
2290
        $repositoryMock->expects($this->once())
2291
            ->method('getContentTypeService')
2292
            ->will($this->returnValue($contentTypeServiceMock));
2293
2294
        $that = $this;
2295
        $permissionResolver->expects($this->once())
2296
            ->method('canUser')
2297
            ->with(
2298
                $this->equalTo('content'),
2299
                $this->equalTo('create'),
2300
                $this->isInstanceOf(APIContentCreateStruct::class),
2301
                $this->equalTo([])
2302
            )->will(
2303
                $this->returnCallback(
2304
                    function () use ($that, $contentCreateStruct) {
2305
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2306
2307
                        return true;
2308
                    }
2309
                )
2310
            );
2311
2312
        $domainMapperMock->expects($this->once())
2313
            ->method('getUniqueHash')
2314
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2315
            ->will(
2316
                $this->returnCallback(
2317
                    function ($object) use ($that, $contentCreateStruct) {
2318
                        $that->assertEquals($contentCreateStruct, $object);
2319
2320
                        return 'hash';
2321
                    }
2322
                )
2323
            );
2324
2325
        $this->getFieldTypeRegistryMock()->expects($this->any())
2326
            ->method('getFieldType')
2327
            ->will($this->returnValue($fieldTypeMock));
2328
2329
        $relationProcessorMock
2330
            ->expects($this->any())
2331
            ->method('appendFieldRelations')
2332
            ->with(
2333
                $this->isType('array'),
2334
                $this->isType('array'),
2335
                $this->isInstanceOf(SPIFieldType::class),
2336
                $this->isInstanceOf(Value::class),
2337
                $this->anything()
2338
            );
2339
2340
        $fieldValues = $this->determineValuesForCreate(
2341
            $mainLanguageCode,
2342
            $structFields,
2343
            $fieldDefinitions,
2344
            $languageCodes
2345
        );
2346
        $allFieldErrors = [];
2347
        $validateCount = 0;
2348
        $emptyValue = self::EMPTY_FIELD_VALUE;
2349
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
2350
            foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) {
2351
                $fieldTypeMock->expects($this->at($validateCount++))
2352
                    ->method('acceptValue')
2353
                    ->will(
2354
                        $this->returnCallback(
2355
                            function ($valueString) {
2356
                                return new ValueStub($valueString);
2357
                            }
2358
                        )
2359
                    );
2360
2361
                $fieldTypeMock->expects($this->at($validateCount++))
2362
                    ->method('isEmptyValue')
2363
                    ->will(
2364
                        $this->returnCallback(
2365
                            function (ValueStub $value) use ($emptyValue) {
2366
                                return $emptyValue === (string)$value;
2367
                            }
2368
                        )
2369
                    );
2370
2371
                if (self::EMPTY_FIELD_VALUE === (string)$value) {
2372
                    continue;
2373
                }
2374
2375
                $fieldTypeMock->expects($this->at($validateCount++))
2376
                    ->method('validate')
2377
                    ->with(
2378
                        $this->equalTo($fieldDefinition),
2379
                        $this->equalTo($value)
2380
                    )->will($this->returnArgument(1));
2381
2382
                $allFieldErrors[$fieldDefinition->id][$languageCode] = $value;
2383
            }
2384
        }
2385
2386
        return [$contentCreateStruct, $allFieldErrors];
2387
    }
2388
2389
    public function providerForTestCreateContentThrowsContentFieldValidationException()
2390
    {
2391
        return $this->providerForTestCreateContentNonRedundantFieldSetComplex();
2392
    }
2393
2394
    /**
2395
     * Test for the createContent() method.
2396
     *
2397
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2398
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2399
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2400
     * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException
2401
     */
2402 View Code Duplication
    public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields)
2403
    {
2404
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2405
        $this->expectExceptionMessage('Content fields did not validate');
2406
2407
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
2408
        list($contentCreateStruct, $allFieldErrors) =
2409
            $this->assertForTestCreateContentThrowsContentFieldValidationException(
2410
                $mainLanguageCode,
2411
                $structFields,
2412
                $fieldDefinitions
2413
            );
2414
2415
        $mockedService = $this->getPartlyMockedContentService();
2416
2417
        try {
2418
            $mockedService->createContent($contentCreateStruct);
2419
        } catch (ContentFieldValidationException $e) {
2420
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
2421
            throw $e;
2422
        }
2423
    }
2424
2425
    /**
2426
     * Test for the createContent() method.
2427
     *
2428
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2429
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2430
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2431
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2432
     */
2433
    public function testCreateContentWithLocations()
2434
    {
2435
        $spiFields = [
2436
            new SPIField(
2437
                [
2438
                    'fieldDefinitionId' => 'fieldDefinitionId',
2439
                    'type' => 'fieldTypeIdentifier',
2440
                    'value' => 'defaultValue',
2441
                    'languageCode' => 'eng-US',
2442
                ]
2443
            ),
2444
        ];
2445
        $fieldDefinitions = [
2446
            new FieldDefinition(
2447
                [
2448
                    'id' => 'fieldDefinitionId',
2449
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2450
                    'isTranslatable' => false,
2451
                    'identifier' => 'identifier',
2452
                    'isRequired' => false,
2453
                    'defaultValue' => 'defaultValue',
2454
                ]
2455
            ),
2456
        ];
2457
2458
        // Set up a simple case that will pass
2459
        $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]);
2460
        $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]);
2461
        $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2];
2462
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2463
            'eng-US',
2464
            [],
2465
            $spiFields,
2466
            $fieldDefinitions,
2467
            $locationCreateStructs,
2468
            false,
2469
            // Do not execute
2470
            false
2471
        );
2472
2473
        $repositoryMock = $this->getRepositoryMock();
2474
        $mockedService = $this->getPartlyMockedContentService();
2475
        $locationServiceMock = $this->getLocationServiceMock();
2476
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2477
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2478
        $domainMapperMock = $this->getDomainMapperMock();
2479
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2480
        $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]);
2481
2482
        $locationServiceMock->expects($this->at(0))
2483
            ->method('loadLocation')
2484
            ->with($this->equalTo(321))
2485
            ->will($this->returnValue($parentLocation));
2486
2487
        $locationServiceMock->expects($this->at(1))
2488
            ->method('loadLocation')
2489
            ->with($this->equalTo(654))
2490
            ->will($this->returnValue($parentLocation));
2491
2492
        $repositoryMock->expects($this->atLeastOnce())
2493
            ->method('getLocationService')
2494
            ->will($this->returnValue($locationServiceMock));
2495
2496
        $domainMapperMock->expects($this->at(1))
2497
            ->method('buildSPILocationCreateStruct')
2498
            ->with(
2499
                $this->equalTo($locationCreateStruct1),
2500
                $this->equalTo($parentLocation),
2501
                $this->equalTo(true),
2502
                $this->equalTo(null),
2503
                $this->equalTo(null)
2504
            )->will($this->returnValue($spiLocationCreateStruct));
2505
2506
        $domainMapperMock->expects($this->at(2))
2507
            ->method('buildSPILocationCreateStruct')
2508
            ->with(
2509
                $this->equalTo($locationCreateStruct2),
2510
                $this->equalTo($parentLocation),
2511
                $this->equalTo(false),
2512
                $this->equalTo(null),
2513
                $this->equalTo(null)
2514
            )->will($this->returnValue($spiLocationCreateStruct));
2515
2516
        $spiContentCreateStruct = new SPIContentCreateStruct(
2517
            [
2518
                'name' => [],
2519
                'typeId' => 123,
2520
                'sectionId' => 1,
2521
                'ownerId' => 169,
2522
                'remoteId' => 'hash',
2523
                'fields' => $spiFields,
2524
                'modified' => time(),
2525
                'initialLanguageId' => 4242,
2526
                'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct],
2527
            ]
2528
        );
2529
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2530
        ++$spiContentCreateStruct2->modified;
2531
2532
        $spiContent = new SPIContent(
2533
            [
2534
                'versionInfo' => new SPIContent\VersionInfo(
2535
                    [
2536
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2537
                        'versionNo' => 7,
2538
                    ]
2539
                ),
2540
            ]
2541
        );
2542
2543
        $handlerMock->expects($this->once())
2544
            ->method('create')
2545
            ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
2546
            ->will($this->returnValue($spiContent));
2547
2548
        $domainMapperMock->expects($this->once())
2549
            ->method('buildContentDomainObject')
2550
            ->with(
2551
                $this->isInstanceOf(SPIContent::class),
2552
                $this->isInstanceOf(APIContentType::class)
2553
            );
2554
2555
        $repositoryMock->expects($this->once())->method('commit');
2556
2557
        // Execute
2558
        $mockedService->createContent($contentCreateStruct, $locationCreateStructs);
2559
    }
2560
2561
    /**
2562
     * Test for the createContent() method.
2563
     *
2564
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2565
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2566
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2567
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2568
     */
2569
    public function testCreateContentWithLocationsDuplicateUnderParent()
2570
    {
2571
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
2572
        $this->expectExceptionMessage('Multiple LocationCreateStructs with the same parent Location \'321\' are given');
2573
2574
        $fieldDefinitions = [
2575
            new FieldDefinition(
2576
                [
2577
                    'id' => 'fieldDefinitionId',
2578
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2579
                    'isTranslatable' => false,
2580
                    'identifier' => 'identifier',
2581
                    'isRequired' => false,
2582
                    'defaultValue' => 'defaultValue',
2583
                ]
2584
            ),
2585
        ];
2586
2587
        $repositoryMock = $this->getRepositoryMock();
2588
        $mockedService = $this->getPartlyMockedContentService();
2589
        $locationServiceMock = $this->getLocationServiceMock();
2590
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2591
        $domainMapperMock = $this->getDomainMapperMock();
2592
        $permissionResolver = $this->getPermissionResolverMock();
2593
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2594
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2595
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2596
        $parentLocation = new Location(['id' => 321]);
2597
        $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]);
2598
        $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct];
2599
        $contentType = new ContentType(
2600
            [
2601
                'id' => 123,
2602
                'fieldDefinitions' => $fieldDefinitions,
2603
                'nameSchema' => '<nameSchema>',
2604
            ]
2605
        );
2606
        $contentCreateStruct = new ContentCreateStruct(
2607
            [
2608
                'fields' => [],
2609
                'mainLanguageCode' => 'eng-US',
2610
                'contentType' => $contentType,
2611
                'alwaysAvailable' => false,
2612
                'ownerId' => 169,
2613
                'sectionId' => 1,
2614
            ]
2615
        );
2616
2617
        $languageHandlerMock->expects($this->any())
2618
            ->method('loadByLanguageCode')
2619
            ->with($this->isType('string'))
2620
            ->will(
2621
                $this->returnCallback(
2622
                    function () {
2623
                        return new Language(['id' => 4242]);
2624
                    }
2625
                )
2626
            );
2627
2628
        $contentTypeServiceMock->expects($this->once())
2629
            ->method('loadContentType')
2630
            ->with($this->equalTo($contentType->id))
2631
            ->will($this->returnValue($contentType));
2632
2633
        $repositoryMock->expects($this->once())
2634
            ->method('getContentTypeService')
2635
            ->will($this->returnValue($contentTypeServiceMock));
2636
2637
        $that = $this;
2638
        $permissionResolver->expects($this->once())
2639
            ->method('canUser')
2640
            ->with(
2641
                $this->equalTo('content'),
2642
                $this->equalTo('create'),
2643
                $this->isInstanceOf(APIContentCreateStruct::class),
2644
                $this->equalTo($locationCreateStructs)
2645
            )->will(
2646
                $this->returnCallback(
2647
                    function () use ($that, $contentCreateStruct) {
2648
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2649
2650
                        return true;
2651
                    }
2652
                )
2653
            );
2654
2655
        $domainMapperMock->expects($this->once())
2656
            ->method('getUniqueHash')
2657
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2658
            ->will(
2659
                $this->returnCallback(
2660
                    function ($object) use ($that, $contentCreateStruct) {
2661
                        $that->assertEquals($contentCreateStruct, $object);
2662
2663
                        return 'hash';
2664
                    }
2665
                )
2666
            );
2667
2668
        $locationServiceMock->expects($this->once())
2669
            ->method('loadLocation')
2670
            ->with($this->equalTo(321))
2671
            ->will($this->returnValue($parentLocation));
2672
2673
        $repositoryMock->expects($this->any())
2674
            ->method('getLocationService')
2675
            ->will($this->returnValue($locationServiceMock));
2676
2677
        $domainMapperMock->expects($this->any())
2678
            ->method('buildSPILocationCreateStruct')
2679
            ->with(
2680
                $this->equalTo($locationCreateStruct),
2681
                $this->equalTo($parentLocation),
2682
                $this->equalTo(true),
2683
                $this->equalTo(null),
2684
                $this->equalTo(null)
2685
            )->will($this->returnValue($spiLocationCreateStruct));
2686
2687
        $mockedService->createContent(
2688
            $contentCreateStruct,
2689
            $locationCreateStructs
2690
        );
2691
    }
2692
2693
    /**
2694
     * Test for the createContent() method.
2695
     *
2696
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2697
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2698
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2699
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2700
     */
2701
    public function testCreateContentObjectStates()
2702
    {
2703
        $spiFields = [
2704
            new SPIField(
2705
                [
2706
                    'fieldDefinitionId' => 'fieldDefinitionId',
2707
                    'type' => 'fieldTypeIdentifier',
2708
                    'value' => 'defaultValue',
2709
                    'languageCode' => 'eng-US',
2710
                ]
2711
            ),
2712
        ];
2713
        $fieldDefinitions = [
2714
            new FieldDefinition(
2715
                [
2716
                    'id' => 'fieldDefinitionId',
2717
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2718
                    'isTranslatable' => false,
2719
                    'identifier' => 'identifier',
2720
                    'isRequired' => false,
2721
                    'defaultValue' => 'defaultValue',
2722
                ]
2723
            ),
2724
        ];
2725
        $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...
2726
            new SPIObjectStateGroup(['id' => 10]),
2727
            new SPIObjectStateGroup(['id' => 20]),
2728
        ];
2729
2730
        // Set up a simple case that will pass
2731
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2732
            'eng-US',
2733
            [],
2734
            $spiFields,
2735
            $fieldDefinitions,
2736
            [],
2737
            true,
2738
            // Do not execute
2739
            false
2740
        );
2741
        $timestamp = time();
2742
        $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}");
2743
2744
        $repositoryMock = $this->getRepositoryMock();
2745
        $mockedService = $this->getPartlyMockedContentService();
2746
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2747
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2748
        $domainMapperMock = $this->getDomainMapperMock();
2749
2750
        $this->mockGetDefaultObjectStates();
2751
        $this->mockSetDefaultObjectStates();
2752
2753
        $spiContentCreateStruct = new SPIContentCreateStruct(
2754
            [
2755
                'name' => [],
2756
                'typeId' => 123,
2757
                'sectionId' => 1,
2758
                'ownerId' => 169,
2759
                'remoteId' => 'hash',
2760
                'fields' => $spiFields,
2761
                'modified' => $timestamp,
2762
                'initialLanguageId' => 4242,
2763
                'locations' => [],
2764
            ]
2765
        );
2766
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2767
        ++$spiContentCreateStruct2->modified;
2768
2769
        $spiContent = new SPIContent(
2770
            [
2771
                'versionInfo' => new SPIContent\VersionInfo(
2772
                    [
2773
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2774
                        'versionNo' => 7,
2775
                    ]
2776
                ),
2777
            ]
2778
        );
2779
2780
        $handlerMock->expects($this->once())
2781
            ->method('create')
2782
            ->with($this->equalTo($spiContentCreateStruct))
2783
            ->will($this->returnValue($spiContent));
2784
2785
        $domainMapperMock->expects($this->once())
2786
            ->method('buildContentDomainObject')
2787
            ->with(
2788
                $this->isInstanceOf(SPIContent::class),
2789
                $this->isInstanceOf(APIContentType::class)
2790
            );
2791
2792
        $repositoryMock->expects($this->once())->method('commit');
2793
2794
        // Execute
2795
        $mockedService->createContent($contentCreateStruct, []);
2796
    }
2797
2798
    /**
2799
     * Test for the createContent() method.
2800
     *
2801
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2802
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2803
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2804
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2805
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2806
     */
2807
    public function testCreateContentWithRollback()
2808
    {
2809
        $this->expectException(\Exception::class);
2810
        $this->expectExceptionMessage('Store failed');
2811
2812
        $fieldDefinitions = [
2813
            new FieldDefinition(
2814
                [
2815
                    'id' => 'fieldDefinitionId',
2816
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2817
                    'isTranslatable' => false,
2818
                    'identifier' => 'identifier',
2819
                    'isRequired' => false,
2820
                    'defaultValue' => 'defaultValue',
2821
                ]
2822
            ),
2823
        ];
2824
2825
        // Setup a simple case that will pass
2826
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2827
            'eng-US',
2828
            [],
2829
            [],
2830
            $fieldDefinitions,
2831
            [],
2832
            false,
2833
            // Do not execute test
2834
            false
2835
        );
2836
2837
        $repositoryMock = $this->getRepositoryMock();
2838
        $repositoryMock->expects($this->never())->method('commit');
2839
        $repositoryMock->expects($this->once())->method('rollback');
2840
2841
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
2842
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
2843
        $contentHandlerMock->expects($this->once())
2844
            ->method('create')
2845
            ->with($this->anything())
2846
            ->will($this->throwException(new \Exception('Store failed')));
2847
2848
        // Execute
2849
        $this->partlyMockedContentService->createContent($contentCreateStruct, []);
2850
    }
2851
2852
    public function providerForTestUpdateContentThrowsBadStateException()
2853
    {
2854
        return [
2855
            [VersionInfo::STATUS_PUBLISHED],
2856
            [VersionInfo::STATUS_ARCHIVED],
2857
        ];
2858
    }
2859
2860
    /**
2861
     * Test for the updateContent() method.
2862
     *
2863
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2864
     * @dataProvider providerForTestUpdateContentThrowsBadStateException
2865
     */
2866
    public function testUpdateContentThrowsBadStateException($status)
2867
    {
2868
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
2869
2870
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2871
        $contentUpdateStruct = new ContentUpdateStruct();
2872
        $versionInfo = new VersionInfo(
2873
            [
2874
                'contentInfo' => new ContentInfo(['id' => 42]),
2875
                'versionNo' => 7,
2876
                'status' => $status,
2877
            ]
2878
        );
2879
        $content = new Content(
2880
            [
2881
                'versionInfo' => $versionInfo,
2882
                'internalFields' => [],
2883
            ]
2884
        );
2885
2886
        $mockedService->expects($this->once())
2887
            ->method('loadContent')
2888
            ->with(
2889
                $this->equalTo(42),
2890
                $this->equalTo(null),
2891
                $this->equalTo(7)
2892
            )->will(
2893
                $this->returnValue($content)
2894
            );
2895
2896
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2897
    }
2898
2899
    /**
2900
     * Test for the updateContent() method.
2901
     *
2902
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2903
     */
2904
    public function testUpdateContentThrowsUnauthorizedException()
2905
    {
2906
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
2907
2908
        $permissionResolverMock = $this->getPermissionResolverMock();
2909
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2910
        $contentUpdateStruct = new ContentUpdateStruct();
2911
        $versionInfo = new VersionInfo(
2912
            [
2913
                'contentInfo' => new ContentInfo(['id' => 42]),
2914
                'versionNo' => 7,
2915
                'status' => VersionInfo::STATUS_DRAFT,
2916
            ]
2917
        );
2918
        $content = new Content(
2919
            [
2920
                'versionInfo' => $versionInfo,
2921
                'internalFields' => [],
2922
            ]
2923
        );
2924
2925
        $mockedService->expects($this->once())
2926
            ->method('loadContent')
2927
            ->with(
2928
                $this->equalTo(42),
2929
                $this->equalTo(null),
2930
                $this->equalTo(7)
2931
            )->will(
2932
                $this->returnValue($content)
2933
            );
2934
2935
        $permissionResolverMock->expects($this->once())
2936
            ->method('canUser')
2937
            ->with(
2938
                $this->equalTo('content'),
2939
                $this->equalTo('edit'),
2940
                $this->equalTo($content),
2941
                $this->isType('array')
2942
            )->will($this->returnValue(false));
2943
2944
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2945
    }
2946
2947
    /**
2948
     * @param string $initialLanguageCode
2949
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2950
     * @param string[] $existingLanguages
2951
     *
2952
     * @return string[]
2953
     */
2954
    protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages)
2955
    {
2956
        $languageCodes = array_fill_keys($existingLanguages, true);
2957
        if ($initialLanguageCode !== null) {
2958
            $languageCodes[$initialLanguageCode] = true;
2959
        }
2960
2961
        foreach ($structFields as $field) {
2962
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
2963
                continue;
2964
            }
2965
2966
            $languageCodes[$field->languageCode] = true;
2967
        }
2968
2969
        return array_keys($languageCodes);
2970
    }
2971
2972
    /**
2973
     * @param string $initialLanguageCode
2974
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2975
     * @param string $mainLanguageCode
2976
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2977
     *
2978
     * @return array
2979
     */
2980
    protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions)
2981
    {
2982
        $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode;
2983
2984
        $mappedFieldDefinitions = [];
2985
        foreach ($fieldDefinitions as $fieldDefinition) {
2986
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
2987
        }
2988
2989
        $mappedStructFields = [];
2990
        foreach ($structFields as $structField) {
2991
            $identifier = $structField->fieldDefIdentifier;
2992
2993
            if ($structField->languageCode !== null) {
2994
                $languageCode = $structField->languageCode;
2995
            } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) {
2996
                $languageCode = $initialLanguageCode;
2997
            } else {
2998
                $languageCode = $mainLanguageCode;
2999
            }
3000
3001
            $mappedStructFields[$identifier][$languageCode] = (string)$structField->value;
3002
        }
3003
3004
        return $mappedStructFields;
3005
    }
3006
3007
    /**
3008
     * Returns full, possibly redundant array of field values, indexed by field definition
3009
     * identifier and language code.
3010
     *
3011
     * @param string $initialLanguageCode
3012
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3013
     * @param \eZ\Publish\Core\Repository\Values\Content\Content $content
3014
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3015
     * @param array $languageCodes
3016
     *
3017
     * @return array
3018
     */
3019
    protected function determineValuesForUpdate(
3020
        $initialLanguageCode,
3021
        array $structFields,
3022
        Content $content,
3023
        array $fieldDefinitions,
3024
        array $languageCodes
3025
    ) {
3026
        $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode;
3027
3028
        $mappedStructFields = $this->mapStructFieldsForUpdate(
3029
            $initialLanguageCode,
3030
            $structFields,
3031
            $mainLanguageCode,
3032
            $fieldDefinitions
3033
        );
3034
3035
        $values = [];
3036
3037
        foreach ($fieldDefinitions as $fieldDefinition) {
3038
            $identifier = $fieldDefinition->identifier;
3039
            foreach ($languageCodes as $languageCode) {
3040 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...
3041
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
3042
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
3043
                    } else {
3044
                        $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode];
3045
                    }
3046
                    continue;
3047
                }
3048
3049 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...
3050
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
3051
                    continue;
3052
                }
3053
3054
                if (isset($content->fields[$identifier][$languageCode])) {
3055
                    $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode];
3056
                    continue;
3057
                }
3058
3059
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
3060
            }
3061
        }
3062
3063
        return $this->stubValues($values);
3064
    }
3065
3066
    protected function stubValues(array $fieldValues)
3067
    {
3068
        foreach ($fieldValues as &$languageValues) {
3069
            foreach ($languageValues as &$value) {
3070
                $value = new ValueStub($value);
3071
            }
3072
        }
3073
3074
        return $fieldValues;
3075
    }
3076
3077
    /**
3078
     * Asserts that calling updateContent() with given API field set causes calling
3079
     * Handler::updateContent() with given SPI field set.
3080
     *
3081
     * @param string $initialLanguageCode
3082
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3083
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
3084
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $existingFields
3085
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3086
     * @param bool $execute
3087
     *
3088
     * @return mixed
3089
     */
3090
    protected function assertForTestUpdateContentNonRedundantFieldSet(
3091
        $initialLanguageCode,
3092
        array $structFields,
3093
        array $spiFields,
3094
        array $existingFields,
3095
        array $fieldDefinitions,
3096
        $execute = true
3097
    ) {
3098
        $repositoryMock = $this->getRepositoryMock();
3099
        $permissionResolverMock = $this->getPermissionResolverMock();
3100
        $permissionResolverMock
3101
            ->expects($this->once())
3102
            ->method('getCurrentUserReference')
3103
            ->willReturn(new UserReference(169));
3104
        $mockedService = $this->getPartlyMockedContentService(['loadContent', 'loadRelations'], $permissionResolverMock);
3105
        $permissionResolverMock = $this->getPermissionResolverMock();
3106
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
3107
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
3108
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
3109
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
3110
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
3111
        $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...
3112
        $domainMapperMock = $this->getDomainMapperMock();
3113
        $relationProcessorMock = $this->getRelationProcessorMock();
3114
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
3115
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
3116
        $existingLanguageCodes = array_map(
3117
            function (Field $field) {
3118
                return $field->languageCode;
3119
            },
3120
            $existingFields
3121
        );
3122
        $languageCodes = $this->determineLanguageCodesForUpdate(
3123
            $initialLanguageCode,
3124
            $structFields,
3125
            $existingLanguageCodes
3126
        );
3127
        $versionInfo = new VersionInfo(
3128
            [
3129
                'contentInfo' => new ContentInfo(
3130
                    [
3131
                        'id' => 42,
3132
                        'contentTypeId' => 24,
3133
                        'mainLanguageCode' => 'eng-GB',
3134
                    ]
3135
                ),
3136
                'versionNo' => 7,
3137
                'languageCodes' => $existingLanguageCodes,
3138
                'status' => VersionInfo::STATUS_DRAFT,
3139
            ]
3140
        );
3141
        $content = new Content(
3142
            [
3143
                'versionInfo' => $versionInfo,
3144
                'internalFields' => $existingFields,
3145
            ]
3146
        );
3147
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
3148
3149
        $languageHandlerMock->expects($this->any())
3150
            ->method('loadByLanguageCode')
3151
            ->with($this->isType('string'))
3152
            ->will(
3153
                $this->returnCallback(
3154
                    function () {
3155
                        return new Language(['id' => 4242]);
3156
                    }
3157
                )
3158
            );
3159
3160
        $mockedService->expects($this->once())
3161
            ->method('loadContent')
3162
            ->with(
3163
                $this->equalTo(42),
3164
                $this->equalTo(null),
3165
                $this->equalTo(7)
3166
            )->will(
3167
                $this->returnValue($content)
3168
            );
3169
3170
        $repositoryMock->expects($this->once())->method('beginTransaction');
3171
3172
        $permissionResolverMock->expects($this->once())
3173
            ->method('canUser')
3174
            ->with(
3175
                $this->equalTo('content'),
3176
                $this->equalTo('edit'),
3177
                $this->equalTo($content),
3178
                $this->isType('array')
3179
            )->will($this->returnValue(true));
3180
3181
        $contentTypeServiceMock->expects($this->once())
3182
            ->method('loadContentType')
3183
            ->with($this->equalTo(24))
3184
            ->will($this->returnValue($contentType));
3185
3186
        $repositoryMock->expects($this->once())
3187
            ->method('getContentTypeService')
3188
            ->will($this->returnValue($contentTypeServiceMock));
3189
3190
        $fieldTypeMock->expects($this->any())
3191
            ->method('acceptValue')
3192
            ->will(
3193
                $this->returnCallback(
3194
                    function ($valueString) {
3195
                        return new ValueStub($valueString);
3196
                    }
3197
                )
3198
            );
3199
3200
        $emptyValue = self::EMPTY_FIELD_VALUE;
3201
        $fieldTypeMock->expects($this->any())
3202
            ->method('toPersistenceValue')
3203
            ->will(
3204
                $this->returnCallback(
3205
                    function (ValueStub $value) {
3206
                        return (string)$value;
3207
                    }
3208
                )
3209
            );
3210
3211
        $fieldTypeMock->expects($this->any())
3212
            ->method('isEmptyValue')
3213
            ->will(
3214
                $this->returnCallback(
3215
                    function (ValueStub $value) use ($emptyValue) {
3216
                        return $emptyValue === (string)$value;
3217
                    }
3218
                )
3219
            );
3220
3221
        $fieldTypeMock->expects($this->any())
3222
            ->method('validate')
3223
            ->will($this->returnValue([]));
3224
3225
        $this->getFieldTypeRegistryMock()->expects($this->any())
3226
            ->method('getFieldType')
3227
            ->will($this->returnValue($fieldTypeMock));
3228
3229
        $relationProcessorMock
3230
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
3231
            ->method('appendFieldRelations')
3232
            ->with(
3233
                $this->isType('array'),
3234
                $this->isType('array'),
3235
                $this->isInstanceOf(SPIFieldType::class),
3236
                $this->isInstanceOf(Value::class),
3237
                $this->anything()
3238
            );
3239
3240
        $values = $this->determineValuesForUpdate(
3241
            $initialLanguageCode,
3242
            $structFields,
3243
            $content,
3244
            $fieldDefinitions,
3245
            $languageCodes
3246
        );
3247
        $nameSchemaServiceMock->expects($this->once())
3248
            ->method('resolveNameSchema')
3249
            ->with(
3250
                $this->equalTo($content),
3251
                $this->equalTo($values),
3252
                $this->equalTo($languageCodes)
3253
            )->will($this->returnValue([]));
3254
3255
        $existingRelations = ['RELATIONS!!!'];
3256
        $mockedService->expects($this->once())
3257
            ->method('loadRelations')
3258
            ->with($content->versionInfo)
3259
            ->will($this->returnValue($existingRelations));
3260
        $relationProcessorMock->expects($this->any())
3261
            ->method('processFieldRelations')
3262
            ->with(
3263
                $this->isType('array'),
3264
                $this->equalTo(42),
3265
                $this->isType('int'),
3266
                $this->equalTo($contentType),
3267
                $this->equalTo($existingRelations)
3268
            );
3269
3270
        $contentUpdateStruct = new ContentUpdateStruct(
3271
            [
3272
                'fields' => $structFields,
3273
                'initialLanguageCode' => $initialLanguageCode,
3274
            ]
3275
        );
3276
3277
        if ($execute) {
3278
            $spiContentUpdateStruct = new SPIContentUpdateStruct(
3279
                [
3280
                    'creatorId' => 169,
3281
                    'fields' => $spiFields,
3282
                    'modificationDate' => time(),
3283
                    'initialLanguageId' => 4242,
3284
                ]
3285
            );
3286
3287
            // During code coverage runs, timestamp might differ 1-3 seconds
3288
            $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct;
3289
            ++$spiContentUpdateStructTs1->modificationDate;
3290
3291
            $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1;
3292
            ++$spiContentUpdateStructTs2->modificationDate;
3293
3294
            $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2;
3295
            ++$spiContentUpdateStructTs3->modificationDate;
3296
3297
            $spiContent = new SPIContent(
3298
                [
3299
                    'versionInfo' => new SPIContent\VersionInfo(
3300
                        [
3301
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
3302
                            'versionNo' => 7,
3303
                        ]
3304
                    ),
3305
                ]
3306
            );
3307
3308
            $contentHandlerMock->expects($this->once())
3309
                ->method('updateContent')
3310
                ->with(
3311
                    42,
3312
                    7,
3313
                    $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3)
3314
                )
3315
                ->will($this->returnValue($spiContent));
3316
3317
            $repositoryMock->expects($this->once())->method('commit');
3318
            $domainMapperMock->expects($this->once())
3319
                ->method('buildContentDomainObject')
3320
                ->with(
3321
                    $this->isInstanceOf(SPIContent::class),
3322
                    $this->isInstanceOf(APIContentType::class)
3323
                );
3324
3325
            $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
3326
        }
3327
3328
        return [$content->versionInfo, $contentUpdateStruct];
3329
    }
3330
3331
    public function providerForTestUpdateContentNonRedundantFieldSet1()
3332
    {
3333
        $spiFields = [
3334
            new SPIField(
3335
                [
3336
                    'id' => '100',
3337
                    'fieldDefinitionId' => 'fieldDefinitionId',
3338
                    'type' => 'fieldTypeIdentifier',
3339
                    'value' => 'newValue',
3340
                    'languageCode' => 'eng-GB',
3341
                    'versionNo' => 7,
3342
                ]
3343
            ),
3344
        ];
3345
3346
        return [
3347
            // With languages set
3348
            [
3349
                'eng-GB',
3350
                [
3351
                    new Field(
3352
                        [
3353
                            'fieldDefIdentifier' => 'identifier',
3354
                            'value' => 'newValue',
3355
                            'languageCode' => 'eng-GB',
3356
                        ]
3357
                    ),
3358
                ],
3359
                $spiFields,
3360
            ],
3361
            // Without languages set
3362
            [
3363
                null,
3364
                [
3365
                    new Field(
3366
                        [
3367
                            'fieldDefIdentifier' => 'identifier',
3368
                            'value' => 'newValue',
3369
                            'languageCode' => null,
3370
                        ]
3371
                    ),
3372
                ],
3373
                $spiFields,
3374
            ],
3375
            // Adding new language without fields
3376
            [
3377
                'eng-US',
3378
                [],
3379
                [],
3380
            ],
3381
        ];
3382
    }
3383
3384
    /**
3385
     * Test for the updateContent() method.
3386
     *
3387
     * Testing the simplest use case.
3388
     *
3389
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3390
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3391
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3392
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1
3393
     */
3394 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields)
3395
    {
3396
        $existingFields = [
3397
            new Field(
3398
                [
3399
                    'id' => '100',
3400
                    'fieldDefIdentifier' => 'identifier',
3401
                    'value' => 'initialValue',
3402
                    'languageCode' => 'eng-GB',
3403
                ]
3404
            ),
3405
        ];
3406
3407
        $fieldDefinitions = [
3408
            new FieldDefinition(
3409
                [
3410
                    'id' => 'fieldDefinitionId',
3411
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3412
                    'isTranslatable' => false,
3413
                    'identifier' => 'identifier',
3414
                    'isRequired' => false,
3415
                    'defaultValue' => 'defaultValue',
3416
                ]
3417
            ),
3418
        ];
3419
3420
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3421
            $initialLanguageCode,
3422
            $structFields,
3423
            $spiFields,
3424
            $existingFields,
3425
            $fieldDefinitions
3426
        );
3427
    }
3428
3429
    public function providerForTestUpdateContentNonRedundantFieldSet2()
3430
    {
3431
        $spiFields0 = [
3432
            new SPIField(
3433
                [
3434
                    'id' => '100',
3435
                    'fieldDefinitionId' => 'fieldDefinitionId',
3436
                    'type' => 'fieldTypeIdentifier',
3437
                    'value' => 'newValue',
3438
                    'languageCode' => 'eng-GB',
3439
                    'versionNo' => 7,
3440
                ]
3441
            ),
3442
        ];
3443
        $spiFields1 = [
3444
            new SPIField(
3445
                [
3446
                    'id' => null,
3447
                    'fieldDefinitionId' => 'fieldDefinitionId',
3448
                    'type' => 'fieldTypeIdentifier',
3449
                    'value' => 'newValue',
3450
                    'languageCode' => 'eng-US',
3451
                    'versionNo' => 7,
3452
                ]
3453
            ),
3454
        ];
3455
        $spiFields2 = [
3456
            new SPIField(
3457
                [
3458
                    'id' => 100,
3459
                    'fieldDefinitionId' => 'fieldDefinitionId',
3460
                    'type' => 'fieldTypeIdentifier',
3461
                    'value' => 'newValue2',
3462
                    'languageCode' => 'eng-GB',
3463
                    'versionNo' => 7,
3464
                ]
3465
            ),
3466
            new SPIField(
3467
                [
3468
                    'id' => null,
3469
                    'fieldDefinitionId' => 'fieldDefinitionId',
3470
                    'type' => 'fieldTypeIdentifier',
3471
                    'value' => 'newValue1',
3472
                    'languageCode' => 'eng-US',
3473
                    'versionNo' => 7,
3474
                ]
3475
            ),
3476
        ];
3477
3478
        return [
3479
            // 0. With languages set
3480
            [
3481
                'eng-GB',
3482
                [
3483
                    new Field(
3484
                        [
3485
                            'fieldDefIdentifier' => 'identifier',
3486
                            'value' => 'newValue',
3487
                            'languageCode' => 'eng-GB',
3488
                        ]
3489
                    ),
3490
                ],
3491
                $spiFields0,
3492
            ],
3493
            // 1. Without languages set
3494
            [
3495
                null,
3496
                [
3497
                    new Field(
3498
                        [
3499
                            'fieldDefIdentifier' => 'identifier',
3500
                            'value' => 'newValue',
3501
                            'languageCode' => null,
3502
                        ]
3503
                    ),
3504
                ],
3505
                $spiFields0,
3506
            ],
3507
            // 2. New language with language set
3508
            [
3509
                'eng-GB',
3510
                [
3511
                    new Field(
3512
                        [
3513
                            'fieldDefIdentifier' => 'identifier',
3514
                            'value' => 'newValue',
3515
                            'languageCode' => 'eng-US',
3516
                        ]
3517
                    ),
3518
                ],
3519
                $spiFields1,
3520
            ],
3521
            // 3. New language without language set
3522
            [
3523
                'eng-US',
3524
                [
3525
                    new Field(
3526
                        [
3527
                            'fieldDefIdentifier' => 'identifier',
3528
                            'value' => 'newValue',
3529
                            'languageCode' => null,
3530
                        ]
3531
                    ),
3532
                ],
3533
                $spiFields1,
3534
            ],
3535
            // 4. New language and existing language with language set
3536
            [
3537
                'eng-GB',
3538
                [
3539
                    new Field(
3540
                        [
3541
                            'fieldDefIdentifier' => 'identifier',
3542
                            'value' => 'newValue1',
3543
                            'languageCode' => 'eng-US',
3544
                        ]
3545
                    ),
3546
                    new Field(
3547
                        [
3548
                            'fieldDefIdentifier' => 'identifier',
3549
                            'value' => 'newValue2',
3550
                            'languageCode' => 'eng-GB',
3551
                        ]
3552
                    ),
3553
                ],
3554
                $spiFields2,
3555
            ],
3556
            // 5. New language and existing language without language set
3557
            [
3558
                'eng-US',
3559
                [
3560
                    new Field(
3561
                        [
3562
                            'fieldDefIdentifier' => 'identifier',
3563
                            'value' => 'newValue1',
3564
                            'languageCode' => null,
3565
                        ]
3566
                    ),
3567
                    new Field(
3568
                        [
3569
                            'fieldDefIdentifier' => 'identifier',
3570
                            'value' => 'newValue2',
3571
                            'languageCode' => 'eng-GB',
3572
                        ]
3573
                    ),
3574
                ],
3575
                $spiFields2,
3576
            ],
3577
            // 6. Adding new language without fields
3578
            [
3579
                'eng-US',
3580
                [],
3581
                [
3582
                    new SPIField(
3583
                        [
3584
                            'id' => null,
3585
                            'fieldDefinitionId' => 'fieldDefinitionId',
3586
                            'type' => 'fieldTypeIdentifier',
3587
                            'value' => 'defaultValue',
3588
                            'languageCode' => 'eng-US',
3589
                            'versionNo' => 7,
3590
                        ]
3591
                    ),
3592
                ],
3593
            ],
3594
        ];
3595
    }
3596
3597
    /**
3598
     * Test for the updateContent() method.
3599
     *
3600
     * Testing with translatable field.
3601
     *
3602
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3603
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3604
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3605
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2
3606
     */
3607 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields)
3608
    {
3609
        $existingFields = [
3610
            new Field(
3611
                [
3612
                    'id' => '100',
3613
                    'fieldDefIdentifier' => 'identifier',
3614
                    'value' => 'initialValue',
3615
                    'languageCode' => 'eng-GB',
3616
                ]
3617
            ),
3618
        ];
3619
3620
        $fieldDefinitions = [
3621
            new FieldDefinition(
3622
                [
3623
                    'id' => 'fieldDefinitionId',
3624
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3625
                    'isTranslatable' => true,
3626
                    'identifier' => 'identifier',
3627
                    'isRequired' => false,
3628
                    'defaultValue' => 'defaultValue',
3629
                ]
3630
            ),
3631
        ];
3632
3633
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3634
            $initialLanguageCode,
3635
            $structFields,
3636
            $spiFields,
3637
            $existingFields,
3638
            $fieldDefinitions
3639
        );
3640
    }
3641
3642
    public function providerForTestUpdateContentNonRedundantFieldSet3()
3643
    {
3644
        $spiFields0 = [
3645
            new SPIField(
3646
                [
3647
                    'id' => null,
3648
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3649
                    'type' => 'fieldTypeIdentifier',
3650
                    'value' => 'newValue1',
3651
                    'languageCode' => 'eng-US',
3652
                    'versionNo' => 7,
3653
                ]
3654
            ),
3655
        ];
3656
        $spiFields1 = [
3657
            new SPIField(
3658
                [
3659
                    'id' => 100,
3660
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3661
                    'type' => 'fieldTypeIdentifier',
3662
                    'value' => 'newValue2',
3663
                    'languageCode' => 'eng-GB',
3664
                    'versionNo' => 7,
3665
                ]
3666
            ),
3667
            new SPIField(
3668
                [
3669
                    'id' => null,
3670
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3671
                    'type' => 'fieldTypeIdentifier',
3672
                    'value' => 'newValue1',
3673
                    'languageCode' => 'eng-US',
3674
                    'versionNo' => 7,
3675
                ]
3676
            ),
3677
        ];
3678
        $spiFields2 = [
3679
            new SPIField(
3680
                [
3681
                    'id' => 100,
3682
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3683
                    'type' => 'fieldTypeIdentifier',
3684
                    'value' => 'newValue2',
3685
                    'languageCode' => 'eng-GB',
3686
                    'versionNo' => 7,
3687
                ]
3688
            ),
3689
            new SPIField(
3690
                [
3691
                    'id' => null,
3692
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3693
                    'type' => 'fieldTypeIdentifier',
3694
                    'value' => 'newValue1',
3695
                    'languageCode' => 'eng-US',
3696
                    'versionNo' => 7,
3697
                ]
3698
            ),
3699
            new SPIField(
3700
                [
3701
                    'id' => 101,
3702
                    'fieldDefinitionId' => 'fieldDefinitionId2',
3703
                    'type' => 'fieldTypeIdentifier',
3704
                    'value' => 'newValue3',
3705
                    'languageCode' => 'eng-GB',
3706
                    'versionNo' => 7,
3707
                ]
3708
            ),
3709
        ];
3710
        $spiFields3 = [
3711
            new SPIField(
3712
                [
3713
                    'id' => null,
3714
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3715
                    'type' => 'fieldTypeIdentifier',
3716
                    'value' => 'defaultValue1',
3717
                    'languageCode' => 'eng-US',
3718
                    'versionNo' => 7,
3719
                ]
3720
            ),
3721
        ];
3722
3723
        return [
3724
            // 0. ew language with language set
3725
            [
3726
                'eng-US',
3727
                [
3728
                    new Field(
3729
                        [
3730
                            'fieldDefIdentifier' => 'identifier1',
3731
                            'value' => 'newValue1',
3732
                            'languageCode' => 'eng-US',
3733
                        ]
3734
                    ),
3735
                ],
3736
                $spiFields0,
3737
            ],
3738
            // 1. New language without language set
3739
            [
3740
                'eng-US',
3741
                [
3742
                    new Field(
3743
                        [
3744
                            'fieldDefIdentifier' => 'identifier1',
3745
                            'value' => 'newValue1',
3746
                            'languageCode' => null,
3747
                        ]
3748
                    ),
3749
                ],
3750
                $spiFields0,
3751
            ],
3752
            // 2. New language and existing language 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
                ],
3771
                $spiFields1,
3772
            ],
3773
            // 3. New language and existing language without language set
3774
            [
3775
                'eng-US',
3776
                [
3777
                    new Field(
3778
                        [
3779
                            'fieldDefIdentifier' => 'identifier1',
3780
                            'value' => 'newValue1',
3781
                            'languageCode' => null,
3782
                        ]
3783
                    ),
3784
                    new Field(
3785
                        [
3786
                            'fieldDefIdentifier' => 'identifier1',
3787
                            'value' => 'newValue2',
3788
                            'languageCode' => 'eng-GB',
3789
                        ]
3790
                    ),
3791
                ],
3792
                $spiFields1,
3793
            ],
3794
            // 4. New language and existing language with untranslatable field, with language set
3795
            [
3796
                'eng-US',
3797
                [
3798
                    new Field(
3799
                        [
3800
                            'fieldDefIdentifier' => 'identifier1',
3801
                            'value' => 'newValue1',
3802
                            'languageCode' => 'eng-US',
3803
                        ]
3804
                    ),
3805
                    new Field(
3806
                        [
3807
                            'fieldDefIdentifier' => 'identifier1',
3808
                            'value' => 'newValue2',
3809
                            'languageCode' => 'eng-GB',
3810
                        ]
3811
                    ),
3812
                    new Field(
3813
                        [
3814
                            'fieldDefIdentifier' => 'identifier2',
3815
                            'value' => 'newValue3',
3816
                            'languageCode' => 'eng-GB',
3817
                        ]
3818
                    ),
3819
                ],
3820
                $spiFields2,
3821
            ],
3822
            // 5. New language and existing language with untranslatable field, without language set
3823
            [
3824
                'eng-US',
3825
                [
3826
                    new Field(
3827
                        [
3828
                            'fieldDefIdentifier' => 'identifier1',
3829
                            'value' => 'newValue1',
3830
                            'languageCode' => null,
3831
                        ]
3832
                    ),
3833
                    new Field(
3834
                        [
3835
                            'fieldDefIdentifier' => 'identifier1',
3836
                            'value' => 'newValue2',
3837
                            'languageCode' => 'eng-GB',
3838
                        ]
3839
                    ),
3840
                    new Field(
3841
                        [
3842
                            'fieldDefIdentifier' => 'identifier2',
3843
                            'value' => 'newValue3',
3844
                            'languageCode' => null,
3845
                        ]
3846
                    ),
3847
                ],
3848
                $spiFields2,
3849
            ],
3850
            // 6. Adding new language without fields
3851
            [
3852
                'eng-US',
3853
                [],
3854
                $spiFields3,
3855
            ],
3856
        ];
3857
    }
3858
3859
    /**
3860
     * Test for the updateContent() method.
3861
     *
3862
     * Testing with new language and untranslatable field.
3863
     *
3864
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3865
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3866
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3867
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3
3868
     */
3869
    public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields)
3870
    {
3871
        $existingFields = [
3872
            new Field(
3873
                [
3874
                    'id' => '100',
3875
                    'fieldDefIdentifier' => 'identifier1',
3876
                    'value' => 'initialValue1',
3877
                    'languageCode' => 'eng-GB',
3878
                ]
3879
            ),
3880
            new Field(
3881
                [
3882
                    'id' => '101',
3883
                    'fieldDefIdentifier' => 'identifier2',
3884
                    'value' => 'initialValue2',
3885
                    'languageCode' => 'eng-GB',
3886
                ]
3887
            ),
3888
        ];
3889
3890
        $fieldDefinitions = [
3891
            new FieldDefinition(
3892
                [
3893
                    'id' => 'fieldDefinitionId1',
3894
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3895
                    'isTranslatable' => true,
3896
                    'identifier' => 'identifier1',
3897
                    'isRequired' => false,
3898
                    'defaultValue' => 'defaultValue1',
3899
                ]
3900
            ),
3901
            new FieldDefinition(
3902
                [
3903
                    'id' => 'fieldDefinitionId2',
3904
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3905
                    'isTranslatable' => false,
3906
                    'identifier' => 'identifier2',
3907
                    'isRequired' => false,
3908
                    'defaultValue' => 'defaultValue2',
3909
                ]
3910
            ),
3911
        ];
3912
3913
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3914
            $initialLanguageCode,
3915
            $structFields,
3916
            $spiFields,
3917
            $existingFields,
3918
            $fieldDefinitions
3919
        );
3920
    }
3921
3922
    public function providerForTestUpdateContentNonRedundantFieldSet4()
3923
    {
3924
        $spiFields0 = [
3925
            new SPIField(
3926
                [
3927
                    'id' => null,
3928
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3929
                    'type' => 'fieldTypeIdentifier',
3930
                    'value' => 'newValue1',
3931
                    'languageCode' => 'eng-US',
3932
                    'versionNo' => 7,
3933
                ]
3934
            ),
3935
        ];
3936
        $spiFields1 = [
3937
            new SPIField(
3938
                [
3939
                    'id' => 100,
3940
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3941
                    'type' => 'fieldTypeIdentifier',
3942
                    'value' => self::EMPTY_FIELD_VALUE,
3943
                    'languageCode' => 'eng-GB',
3944
                    'versionNo' => 7,
3945
                ]
3946
            ),
3947
            new SPIField(
3948
                [
3949
                    'id' => null,
3950
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3951
                    'type' => 'fieldTypeIdentifier',
3952
                    'value' => 'newValue1',
3953
                    'languageCode' => 'eng-US',
3954
                    'versionNo' => 7,
3955
                ]
3956
            ),
3957
        ];
3958
        $spiFields2 = [
3959
            new SPIField(
3960
                [
3961
                    'id' => 100,
3962
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3963
                    'type' => 'fieldTypeIdentifier',
3964
                    'value' => self::EMPTY_FIELD_VALUE,
3965
                    'languageCode' => 'eng-GB',
3966
                    'versionNo' => 7,
3967
                ]
3968
            ),
3969
        ];
3970
3971
        return [
3972
            // 0. New translation with empty field by default
3973
            [
3974
                'eng-US',
3975
                [
3976
                    new Field(
3977
                        [
3978
                            'fieldDefIdentifier' => 'identifier1',
3979
                            'value' => 'newValue1',
3980
                            'languageCode' => 'eng-US',
3981
                        ]
3982
                    ),
3983
                ],
3984
                $spiFields0,
3985
            ],
3986
            // 1. New translation with empty field by default, without language set
3987
            [
3988
                'eng-US',
3989
                [
3990
                    new Field(
3991
                        [
3992
                            'fieldDefIdentifier' => 'identifier1',
3993
                            'value' => 'newValue1',
3994
                            'languageCode' => null,
3995
                        ]
3996
                    ),
3997
                ],
3998
                $spiFields0,
3999
            ],
4000
            // 2. New translation with empty field given
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' => 'identifier2',
4014
                            'value' => self::EMPTY_FIELD_VALUE,
4015
                            'languageCode' => 'eng-US',
4016
                        ]
4017
                    ),
4018
                ],
4019
                $spiFields0,
4020
            ],
4021
            // 3. New translation with empty field given, 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' => 'identifier2',
4035
                            'value' => self::EMPTY_FIELD_VALUE,
4036
                            'languageCode' => null,
4037
                        ]
4038
                    ),
4039
                ],
4040
                $spiFields0,
4041
            ],
4042
            // 4. Updating existing language with empty value
4043
            [
4044
                'eng-US',
4045
                [
4046
                    new Field(
4047
                        [
4048
                            'fieldDefIdentifier' => 'identifier1',
4049
                            'value' => 'newValue1',
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
                $spiFields1,
4062
            ],
4063
            // 5. Updating existing language with empty value, without language set
4064
            [
4065
                'eng-US',
4066
                [
4067
                    new Field(
4068
                        [
4069
                            'fieldDefIdentifier' => 'identifier1',
4070
                            'value' => 'newValue1',
4071
                            'languageCode' => null,
4072
                        ]
4073
                    ),
4074
                    new Field(
4075
                        [
4076
                            'fieldDefIdentifier' => 'identifier1',
4077
                            'value' => self::EMPTY_FIELD_VALUE,
4078
                            'languageCode' => 'eng-GB',
4079
                        ]
4080
                    ),
4081
                ],
4082
                $spiFields1,
4083
            ],
4084
            // 6. Updating existing language with empty value and adding new language with empty value
4085
            [
4086
                'eng-US',
4087
                [
4088
                    new Field(
4089
                        [
4090
                            'fieldDefIdentifier' => 'identifier1',
4091
                            'value' => self::EMPTY_FIELD_VALUE,
4092
                            'languageCode' => 'eng-US',
4093
                        ]
4094
                    ),
4095
                    new Field(
4096
                        [
4097
                            'fieldDefIdentifier' => 'identifier1',
4098
                            'value' => self::EMPTY_FIELD_VALUE,
4099
                            'languageCode' => 'eng-GB',
4100
                        ]
4101
                    ),
4102
                ],
4103
                $spiFields2,
4104
            ],
4105
            // 7. Updating existing language with empty value and adding new language with empty value,
4106
            // without language set
4107
            [
4108
                'eng-US',
4109
                [
4110
                    new Field(
4111
                        [
4112
                            'fieldDefIdentifier' => 'identifier1',
4113
                            'value' => self::EMPTY_FIELD_VALUE,
4114
                            'languageCode' => null,
4115
                        ]
4116
                    ),
4117
                    new Field(
4118
                        [
4119
                            'fieldDefIdentifier' => 'identifier1',
4120
                            'value' => self::EMPTY_FIELD_VALUE,
4121
                            'languageCode' => 'eng-GB',
4122
                        ]
4123
                    ),
4124
                ],
4125
                $spiFields2,
4126
            ],
4127
            // 8. Adding new language with no fields given
4128
            [
4129
                'eng-US',
4130
                [],
4131
                [],
4132
            ],
4133
            // 9. Adding new language with fields
4134
            [
4135
                'eng-US',
4136
                [
4137
                    new Field(
4138
                        [
4139
                            'fieldDefIdentifier' => 'identifier1',
4140
                            'value' => self::EMPTY_FIELD_VALUE,
4141
                            'languageCode' => 'eng-US',
4142
                        ]
4143
                    ),
4144
                ],
4145
                [],
4146
            ],
4147
            // 10. Adding new language with fields, without language set
4148
            [
4149
                'eng-US',
4150
                [
4151
                    new Field(
4152
                        [
4153
                            'fieldDefIdentifier' => 'identifier1',
4154
                            'value' => self::EMPTY_FIELD_VALUE,
4155
                            'languageCode' => null,
4156
                        ]
4157
                    ),
4158
                ],
4159
                [],
4160
            ],
4161
        ];
4162
    }
4163
4164
    /**
4165
     * Test for the updateContent() method.
4166
     *
4167
     * Testing with empty values.
4168
     *
4169
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4170
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4171
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4172
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4
4173
     */
4174
    public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields)
4175
    {
4176
        $existingFields = [
4177
            new Field(
4178
                [
4179
                    'id' => '100',
4180
                    'fieldDefIdentifier' => 'identifier1',
4181
                    'value' => 'initialValue1',
4182
                    'languageCode' => 'eng-GB',
4183
                ]
4184
            ),
4185
            new Field(
4186
                [
4187
                    'id' => '101',
4188
                    'fieldDefIdentifier' => 'identifier2',
4189
                    'value' => 'initialValue2',
4190
                    'languageCode' => 'eng-GB',
4191
                ]
4192
            ),
4193
        ];
4194
4195
        $fieldDefinitions = [
4196
            new FieldDefinition(
4197
                [
4198
                    'id' => 'fieldDefinitionId1',
4199
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4200
                    'isTranslatable' => true,
4201
                    'identifier' => 'identifier1',
4202
                    'isRequired' => false,
4203
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4204
                ]
4205
            ),
4206
            new FieldDefinition(
4207
                [
4208
                    'id' => 'fieldDefinitionId2',
4209
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4210
                    'isTranslatable' => true,
4211
                    'identifier' => 'identifier2',
4212
                    'isRequired' => false,
4213
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4214
                ]
4215
            ),
4216
        ];
4217
4218
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4219
            $initialLanguageCode,
4220
            $structFields,
4221
            $spiFields,
4222
            $existingFields,
4223
            $fieldDefinitions
4224
        );
4225
    }
4226
4227
    /**
4228
     * @todo add first field empty
4229
     *
4230
     * @return array
4231
     */
4232
    public function providerForTestUpdateContentNonRedundantFieldSetComplex()
4233
    {
4234
        $spiFields0 = [
4235
            new SPIField(
4236
                [
4237
                    'id' => 100,
4238
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4239
                    'type' => 'fieldTypeIdentifier',
4240
                    'value' => 'newValue1-eng-GB',
4241
                    'languageCode' => 'eng-GB',
4242
                    'versionNo' => 7,
4243
                ]
4244
            ),
4245
            new SPIField(
4246
                [
4247
                    'id' => null,
4248
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4249
                    'type' => 'fieldTypeIdentifier',
4250
                    'value' => 'newValue4',
4251
                    'languageCode' => 'eng-US',
4252
                    'versionNo' => 7,
4253
                ]
4254
            ),
4255
        ];
4256
        $spiFields1 = [
4257
            new SPIField(
4258
                [
4259
                    'id' => 100,
4260
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4261
                    'type' => 'fieldTypeIdentifier',
4262
                    'value' => 'newValue1-eng-GB',
4263
                    'languageCode' => 'eng-GB',
4264
                    'versionNo' => 7,
4265
                ]
4266
            ),
4267
            new SPIField(
4268
                [
4269
                    'id' => null,
4270
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4271
                    'type' => 'fieldTypeIdentifier',
4272
                    'value' => 'newValue2',
4273
                    'languageCode' => 'eng-US',
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
        $spiFields2 = [
4289
            new SPIField(
4290
                [
4291
                    'id' => 100,
4292
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4293
                    'type' => 'fieldTypeIdentifier',
4294
                    'value' => 'newValue1-eng-GB',
4295
                    'languageCode' => 'eng-GB',
4296
                    'versionNo' => 7,
4297
                ]
4298
            ),
4299
            new SPIField(
4300
                [
4301
                    'id' => null,
4302
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4303
                    'type' => 'fieldTypeIdentifier',
4304
                    'value' => 'newValue2',
4305
                    'languageCode' => 'eng-US',
4306
                    'versionNo' => 7,
4307
                ]
4308
            ),
4309
            new SPIField(
4310
                [
4311
                    'id' => null,
4312
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4313
                    'type' => 'fieldTypeIdentifier',
4314
                    'value' => 'defaultValue4',
4315
                    'languageCode' => 'ger-DE',
4316
                    'versionNo' => 7,
4317
                ]
4318
            ),
4319
            new SPIField(
4320
                [
4321
                    'id' => null,
4322
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4323
                    'type' => 'fieldTypeIdentifier',
4324
                    'value' => 'defaultValue4',
4325
                    'languageCode' => 'eng-US',
4326
                    'versionNo' => 7,
4327
                ]
4328
            ),
4329
        ];
4330
4331
        return [
4332
            // 0. Add new language and update existing
4333
            [
4334
                'eng-US',
4335
                [
4336
                    new Field(
4337
                        [
4338
                            'fieldDefIdentifier' => 'identifier4',
4339
                            'value' => 'newValue4',
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
                $spiFields0,
4352
            ],
4353
            // 1. Add new language and update existing, without language set
4354
            [
4355
                'eng-US',
4356
                [
4357
                    new Field(
4358
                        [
4359
                            'fieldDefIdentifier' => 'identifier4',
4360
                            'value' => 'newValue4',
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
                $spiFields0,
4373
            ],
4374
            // 2. Add new language and update existing variant
4375
            [
4376
                'eng-US',
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
                $spiFields1,
4394
            ],
4395
            // 3. Add new language and update existing variant, without language set
4396
            [
4397
                'eng-US',
4398
                [
4399
                    new Field(
4400
                        [
4401
                            'fieldDefIdentifier' => 'identifier2',
4402
                            'value' => 'newValue2',
4403
                            'languageCode' => null,
4404
                        ]
4405
                    ),
4406
                    new Field(
4407
                        [
4408
                            'fieldDefIdentifier' => 'identifier1',
4409
                            'value' => 'newValue1-eng-GB',
4410
                            'languageCode' => 'eng-GB',
4411
                        ]
4412
                    ),
4413
                ],
4414
                $spiFields1,
4415
            ],
4416
            // 4. Update with multiple languages
4417
            [
4418
                'ger-DE',
4419
                [
4420
                    new Field(
4421
                        [
4422
                            'fieldDefIdentifier' => 'identifier2',
4423
                            'value' => 'newValue2',
4424
                            'languageCode' => 'eng-US',
4425
                        ]
4426
                    ),
4427
                    new Field(
4428
                        [
4429
                            'fieldDefIdentifier' => 'identifier1',
4430
                            'value' => 'newValue1-eng-GB',
4431
                            'languageCode' => 'eng-GB',
4432
                        ]
4433
                    ),
4434
                ],
4435
                $spiFields2,
4436
            ],
4437
            // 5. Update with multiple languages without language set
4438
            [
4439
                'ger-DE',
4440
                [
4441
                    new Field(
4442
                        [
4443
                            'fieldDefIdentifier' => 'identifier2',
4444
                            'value' => 'newValue2',
4445
                            'languageCode' => 'eng-US',
4446
                        ]
4447
                    ),
4448
                    new Field(
4449
                        [
4450
                            'fieldDefIdentifier' => 'identifier1',
4451
                            'value' => 'newValue1-eng-GB',
4452
                            'languageCode' => null,
4453
                        ]
4454
                    ),
4455
                ],
4456
                $spiFields2,
4457
            ],
4458
        ];
4459
    }
4460
4461
    protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex()
4462
    {
4463
        $existingFields = [
4464
            new Field(
4465
                [
4466
                    'id' => '100',
4467
                    'fieldDefIdentifier' => 'identifier1',
4468
                    'value' => 'initialValue1',
4469
                    'languageCode' => 'eng-GB',
4470
                ]
4471
            ),
4472
            new Field(
4473
                [
4474
                    'id' => '101',
4475
                    'fieldDefIdentifier' => 'identifier2',
4476
                    'value' => 'initialValue2',
4477
                    'languageCode' => 'eng-GB',
4478
                ]
4479
            ),
4480
            new Field(
4481
                [
4482
                    'id' => '102',
4483
                    'fieldDefIdentifier' => 'identifier3',
4484
                    'value' => 'initialValue3',
4485
                    'languageCode' => 'eng-GB',
4486
                ]
4487
            ),
4488
            new Field(
4489
                [
4490
                    'id' => '103',
4491
                    'fieldDefIdentifier' => 'identifier4',
4492
                    'value' => 'initialValue4',
4493
                    'languageCode' => 'eng-GB',
4494
                ]
4495
            ),
4496
        ];
4497
4498
        $fieldDefinitions = [
4499
            new FieldDefinition(
4500
                [
4501
                    'id' => 'fieldDefinitionId1',
4502
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4503
                    'isTranslatable' => false,
4504
                    'identifier' => 'identifier1',
4505
                    'isRequired' => false,
4506
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4507
                ]
4508
            ),
4509
            new FieldDefinition(
4510
                [
4511
                    'id' => 'fieldDefinitionId2',
4512
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4513
                    'isTranslatable' => true,
4514
                    'identifier' => 'identifier2',
4515
                    'isRequired' => false,
4516
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4517
                ]
4518
            ),
4519
            new FieldDefinition(
4520
                [
4521
                    'id' => 'fieldDefinitionId3',
4522
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4523
                    'isTranslatable' => false,
4524
                    'identifier' => 'identifier3',
4525
                    'isRequired' => false,
4526
                    'defaultValue' => 'defaultValue3',
4527
                ]
4528
            ),
4529
            new FieldDefinition(
4530
                [
4531
                    'id' => 'fieldDefinitionId4',
4532
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4533
                    'isTranslatable' => true,
4534
                    'identifier' => 'identifier4',
4535
                    'isRequired' => false,
4536
                    'defaultValue' => 'defaultValue4',
4537
                ]
4538
            ),
4539
        ];
4540
4541
        return [$existingFields, $fieldDefinitions];
4542
    }
4543
4544
    /**
4545
     * Test for the updateContent() method.
4546
     *
4547
     * Testing more complex cases.
4548
     *
4549
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4550
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4551
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4552
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex
4553
     */
4554
    public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields)
4555
    {
4556
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
4557
4558
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4559
            $initialLanguageCode,
4560
            $structFields,
4561
            $spiFields,
4562
            $existingFields,
4563
            $fieldDefinitions
4564
        );
4565
    }
4566
4567 View Code Duplication
    public function providerForTestUpdateContentWithInvalidLanguage()
4568
    {
4569
        return [
4570
            [
4571
                'eng-GB',
4572
                [
4573
                    new Field(
4574
                        [
4575
                            'fieldDefIdentifier' => 'identifier',
4576
                            'value' => 'newValue',
4577
                            'languageCode' => 'Klingon',
4578
                        ]
4579
                    ),
4580
                ],
4581
            ],
4582
            [
4583
                'Klingon',
4584
                [
4585
                    new Field(
4586
                        [
4587
                            'fieldDefIdentifier' => 'identifier',
4588
                            'value' => 'newValue',
4589
                            'languageCode' => 'eng-GB',
4590
                        ]
4591
                    ),
4592
                ],
4593
            ],
4594
        ];
4595
    }
4596
4597
    /**
4598
     * Test for the updateContent() method.
4599
     *
4600
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4601
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4602
     * @dataProvider providerForTestUpdateContentWithInvalidLanguage
4603
     */
4604
    public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields)
4605
    {
4606
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
4607
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
4608
4609
        $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...
4610
        $permissionResolverMock = $this->getPermissionResolverMock();
4611
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4612
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4613
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4614
        $versionInfo = new VersionInfo(
4615
            [
4616
                'contentInfo' => new ContentInfo(
4617
                    [
4618
                        'id' => 42,
4619
                        'contentTypeId' => 24,
4620
                        'mainLanguageCode' => 'eng-GB',
4621
                    ]
4622
                ),
4623
                'versionNo' => 7,
4624
                'languageCodes' => ['eng-GB'],
4625
                'status' => VersionInfo::STATUS_DRAFT,
4626
            ]
4627
        );
4628
        $content = new Content(
4629
            [
4630
                'versionInfo' => $versionInfo,
4631
                'internalFields' => [],
4632
            ]
4633
        );
4634
4635
        $languageHandlerMock->expects($this->any())
4636
            ->method('loadByLanguageCode')
4637
            ->with($this->isType('string'))
4638
            ->will(
4639
                $this->returnCallback(
4640 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...
4641
                        if ($languageCode === 'Klingon') {
4642
                            throw new NotFoundException('Language', 'Klingon');
4643
                        }
4644
4645
                        return new Language(['id' => 4242]);
4646
                    }
4647
                )
4648
            );
4649
4650
        $mockedService->expects($this->once())
4651
            ->method('loadContent')
4652
            ->with(
4653
                $this->equalTo(42),
4654
                $this->equalTo(null),
4655
                $this->equalTo(7)
4656
            )->will(
4657
                $this->returnValue($content)
4658
            );
4659
4660
        $permissionResolverMock->expects($this->once())
4661
            ->method('canUser')
4662
            ->with(
4663
                $this->equalTo('content'),
4664
                $this->equalTo('edit'),
4665
                $this->equalTo($content),
4666
                $this->isType('array')
4667
            )->will($this->returnValue(true));
4668
4669
        $contentUpdateStruct = new ContentUpdateStruct(
4670
            [
4671
                'fields' => $structFields,
4672
                'initialLanguageCode' => $initialLanguageCode,
4673
            ]
4674
        );
4675
4676
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4677
    }
4678
4679
    protected function assertForUpdateContentContentValidationException(
4680
        $initialLanguageCode,
4681
        $structFields,
4682
        $fieldDefinitions = []
4683
    ) {
4684
        $repositoryMock = $this->getRepositoryMock();
4685
        $permissionResolverMock = $this->getPermissionResolverMock();
4686
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4687
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4688
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4689
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4690
        $versionInfo = new VersionInfo(
4691
            [
4692
                'contentInfo' => new ContentInfo(
4693
                    [
4694
                        'id' => 42,
4695
                        'contentTypeId' => 24,
4696
                        'mainLanguageCode' => 'eng-GB',
4697
                    ]
4698
                ),
4699
                'versionNo' => 7,
4700
                'languageCodes' => ['eng-GB'],
4701
                'status' => VersionInfo::STATUS_DRAFT,
4702
            ]
4703
        );
4704
        $content = new Content(
4705
            [
4706
                'versionInfo' => $versionInfo,
4707
                'internalFields' => [],
4708
            ]
4709
        );
4710
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4711
4712
        $languageHandlerMock->expects($this->any())
4713
            ->method('loadByLanguageCode')
4714
            ->with($this->isType('string'))
4715
            ->will(
4716
                $this->returnCallback(
4717 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...
4718
                        if ($languageCode === 'Klingon') {
4719
                            throw new NotFoundException('Language', 'Klingon');
4720
                        }
4721
4722
                        return new Language(['id' => 4242]);
4723
                    }
4724
                )
4725
            );
4726
4727
        $mockedService->expects($this->once())
4728
            ->method('loadContent')
4729
            ->with(
4730
                $this->equalTo(42),
4731
                $this->equalTo(null),
4732
                $this->equalTo(7)
4733
            )->will(
4734
                $this->returnValue($content)
4735
            );
4736
4737
        $permissionResolverMock->expects($this->once())
4738
            ->method('canUser')
4739
            ->with(
4740
                $this->equalTo('content'),
4741
                $this->equalTo('edit'),
4742
                $this->equalTo($content),
4743
                $this->isType('array')
4744
            )->will($this->returnValue(true));
4745
4746
        $contentTypeServiceMock->expects($this->once())
4747
            ->method('loadContentType')
4748
            ->with($this->equalTo(24))
4749
            ->will($this->returnValue($contentType));
4750
4751
        $repositoryMock->expects($this->once())
4752
            ->method('getContentTypeService')
4753
            ->will($this->returnValue($contentTypeServiceMock));
4754
4755
        $contentUpdateStruct = new ContentUpdateStruct(
4756
            [
4757
                'fields' => $structFields,
4758
                'initialLanguageCode' => $initialLanguageCode,
4759
            ]
4760
        );
4761
4762
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4763
    }
4764
4765 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition()
4766
    {
4767
        return [
4768
            [
4769
                'eng-GB',
4770
                [
4771
                    new Field(
4772
                        [
4773
                            'fieldDefIdentifier' => 'identifier',
4774
                            'value' => 'newValue',
4775
                            'languageCode' => 'eng-GB',
4776
                        ]
4777
                    ),
4778
                ],
4779
            ],
4780
        ];
4781
    }
4782
4783
    /**
4784
     * Test for the updateContent() method.
4785
     *
4786
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4787
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4788
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4789
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition
4790
     */
4791
    public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields)
4792
    {
4793
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4794
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
4795
4796
        $this->assertForUpdateContentContentValidationException(
4797
            $initialLanguageCode,
4798
            $structFields,
4799
            []
4800
        );
4801
    }
4802
4803 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation()
4804
    {
4805
        return [
4806
            [
4807
                'eng-US',
4808
                [
4809
                    new Field(
4810
                        [
4811
                            'fieldDefIdentifier' => 'identifier',
4812
                            'value' => 'newValue',
4813
                            'languageCode' => 'eng-US',
4814
                        ]
4815
                    ),
4816
                ],
4817
            ],
4818
        ];
4819
    }
4820
4821
    /**
4822
     * Test for the updateContent() method.
4823
     *
4824
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4825
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4826
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4827
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation
4828
     */
4829 View Code Duplication
    public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields)
4830
    {
4831
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4832
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
4833
4834
        $fieldDefinitions = [
4835
            new FieldDefinition(
4836
                [
4837
                    'id' => 'fieldDefinitionId1',
4838
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4839
                    'isTranslatable' => false,
4840
                    'identifier' => 'identifier',
4841
                    'isRequired' => false,
4842
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4843
                ]
4844
            ),
4845
        ];
4846
4847
        $this->assertForUpdateContentContentValidationException(
4848
            $initialLanguageCode,
4849
            $structFields,
4850
            $fieldDefinitions
4851
        );
4852
    }
4853
4854
    public function assertForTestUpdateContentRequiredField(
4855
        $initialLanguageCode,
4856
        $structFields,
4857
        $existingFields,
4858
        $fieldDefinitions
4859
    ) {
4860
        $repositoryMock = $this->getRepositoryMock();
4861
        $permissionResolver = $this->getPermissionResolverMock();
4862
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4863
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4864
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4865
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4866
        $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...
4867
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
4868
        $existingLanguageCodes = array_map(
4869
            function (Field $field) {
4870
                return $field->languageCode;
4871
            },
4872
            $existingFields
4873
        );
4874
        $versionInfo = new VersionInfo(
4875
            [
4876
                'contentInfo' => new ContentInfo(
4877
                    [
4878
                        'id' => 42,
4879
                        'contentTypeId' => 24,
4880
                        'mainLanguageCode' => 'eng-GB',
4881
                    ]
4882
                ),
4883
                'versionNo' => 7,
4884
                'languageCodes' => $existingLanguageCodes,
4885
                'status' => VersionInfo::STATUS_DRAFT,
4886
            ]
4887
        );
4888
        $content = new Content(
4889
            [
4890
                'versionInfo' => $versionInfo,
4891
                'internalFields' => $existingFields,
4892
            ]
4893
        );
4894
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4895
4896
        $languageHandlerMock->expects($this->any())
4897
            ->method('loadByLanguageCode')
4898
            ->with($this->isType('string'))
4899
            ->will(
4900
                $this->returnCallback(
4901
                    function () {
4902
                        return new Language(['id' => 4242]);
4903
                    }
4904
                )
4905
            );
4906
4907
        $mockedService->expects($this->once())
4908
            ->method('loadContent')
4909
            ->with(
4910
                $this->equalTo(42),
4911
                $this->equalTo(null),
4912
                $this->equalTo(7)
4913
            )->will(
4914
                $this->returnValue($content)
4915
            );
4916
4917
        $permissionResolver->expects($this->once())
4918
            ->method('canUser')
4919
            ->with(
4920
                $this->equalTo('content'),
4921
                $this->equalTo('edit'),
4922
                $this->equalTo($content),
4923
                $this->isType('array')
4924
            )->will($this->returnValue(true));
4925
4926
        $contentTypeServiceMock->expects($this->once())
4927
            ->method('loadContentType')
4928
            ->with($this->equalTo(24))
4929
            ->will($this->returnValue($contentType));
4930
4931
        $repositoryMock->expects($this->once())
4932
            ->method('getContentTypeService')
4933
            ->will($this->returnValue($contentTypeServiceMock));
4934
4935
        $fieldTypeMock->expects($this->any())
4936
            ->method('acceptValue')
4937
            ->will(
4938
                $this->returnCallback(
4939
                    function ($valueString) {
4940
                        return new ValueStub($valueString);
4941
                    }
4942
                )
4943
            );
4944
4945
        $emptyValue = self::EMPTY_FIELD_VALUE;
4946
        $fieldTypeMock->expects($this->any())
4947
            ->method('isEmptyValue')
4948
            ->will(
4949
                $this->returnCallback(
4950
                    function (ValueStub $value) use ($emptyValue) {
4951
                        return $emptyValue === (string)$value;
4952
                    }
4953
                )
4954
            );
4955
4956
        $fieldTypeMock->expects($this->any())
4957
            ->method('validate')
4958
            ->with(
4959
                $this->isInstanceOf(APIFieldDefinition::class),
4960
                $this->isInstanceOf(Value::class)
4961
            );
4962
4963
        $this->getFieldTypeRegistryMock()->expects($this->any())
4964
            ->method('getFieldType')
4965
            ->will($this->returnValue($fieldTypeMock));
4966
4967
        $contentUpdateStruct = new ContentUpdateStruct(
4968
            [
4969
                'fields' => $structFields,
4970
                'initialLanguageCode' => $initialLanguageCode,
4971
            ]
4972
        );
4973
4974
        return [$content->versionInfo, $contentUpdateStruct];
4975
    }
4976
4977 View Code Duplication
    public function providerForTestUpdateContentRequiredField()
4978
    {
4979
        return [
4980
            [
4981
                'eng-US',
4982
                [
4983
                    new Field(
4984
                        [
4985
                            'fieldDefIdentifier' => 'identifier',
4986
                            'value' => self::EMPTY_FIELD_VALUE,
4987
                            'languageCode' => null,
4988
                        ]
4989
                    ),
4990
                ],
4991
                'identifier',
4992
                'eng-US',
4993
            ],
4994
        ];
4995
    }
4996
4997
    /**
4998
     * Test for the updateContent() method.
4999
     *
5000
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5001
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5002
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5003
     * @dataProvider providerForTestUpdateContentRequiredField
5004
     */
5005
    public function testUpdateContentRequiredField(
5006
        $initialLanguageCode,
5007
        $structFields,
5008
        $identifier,
5009
        $languageCode
5010
    ) {
5011
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
5012
5013
        $existingFields = [
5014
            new Field(
5015
                [
5016
                    'id' => '100',
5017
                    'fieldDefIdentifier' => 'identifier',
5018
                    'value' => 'initialValue',
5019
                    'languageCode' => 'eng-GB',
5020
                ]
5021
            ),
5022
        ];
5023
        $fieldDefinitions = [
5024
            new FieldDefinition(
5025
                [
5026
                    'id' => 'fieldDefinitionId',
5027
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5028
                    'isTranslatable' => true,
5029
                    'identifier' => 'identifier',
5030
                    'isRequired' => true,
5031
                    'defaultValue' => 'defaultValue',
5032
                ]
5033
            ),
5034
        ];
5035
        list($versionInfo, $contentUpdateStruct) =
5036
            $this->assertForTestUpdateContentRequiredField(
5037
                $initialLanguageCode,
5038
                $structFields,
5039
                $existingFields,
5040
                $fieldDefinitions
5041
            );
5042
5043
        try {
5044
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5045
        } catch (ContentValidationException $e) {
5046
            $this->assertEquals(
5047
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
5048
                $e->getMessage()
5049
            );
5050
5051
            throw $e;
5052
        }
5053
    }
5054
5055
    public function assertForTestUpdateContentThrowsContentFieldValidationException(
5056
        $initialLanguageCode,
5057
        $structFields,
5058
        $existingFields,
5059
        $fieldDefinitions
5060
    ) {
5061
        $repositoryMock = $this->getRepositoryMock();
5062
        $permissionResolverMock = $this->getPermissionResolverMock();
5063
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
5064
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
5065
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
5066
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5067
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
5068
        $existingLanguageCodes = array_map(
5069
            function (Field $field) {
5070
                return $field->languageCode;
5071
            },
5072
            $existingFields
5073
        );
5074
        $languageCodes = $this->determineLanguageCodesForUpdate(
5075
            $initialLanguageCode,
5076
            $structFields,
5077
            $existingLanguageCodes
5078
        );
5079
        $versionInfo = new VersionInfo(
5080
            [
5081
                'contentInfo' => new ContentInfo(
5082
                    [
5083
                        'id' => 42,
5084
                        'contentTypeId' => 24,
5085
                        'mainLanguageCode' => 'eng-GB',
5086
                    ]
5087
                ),
5088
                'versionNo' => 7,
5089
                'languageCodes' => $existingLanguageCodes,
5090
                'status' => VersionInfo::STATUS_DRAFT,
5091
            ]
5092
        );
5093
        $content = new Content(
5094
            [
5095
                'versionInfo' => $versionInfo,
5096
                'internalFields' => $existingFields,
5097
            ]
5098
        );
5099
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
5100
5101
        $languageHandlerMock->expects($this->any())
5102
            ->method('loadByLanguageCode')
5103
            ->with($this->isType('string'))
5104
            ->will(
5105
                $this->returnCallback(
5106
                    function () {
5107
                        return new Language(['id' => 4242]);
5108
                    }
5109
                )
5110
            );
5111
5112
        $mockedService->expects($this->once())
5113
            ->method('loadContent')
5114
            ->with(
5115
                $this->equalTo(42),
5116
                $this->equalTo(null),
5117
                $this->equalTo(7)
5118
            )->will(
5119
                $this->returnValue($content)
5120
            );
5121
5122
        $permissionResolverMock->expects($this->once())
5123
            ->method('canUser')
5124
            ->with(
5125
                $this->equalTo('content'),
5126
                $this->equalTo('edit'),
5127
                $this->equalTo($content),
5128
                $this->isType('array')
5129
            )->will($this->returnValue(true));
5130
5131
        $contentTypeServiceMock->expects($this->once())
5132
            ->method('loadContentType')
5133
            ->with($this->equalTo(24))
5134
            ->will($this->returnValue($contentType));
5135
5136
        $repositoryMock->expects($this->once())
5137
            ->method('getContentTypeService')
5138
            ->will($this->returnValue($contentTypeServiceMock));
5139
5140
        $fieldValues = $this->determineValuesForUpdate(
5141
            $initialLanguageCode,
5142
            $structFields,
5143
            $content,
5144
            $fieldDefinitions,
5145
            $languageCodes
5146
        );
5147
        $allFieldErrors = [];
5148
        $emptyValue = self::EMPTY_FIELD_VALUE;
5149
5150
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5151
            ->method('acceptValue')
5152
            ->will(
5153
                $this->returnCallback(
5154
                    function ($valueString) {
5155
                        return new ValueStub($valueString);
5156
                    }
5157
                )
5158
            );
5159
5160
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5161
            ->method('isEmptyValue')
5162
            ->will(
5163
                $this->returnCallback(
5164
                    function (ValueStub $value) use ($emptyValue) {
5165
                        return $emptyValue === (string)$value;
5166
                    }
5167
                )
5168
            );
5169
5170
        $fieldTypeMock
5171
            ->expects($this->any())
5172
            ->method('validate')
5173
            ->willReturnArgument(1);
5174
5175
        $this->getFieldTypeRegistryMock()->expects($this->any())
5176
            ->method('getFieldType')
5177
            ->will($this->returnValue($fieldTypeMock));
5178
5179
        $contentUpdateStruct = new ContentUpdateStruct(
5180
            [
5181
                'fields' => $structFields,
5182
                'initialLanguageCode' => $initialLanguageCode,
5183
            ]
5184
        );
5185
5186
        return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors];
5187
    }
5188
5189
    public function providerForTestUpdateContentThrowsContentFieldValidationException()
5190
    {
5191
        $allFieldErrors = [
5192
            [
5193
                'fieldDefinitionId1' => [
5194
                    'eng-GB' => 'newValue1-eng-GB',
5195
                    'eng-US' => 'newValue1-eng-GB',
5196
                ],
5197
                'fieldDefinitionId2' => [
5198
                    'eng-GB' => 'initialValue2',
5199
                ],
5200
                'fieldDefinitionId3' => [
5201
                    'eng-GB' => 'initialValue3',
5202
                    'eng-US' => 'initialValue3',
5203
                ],
5204
                'fieldDefinitionId4' => [
5205
                    'eng-GB' => 'initialValue4',
5206
                    'eng-US' => 'newValue4',
5207
                ],
5208
            ],
5209
            [
5210
                'fieldDefinitionId1' => [
5211
                    'eng-GB' => 'newValue1-eng-GB',
5212
                    'eng-US' => 'newValue1-eng-GB',
5213
                ],
5214
                'fieldDefinitionId2' => [
5215
                    'eng-GB' => 'initialValue2',
5216
                ],
5217
                'fieldDefinitionId3' => [
5218
                    'eng-GB' => 'initialValue3',
5219
                    'eng-US' => 'initialValue3',
5220
                ],
5221
                'fieldDefinitionId4' => [
5222
                    'eng-GB' => 'initialValue4',
5223
                    'eng-US' => 'newValue4',
5224
                ],
5225
            ],
5226
            [
5227
                'fieldDefinitionId1' => [
5228
                    'eng-GB' => 'newValue1-eng-GB',
5229
                    'eng-US' => 'newValue1-eng-GB',
5230
                ],
5231
                'fieldDefinitionId2' => [
5232
                    'eng-GB' => 'initialValue2',
5233
                    'eng-US' => 'newValue2',
5234
                ],
5235
                'fieldDefinitionId3' => [
5236
                    'eng-GB' => 'initialValue3',
5237
                    'eng-US' => 'initialValue3',
5238
                ],
5239
                'fieldDefinitionId4' => [
5240
                    'eng-GB' => 'initialValue4',
5241
                    'eng-US' => 'defaultValue4',
5242
                ],
5243
            ],
5244
            [
5245
                'fieldDefinitionId1' => [
5246
                    'eng-GB' => 'newValue1-eng-GB',
5247
                    'eng-US' => 'newValue1-eng-GB',
5248
                ],
5249
                'fieldDefinitionId2' => [
5250
                    'eng-GB' => 'initialValue2',
5251
                    'eng-US' => 'newValue2',
5252
                ],
5253
                'fieldDefinitionId3' => [
5254
                    'eng-GB' => 'initialValue3',
5255
                    'eng-US' => 'initialValue3',
5256
                ],
5257
                'fieldDefinitionId4' => [
5258
                    'eng-GB' => 'initialValue4',
5259
                    'eng-US' => 'defaultValue4',
5260
                ],
5261
            ],
5262
            [
5263
                'fieldDefinitionId1' => [
5264
                    'eng-GB' => 'newValue1-eng-GB',
5265
                    'ger-DE' => 'newValue1-eng-GB',
5266
                    'eng-US' => 'newValue1-eng-GB',
5267
                ],
5268
                'fieldDefinitionId2' => [
5269
                    'eng-GB' => 'initialValue2',
5270
                    'eng-US' => 'newValue2',
5271
                ],
5272
                'fieldDefinitionId3' => [
5273
                    'eng-GB' => 'initialValue3',
5274
                    'ger-DE' => 'initialValue3',
5275
                    'eng-US' => 'initialValue3',
5276
                ],
5277
                'fieldDefinitionId4' => [
5278
                    'eng-GB' => 'initialValue4',
5279
                    'eng-US' => 'defaultValue4',
5280
                    'ger-DE' => 'defaultValue4',
5281
                ],
5282
            ],
5283
            [
5284
                'fieldDefinitionId1' => [
5285
                    'eng-US' => 'newValue1-eng-GB',
5286
                    'ger-DE' => 'newValue1-eng-GB',
5287
                ],
5288
                'fieldDefinitionId2' => [
5289
                    'eng-US' => 'newValue2',
5290
                ],
5291
                'fieldDefinitionId3' => [
5292
                    'ger-DE' => 'initialValue3',
5293
                    'eng-US' => 'initialValue3',
5294
                ],
5295
                'fieldDefinitionId4' => [
5296
                    'ger-DE' => 'defaultValue4',
5297
                    'eng-US' => 'defaultValue4',
5298
                ],
5299
            ],
5300
        ];
5301
5302
        $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex();
5303
        $count = count($data);
5304
        for ($i = 0; $i < $count; ++$i) {
5305
            $data[$i][] = $allFieldErrors[$i];
5306
        }
5307
5308
        return $data;
5309
    }
5310
5311
    /**
5312
     * Test for the updateContent() method.
5313
     *
5314
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5315
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5316
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5317
     * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException
5318
     */
5319 View Code Duplication
    public function testUpdateContentThrowsContentFieldValidationException($initialLanguageCode, $structFields, $spiField, $allFieldErrors)
5320
    {
5321
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
5322
        $this->expectExceptionMessage('Content fields did not validate');
5323
5324
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
5325
        list($versionInfo, $contentUpdateStruct) =
5326
            $this->assertForTestUpdateContentThrowsContentFieldValidationException(
5327
                $initialLanguageCode,
5328
                $structFields,
5329
                $existingFields,
5330
                $fieldDefinitions
5331
            );
5332
5333
        try {
5334
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5335
        } catch (ContentFieldValidationException $e) {
5336
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
5337
            throw $e;
5338
        }
5339
    }
5340
5341
    /**
5342
     * Test for the updateContent() method.
5343
     *
5344
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5345
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5346
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5347
     */
5348
    public function testUpdateContentTransactionRollback()
5349
    {
5350
        $this->expectException(\Exception::class);
5351
        $this->expectExceptionMessage('Store failed');
5352
5353
        $existingFields = [
5354
            new Field(
5355
                [
5356
                    'id' => '100',
5357
                    'fieldDefIdentifier' => 'identifier',
5358
                    'value' => 'initialValue',
5359
                    'languageCode' => 'eng-GB',
5360
                ]
5361
            ),
5362
        ];
5363
5364
        $fieldDefinitions = [
5365
            new FieldDefinition(
5366
                [
5367
                    'id' => 'fieldDefinitionId',
5368
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5369
                    'isTranslatable' => false,
5370
                    'identifier' => 'identifier',
5371
                    'isRequired' => false,
5372
                    'defaultValue' => 'defaultValue',
5373
                ]
5374
            ),
5375
        ];
5376
5377
        // Setup a simple case that will pass
5378
        list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet(
5379
            'eng-US',
5380
            [],
5381
            [],
5382
            $existingFields,
5383
            $fieldDefinitions,
5384
            // Do not execute test
5385
            false
5386
        );
5387
5388
        $repositoryMock = $this->getRepositoryMock();
5389
        $repositoryMock->expects($this->never())->method('commit');
5390
        $repositoryMock->expects($this->once())->method('rollback');
5391
5392
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5393
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5394
        $contentHandlerMock->expects($this->once())
5395
            ->method('updateContent')
5396
            ->with(
5397
                $this->anything(),
5398
                $this->anything(),
5399
                $this->anything()
5400
            )->will($this->throwException(new \Exception('Store failed')));
5401
5402
        // Execute
5403
        $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5404
    }
5405
5406
    /**
5407
     * Test for the copyContent() method.
5408
     *
5409
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5410
     */
5411
    public function testCopyContentThrowsUnauthorizedException()
5412
    {
5413
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
5414
5415
        $repository = $this->getRepositoryMock();
5416
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
5417
        $contentInfo = $this->createMock(APIContentInfo::class);
5418
        $locationCreateStruct = new LocationCreateStruct();
5419
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5420
        $locationServiceMock = $this->getLocationServiceMock();
5421
        $permissionResolver = $this->getPermissionResolverMock();
5422
5423
        $repository->expects($this->once())
5424
            ->method('getLocationService')
5425
            ->will($this->returnValue($locationServiceMock))
5426
        ;
5427
5428
        $locationServiceMock->expects($this->once())
5429
            ->method('loadLocation')
5430
            ->with(
5431
                $locationCreateStruct->parentLocationId
5432
            )
5433
            ->will($this->returnValue($location))
5434
        ;
5435
5436
        $contentInfo->expects($this->any())
5437
            ->method('__get')
5438
            ->with('sectionId')
5439
            ->will($this->returnValue(42));
5440
5441
        $permissionResolver
5442
            ->method('canUser')
5443
            ->with(
5444
                'content',
5445
                'create',
5446
                $contentInfo,
5447
                [$location]
5448
            )
5449
            ->will($this->returnValue(false));
5450
5451
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
5452
        $contentService->copyContent($contentInfo, $locationCreateStruct);
5453
    }
5454
5455
    /**
5456
     * Test for the copyContent() method.
5457
     *
5458
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5459
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5460
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5461
     */
5462
    public function testCopyContent()
5463
    {
5464
        $repositoryMock = $this->getRepositoryMock();
5465
        $contentService = $this->getPartlyMockedContentService([
5466
            'internalLoadContentInfo',
5467
            'internalLoadContent',
5468
            'getUnixTimestamp',
5469
        ]);
5470
        $locationServiceMock = $this->getLocationServiceMock();
5471
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5472
        $locationCreateStruct = new LocationCreateStruct();
5473
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5474
        $user = $this->getStubbedUser(14);
5475
5476
        $permissionResolverMock = $this->getPermissionResolverMock();
5477
5478
        $permissionResolverMock
5479
            ->method('getCurrentUserReference')
5480
            ->willReturn($user);
5481
5482
        $repositoryMock
5483
            ->method('getPermissionResolver')
5484
            ->willReturn($permissionResolverMock);
5485
5486
        $repositoryMock->expects($this->exactly(3))
5487
            ->method('getLocationService')
5488
            ->will($this->returnValue($locationServiceMock));
5489
5490
        $locationServiceMock->expects($this->once())
5491
            ->method('loadLocation')
5492
            ->with($locationCreateStruct->parentLocationId)
5493
            ->will($this->returnValue($location))
5494
        ;
5495
5496
        $contentInfoMock->expects($this->any())
5497
            ->method('__get')
5498
            ->with('id')
5499
            ->will($this->returnValue(42));
5500
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5501
5502
        $versionInfoMock->expects($this->any())
5503
            ->method('__get')
5504
            ->will(
5505
                $this->returnValueMap(
5506
                    [
5507
                        ['versionNo', 123],
5508
                    ]
5509
                )
5510
            );
5511
5512
        $versionInfoMock->expects($this->once())
5513
            ->method('isDraft')
5514
            ->willReturn(true);
5515
5516
        $versionInfoMock->expects($this->once())
5517
            ->method('getContentInfo')
5518
            ->will($this->returnValue($contentInfoMock));
5519
5520
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5521
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5522
        $domainMapperMock = $this->getDomainMapperMock();
5523
5524
        $repositoryMock->expects($this->once())->method('beginTransaction');
5525
        $repositoryMock->expects($this->once())->method('commit');
5526
5527
        $permissionResolverMock
5528
            ->method('canUser')
5529
            ->willReturnMap(
5530
                [
5531
                    ['content', 'create', $contentInfoMock, [$location], true],
5532
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5533
                ]
5534
            );
5535
5536
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5537
        $spiVersionInfo = new SPIVersionInfo(
5538
            [
5539
                'contentInfo' => $spiContentInfo,
5540
                'creationDate' => 123456,
5541
            ]
5542
        );
5543
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5544
        $contentHandlerMock->expects($this->once())
5545
            ->method('copy')
5546
            ->with(42, null)
5547
            ->will($this->returnValue($spiContent));
5548
5549
        $this->mockGetDefaultObjectStates();
5550
        $this->mockSetDefaultObjectStates();
5551
5552
        $domainMapperMock->expects($this->once())
5553
            ->method('buildVersionInfoDomainObject')
5554
            ->with($spiVersionInfo)
5555
            ->will($this->returnValue($versionInfoMock));
5556
5557
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5558
        $content = $this->mockPublishVersion(123456, 126666);
5559
        $locationServiceMock->expects($this->once())
5560
            ->method('createLocation')
5561
            ->with(
5562
                $content->getVersionInfo()->getContentInfo(),
5563
                $locationCreateStruct
5564
            );
5565
5566
        $contentService->expects($this->once())
5567
            ->method('internalLoadContent')
5568
            ->with(
5569
                $content->id
5570
            )
5571
            ->will($this->returnValue($content));
5572
5573
        $contentService->expects($this->once())
5574
            ->method('getUnixTimestamp')
5575
            ->will($this->returnValue(126666));
5576
5577
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5578
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5579
    }
5580
5581
    /**
5582
     * Test for the copyContent() method.
5583
     *
5584
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5585
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5586
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5587
     */
5588
    public function testCopyContentWithVersionInfo()
5589
    {
5590
        $repositoryMock = $this->getRepositoryMock();
5591
        $contentService = $this->getPartlyMockedContentService([
5592
            'internalLoadContentInfo',
5593
            'internalLoadContent',
5594
            'getUnixTimestamp',
5595
        ]);
5596
        $locationServiceMock = $this->getLocationServiceMock();
5597
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5598
        $locationCreateStruct = new LocationCreateStruct();
5599
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5600
        $user = $this->getStubbedUser(14);
5601
5602
        $permissionResolverMock = $this->getPermissionResolverMock();
5603
5604
        $permissionResolverMock
5605
            ->method('getCurrentUserReference')
5606
            ->willReturn($user);
5607
5608
        $repositoryMock
5609
            ->method('getPermissionResolver')
5610
            ->willReturn($permissionResolverMock);
5611
5612
        $repositoryMock->expects($this->exactly(3))
5613
            ->method('getLocationService')
5614
            ->will($this->returnValue($locationServiceMock));
5615
5616
        $locationServiceMock->expects($this->once())
5617
            ->method('loadLocation')
5618
            ->with($locationCreateStruct->parentLocationId)
5619
            ->will($this->returnValue($location))
5620
        ;
5621
5622
        $contentInfoMock->expects($this->any())
5623
            ->method('__get')
5624
            ->with('id')
5625
            ->will($this->returnValue(42));
5626
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5627
5628
        $versionInfoMock->expects($this->any())
5629
            ->method('__get')
5630
            ->will(
5631
                $this->returnValueMap(
5632
                    [
5633
                        ['versionNo', 123],
5634
                    ]
5635
                )
5636
            );
5637
        $versionInfoMock->expects($this->once())
5638
            ->method('isDraft')
5639
            ->willReturn(true);
5640
        $versionInfoMock->expects($this->once())
5641
            ->method('getContentInfo')
5642
            ->will($this->returnValue($contentInfoMock));
5643
5644
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5645
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5646
        $domainMapperMock = $this->getDomainMapperMock();
5647
5648
        $repositoryMock->expects($this->once())->method('beginTransaction');
5649
        $repositoryMock->expects($this->once())->method('commit');
5650
5651
        $permissionResolverMock
5652
            ->method('canUser')
5653
            ->willReturnMap(
5654
                [
5655
                    ['content', 'create', $contentInfoMock, [$location], true],
5656
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5657
                ]
5658
            );
5659
5660
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5661
        $spiVersionInfo = new SPIVersionInfo(
5662
            [
5663
                'contentInfo' => $spiContentInfo,
5664
                'creationDate' => 123456,
5665
            ]
5666
        );
5667
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5668
        $contentHandlerMock->expects($this->once())
5669
            ->method('copy')
5670
            ->with(42, 123)
5671
            ->will($this->returnValue($spiContent));
5672
5673
        $this->mockGetDefaultObjectStates();
5674
        $this->mockSetDefaultObjectStates();
5675
5676
        $domainMapperMock->expects($this->once())
5677
            ->method('buildVersionInfoDomainObject')
5678
            ->with($spiVersionInfo)
5679
            ->will($this->returnValue($versionInfoMock));
5680
5681
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5682
        $content = $this->mockPublishVersion(123456, 126666);
5683
        $locationServiceMock->expects($this->once())
5684
            ->method('createLocation')
5685
            ->with(
5686
                $content->getVersionInfo()->getContentInfo(),
5687
                $locationCreateStruct
5688
            );
5689
5690
        $contentService->expects($this->once())
5691
            ->method('internalLoadContent')
5692
            ->with(
5693
                $content->id
5694
            )
5695
            ->will($this->returnValue($content));
5696
5697
        $contentService->expects($this->once())
5698
            ->method('getUnixTimestamp')
5699
            ->will($this->returnValue(126666));
5700
5701
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5702
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock);
5703
    }
5704
5705
    /**
5706
     * Test for the copyContent() method.
5707
     *
5708
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5709
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5710
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5711
     */
5712
    public function testCopyContentWithRollback()
5713
    {
5714
        $this->expectException(\Exception::class);
5715
        $this->expectExceptionMessage('Handler threw an exception');
5716
5717
        $repositoryMock = $this->getRepositoryMock();
5718
        $contentService = $this->getPartlyMockedContentService();
5719
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5720
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5721
        $locationCreateStruct = new LocationCreateStruct();
5722
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5723
        $locationServiceMock = $this->getLocationServiceMock();
5724
        $user = $this->getStubbedUser(14);
5725
5726
        $permissionResolverMock = $this->getPermissionResolverMock();
5727
5728
        $permissionResolverMock
5729
            ->method('getCurrentUserReference')
5730
            ->willReturn($user);
5731
5732
        $repositoryMock
5733
            ->method('getPermissionResolver')
5734
            ->willReturn($permissionResolverMock);
5735
5736
        $repositoryMock->expects($this->once())
5737
            ->method('getLocationService')
5738
            ->will($this->returnValue($locationServiceMock))
5739
        ;
5740
5741
        $locationServiceMock->expects($this->once())
5742
            ->method('loadLocation')
5743
            ->with($locationCreateStruct->parentLocationId)
5744
            ->will($this->returnValue($location))
5745
        ;
5746
5747
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5748
        $contentInfoMock->expects($this->any())
5749
            ->method('__get')
5750
            ->with('id')
5751
            ->will($this->returnValue(42));
5752
5753
        $this->mockGetDefaultObjectStates();
5754
5755
        $repositoryMock->expects($this->once())->method('beginTransaction');
5756
        $repositoryMock->expects($this->once())->method('rollback');
5757
5758
        $permissionResolverMock
5759
            ->method('canUser')
5760
            ->willReturnMap(
5761
                [
5762
                    ['content', 'create', $contentInfoMock, [$location], true],
5763
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5764
                ]
5765
            );
5766
5767
        $contentHandlerMock->expects($this->once())
5768
            ->method('copy')
5769
            ->with(42, null)
5770
            ->will($this->throwException(new Exception('Handler threw an exception')));
5771
5772
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5773
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5774
    }
5775
5776
    /**
5777
     * Reusable method for setting exceptions on buildContentDomainObject usage.
5778
     *
5779
     * Plain usage as in when content type is loaded directly.
5780
     *
5781
     * @param \eZ\Publish\SPI\Persistence\Content $spiContent
5782
     * @param array $translations
5783
     * @param bool $useAlwaysAvailable
5784
     *
5785
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Values\Content\Content
5786
     */
5787
    private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null)
5788
    {
5789
        $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId;
5790
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5791
        $repositoryMock = $this->getRepositoryMock();
5792
5793
        $contentType = new ContentType([
5794
            'id' => $contentTypeId,
5795
            'fieldDefinitions' => [],
5796
        ]);
5797
5798
        $repositoryMock->expects($this->once())
5799
            ->method('getContentTypeService')
5800
            ->willReturn($contentTypeServiceMock);
5801
5802
        $contentTypeServiceMock->expects($this->once())
5803
            ->method('loadContentType')
5804
            ->with($this->equalTo($contentTypeId))
5805
            ->willReturn($contentType);
5806
5807
        $content = $this->createMock(APIContent::class);
5808
5809
        $this->getDomainMapperMock()
5810
            ->expects($this->once())
5811
            ->method('buildContentDomainObject')
5812
            ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable)
5813
            ->willReturn($content);
5814
5815
        return $content;
5816
    }
5817
5818
    protected function mockGetDefaultObjectStates()
5819
    {
5820
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5821
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5822
5823
        $objectStateGroups = [
5824
            new SPIObjectStateGroup(['id' => 10]),
5825
            new SPIObjectStateGroup(['id' => 20]),
5826
        ];
5827
5828
        /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5829
        $objectStateHandlerMock->expects($this->once())
5830
            ->method('loadAllGroups')
5831
            ->will($this->returnValue($objectStateGroups));
5832
5833
        $objectStateHandlerMock->expects($this->at(1))
5834
            ->method('loadObjectStates')
5835
            ->with($this->equalTo(10))
5836
            ->will(
5837
                $this->returnValue(
5838
                    [
5839
                        new SPIObjectState(['id' => 11, 'groupId' => 10]),
5840
                        new SPIObjectState(['id' => 12, 'groupId' => 10]),
5841
                    ]
5842
                )
5843
            );
5844
5845
        $objectStateHandlerMock->expects($this->at(2))
5846
            ->method('loadObjectStates')
5847
            ->with($this->equalTo(20))
5848
            ->will(
5849
                $this->returnValue(
5850
                    [
5851
                        new SPIObjectState(['id' => 21, 'groupId' => 20]),
5852
                        new SPIObjectState(['id' => 22, 'groupId' => 20]),
5853
                    ]
5854
                )
5855
            );
5856
    }
5857
5858
    protected function mockSetDefaultObjectStates()
5859
    {
5860
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5861
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5862
5863
        $defaultObjectStates = [
5864
            new SPIObjectState(['id' => 11, 'groupId' => 10]),
5865
            new SPIObjectState(['id' => 21, 'groupId' => 20]),
5866
        ];
5867
        foreach ($defaultObjectStates as $index => $objectState) {
5868
            $objectStateHandlerMock->expects($this->at($index + 3))
5869
                ->method('setContentState')
5870
                ->with(
5871
                    42,
5872
                    $objectState->groupId,
5873
                    $objectState->id
5874
                );
5875
        }
5876
    }
5877
5878
    /**
5879
     * @param int|null $publicationDate
5880
     * @param int|null $modificationDate
5881
     *
5882
     * @return \eZ\Publish\API\Repository\Values\Content\Content
5883
     */
5884
    protected function mockPublishVersion($publicationDate = null, $modificationDate = null)
5885
    {
5886
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5887
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5888
        /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5889
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5890
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
5891
5892
        $spiContent = new SPIContent([
5893
            'versionInfo' => new VersionInfo([
5894
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
5895
            ]),
5896
        ]);
5897
5898
        $contentMock = $this->mockBuildContentDomainObject($spiContent);
5899
        $contentMock->expects($this->any())
5900
            ->method('__get')
5901
            ->will(
5902
                $this->returnValueMap(
5903
                    [
5904
                        ['id', 42],
5905
                        ['contentInfo', $contentInfoMock],
5906
                        ['versionInfo', $versionInfoMock],
5907
                    ]
5908
                )
5909
            );
5910
        $contentMock->expects($this->any())
5911
            ->method('getVersionInfo')
5912
            ->will($this->returnValue($versionInfoMock));
5913
        $versionInfoMock->expects($this->any())
5914
            ->method('getContentInfo')
5915
            ->will($this->returnValue($contentInfoMock));
5916
        $versionInfoMock->expects($this->any())
5917
            ->method('__get')
5918
            ->will(
5919
                $this->returnValueMap(
5920
                    [
5921
                        ['languageCodes', ['eng-GB']],
5922
                    ]
5923
                )
5924
            );
5925
        $contentInfoMock->expects($this->any())
5926
            ->method('__get')
5927
            ->will(
5928
                $this->returnValueMap(
5929
                    [
5930
                        ['alwaysAvailable', true],
5931
                        ['mainLanguageCode', 'eng-GB'],
5932
                    ]
5933
                )
5934
            );
5935
5936
        $currentTime = time();
5937
        if ($publicationDate === null && $versionInfoMock->versionNo === 1) {
5938
            $publicationDate = $currentTime;
5939
        }
5940
5941
        // Account for 1 second of test execution time
5942
        $metadataUpdateStruct->publicationDate = $publicationDate;
5943
        $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime;
5944
5945
        $contentHandlerMock->expects($this->once())
5946
            ->method('publish')
5947
            ->with(
5948
                42,
5949
                123,
5950
                $metadataUpdateStruct
5951
            )
5952
            ->will($this->returnValue($spiContent));
5953
5954
        /* @var \eZ\Publish\API\Repository\Values\Content\Content $contentMock */
5955
        $this->mockPublishUrlAliasesForContent($contentMock);
5956
5957
        return $contentMock;
5958
    }
5959
5960
    /**
5961
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
5962
     */
5963
    protected function mockPublishUrlAliasesForContent(APIContent $content)
5964
    {
5965
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
5966
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */
5967
        $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler();
5968
        $locationServiceMock = $this->getLocationServiceMock();
5969
        $location = $this->createMock(APILocation::class);
5970
5971
        $location->expects($this->at(0))
5972
            ->method('__get')
5973
            ->with('id')
5974
            ->will($this->returnValue(123));
5975
        $location->expects($this->at(1))
5976
            ->method('__get')
5977
            ->with('parentLocationId')
5978
            ->will($this->returnValue(456));
5979
5980
        $urlAliasNames = ['eng-GB' => 'hello'];
5981
        $nameSchemaServiceMock->expects($this->once())
5982
            ->method('resolveUrlAliasSchema')
5983
            ->with($content)
5984
            ->will($this->returnValue($urlAliasNames));
5985
5986
        $locationServiceMock->expects($this->once())
5987
            ->method('loadLocations')
5988
            ->with($content->getVersionInfo()->getContentInfo())
5989
            ->will($this->returnValue([$location]));
5990
5991
        $urlAliasHandlerMock->expects($this->once())
5992
            ->method('publishUrlAliasForLocation')
5993
            ->with(123, 456, 'hello', 'eng-GB', true, true);
5994
5995
        $location->expects($this->at(2))
5996
            ->method('__get')
5997
            ->with('id')
5998
            ->will($this->returnValue(123));
5999
6000
        $location->expects($this->at(3))
6001
            ->method('__get')
6002
            ->with('parentLocationId')
6003
            ->will($this->returnValue(456));
6004
6005
        $urlAliasHandlerMock->expects($this->once())
6006
            ->method('archiveUrlAliasesForDeletedTranslations')
6007
            ->with(123, 456, ['eng-GB']);
6008
    }
6009
6010
    protected $domainMapperMock;
6011
6012
    /**
6013
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\DomainMapper
6014
     */
6015
    protected function getDomainMapperMock()
6016
    {
6017
        if (!isset($this->domainMapperMock)) {
6018
            $this->domainMapperMock = $this->createMock(DomainMapper::class);
6019
        }
6020
6021
        return $this->domainMapperMock;
6022
    }
6023
6024
    protected $relationProcessorMock;
6025
6026
    /**
6027
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\RelationProcessor
6028
     */
6029
    protected function getRelationProcessorMock()
6030
    {
6031
        if (!isset($this->relationProcessorMock)) {
6032
            $this->relationProcessorMock = $this->createMock(RelationProcessor::class);
6033
        }
6034
6035
        return $this->relationProcessorMock;
6036
    }
6037
6038
    protected $nameSchemaServiceMock;
6039
6040
    /**
6041
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService
6042
     */
6043
    protected function getNameSchemaServiceMock()
6044
    {
6045
        if (!isset($this->nameSchemaServiceMock)) {
6046
            $this->nameSchemaServiceMock = $this->createMock(NameSchemaService::class);
6047
        }
6048
6049
        return $this->nameSchemaServiceMock;
6050
    }
6051
6052
    protected $contentTypeServiceMock;
6053
6054
    /**
6055
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentTypeService
6056
     */
6057
    protected function getContentTypeServiceMock()
6058
    {
6059
        if (!isset($this->contentTypeServiceMock)) {
6060
            $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class);
6061
        }
6062
6063
        return $this->contentTypeServiceMock;
6064
    }
6065
6066
    protected $locationServiceMock;
6067
6068
    /**
6069
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\LocationService
6070
     */
6071
    protected function getLocationServiceMock()
6072
    {
6073
        if (!isset($this->locationServiceMock)) {
6074
            $this->locationServiceMock = $this->createMock(APILocationService::class);
6075
        }
6076
6077
        return $this->locationServiceMock;
6078
    }
6079
6080
    /** @var \eZ\Publish\Core\Repository\ContentService */
6081
    protected $partlyMockedContentService;
6082
6083
    /**
6084
     * Returns the content service to test with $methods mocked.
6085
     *
6086
     * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()}
6087
     *
6088
     * @param string[] $methods
6089
     *
6090
     * @return \eZ\Publish\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject
6091
     */
6092
    protected function getPartlyMockedContentService(array $methods = null)
6093
    {
6094
        if (!isset($this->partlyMockedContentService)) {
6095
            $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class)
6096
                ->setMethods($methods)
6097
                ->setConstructorArgs(
6098
                    [
6099
                        $this->getRepositoryMock(),
6100
                        $this->getPersistenceMock(),
6101
                        $this->getDomainMapperMock(),
6102
                        $this->getRelationProcessorMock(),
6103
                        $this->getNameSchemaServiceMock(),
6104
                        $this->getFieldTypeRegistryMock(),
6105
                        $this->getPermissionResolverMock(),
6106
                        [],
6107
                    ]
6108
                )
6109
                ->getMock();
6110
        }
6111
6112
        return $this->partlyMockedContentService;
6113
    }
6114
6115
    /**
6116
     * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject
6117
     */
6118
    protected function getRepositoryMock(): Repository
6119
    {
6120
        $repositoryMock = parent::getRepositoryMock();
6121
        $repositoryMock
6122
            ->expects($this->any())
6123
            ->method('getPermissionResolver')
6124
            ->willReturn($this->getPermissionResolverMock());
6125
6126
        return $repositoryMock;
6127
    }
6128
}
6129