Completed
Push — enhanced_content_type_vo ( 300b39...1c72cb )
by
unknown
26:32 queued 12:29
created

ContentTest::createFieldDefinitionCollectionMock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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