BaseIntegrationTest::createContent()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\API\Repository\Tests\FieldType;
8
9
use eZ\Publish\API\Repository\Tests;
10
use eZ\Publish\API\Repository;
11
use eZ\Publish\API\Repository\Values\Content\Content;
12
use eZ\Publish\API\Repository\Values\Content\Field;
13
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition;
14
use Exception;
15
use PHPUnit_Framework_AssertionFailedError;
16
17
/**
18
 * Integration test for legacy storage field types.
19
 *
20
 * This abstract base test case is supposed to be the base for field type
21
 * integration tests. It basically calls all involved methods in the field type
22
 * ``Converter`` and ``Storage`` implementations. Fo get it working implement
23
 * the abstract methods in a sensible way.
24
 *
25
 * The following actions are performed by this test using the custom field
26
 * type:
27
 *
28
 * - Create a new content type with the given field type
29
 * - Load created content type
30
 * - Create content object of new content type
31
 * - Load created content
32
 * - Publish created content
33
 * - Update content
34
 * - Copy created content
35
 * - Remove copied content
36
 * - Test toHash
37
 * - Test fromHash
38
 *
39
 * @group integration
40
 * @group field-type
41
 *
42
 * @todo Finalize dependencies to other tests (including groups!)
43
 */
44
abstract class BaseIntegrationTest extends Tests\BaseTest
45
{
46
    /**
47
     * Content version archive limit (default).
48
     * Note: currently there is no way to retrieve this setting from the ContentService.
49
     */
50
    const VERSION_ARCHIVE_LIMIT = 5;
51
52
    /**
53
     * Identifier of the custom field.
54
     *
55
     * @var string
56
     */
57
    protected $customFieldIdentifier = 'data';
58
59
    /**
60
     * Get name of tested field type.
61
     *
62
     * @return string
63
     */
64
    abstract public function getTypeName();
65
66
    /**
67
     * Get expected settings schema.
68
     *
69
     * @return array
70
     */
71
    abstract public function getSettingsSchema();
72
73
    /**
74
     * Get a valid $fieldSettings value.
75
     *
76
     * @return mixed
77
     */
78
    abstract public function getValidFieldSettings();
79
80
    /**
81
     * Get $fieldSettings value not accepted by the field type.
82
     *
83
     * @return mixed
84
     */
85
    abstract public function getInvalidFieldSettings();
86
87
    /**
88
     * Get expected validator schema.
89
     *
90
     * @return array
91
     */
92
    abstract public function getValidatorSchema();
93
94
    /**
95
     * Get a valid $validatorConfiguration.
96
     *
97
     * @return mixed
98
     */
99
    abstract public function getValidValidatorConfiguration();
100
101
    /**
102
     * Get $validatorConfiguration not accepted by the field type.
103
     *
104
     * @return mixed
105
     */
106
    abstract public function getInvalidValidatorConfiguration();
107
108
    /**
109
     * Get initial field data for valid object creation.
110
     *
111
     * @return mixed
112
     */
113
    abstract public function getValidCreationFieldData();
114
115
    /**
116
     * Get name generated by the given field type (via fieldType->getName()).
117
     *
118
     * @return string
119
     */
120
    abstract public function getFieldName();
121
122
    /**
123
     * Asserts that the field data was loaded correctly.
124
     *
125
     * Asserts that the data provided by {@link getValidCreationFieldData()}
126
     * was stored and loaded correctly.
127
     *
128
     * @param Field $field
129
     */
130
    abstract public function assertFieldDataLoadedCorrect(Field $field);
131
132
    /**
133
     * Get field data which will result in errors during creation.
134
     *
135
     * This is a PHPUnit data provider.
136
     *
137
     * The returned records must contain of an error producing data value and
138
     * the expected exception class (from the API or SPI, not implementation
139
     * specific!) as the second element. For example:
140
     *
141
     * <code>
142
     * array(
143
     *      array(
144
     *          new DoomedValue( true ),
145
     *          'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException'
146
     *      ),
147
     *      // ...
148
     * );
149
     * </code>
150
     *
151
     * @return array[]
152
     */
153
    abstract public function provideInvalidCreationFieldData();
154
155
    /**
156
     * Get valid field data for updating content.
157
     *
158
     * @return mixed
159
     */
160
    abstract public function getValidUpdateFieldData();
161
162
    /**
163
     * Asserts the the field data was loaded correctly.
164
     *
165
     * Asserts that the data provided by {@link getValidUpdateFieldData()}
166
     * was stored and loaded correctly.
167
     *
168
     * @param Field $field
169
     */
170
    abstract public function assertUpdatedFieldDataLoadedCorrect(Field $field);
171
172
    /**
173
     * Get field data which will result in errors during update.
174
     *
175
     * This is a PHPUnit data provider.
176
     *
177
     * The returned records must contain of an error producing data value and
178
     * the expected exception class (from the API or SPI, not implementation
179
     * specific!) as the second element. For example:
180
     *
181
     * <code>
182
     * array(
183
     *      array(
184
     *          new DoomedValue( true ),
185
     *          'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException'
186
     *      ),
187
     *      // ...
188
     * );
189
     * </code>
190
     *
191
     * @return array[]
192
     */
193
    abstract public function provideInvalidUpdateFieldData();
194
195
    /**
196
     * Asserts the the field data was loaded correctly.
197
     *
198
     * Asserts that the data provided by {@link getValidCreationFieldData()}
199
     * was copied and loaded correctly.
200
     *
201
     * @param Field $field
202
     */
203
    abstract public function assertCopiedFieldDataLoadedCorrectly(Field $field);
204
205
    /**
206
     * Get data to test to hash method.
207
     *
208
     * This is a PHPUnit data provider
209
     *
210
     * The returned records must have the the original value assigned to the
211
     * first index and the expected hash result to the second. For example:
212
     *
213
     * <code>
214
     * array(
215
     *      array(
216
     *          new MyValue( true ),
217
     *          array( 'myValue' => true ),
218
     *      ),
219
     *      // ...
220
     * );
221
     * </code>
222
     *
223
     * @return array
224
     */
225
    abstract public function provideToHashData();
226
227
    /**
228
     * Get hashes and their respective converted values.
229
     *
230
     * This is a PHPUnit data provider
231
     *
232
     * The returned records must have the the input hash assigned to the
233
     * first index and the expected value result to the second. For example:
234
     *
235
     * <code>
236
     * array(
237
     *      array(
238
     *          array( 'myValue' => true ),
239
     *          new MyValue( true ),
240
     *      ),
241
     *      // ...
242
     * );
243
     * </code>
244
     *
245
     * @return array
246
     */
247
    abstract public function provideFromHashData();
248
249
    /**
250
     * Method called after content creation.
251
     *
252
     * Useful, if additional stuff should be executed (like creating the actual
253
     * user).
254
     *
255
     * We cannot just overwrite the testCreateContent method, since this messes
256
     * up PHPUnits @depends sorting of tests, so everything will be skipped.
257
     *
258
     * @param Repository\Repository $repository
259
     * @param Repository\Values\Content\Content $content
260
     */
261
    public function postCreationHook(Repository\Repository $repository, Repository\Values\Content\Content $content)
0 ignored issues
show
Unused Code introduced by
The parameter $repository is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $content is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
262
    {
263
        // Do nothing by default
264
    }
265
266
    public function getValidContentTypeConfiguration(): array
267
    {
268
        return [];
269
    }
270
271
    public function getValidFieldConfiguration(): array
272
    {
273
        return [];
274
    }
275
276
    /**
277
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType
278
     */
279
    public function testCreateContentType()
280
    {
281
        $contentType = $this->createContentType(
282
            $this->getValidFieldSettings(),
283
            $this->getValidValidatorConfiguration(),
284
            $this->getValidContentTypeConfiguration(),
285
            $this->getValidFieldConfiguration()
286
        );
287
288
        $this->assertNotNull($contentType->id);
289
290
        return $contentType;
291
    }
292
293
    /**
294
     * For checking if field type can be used in name/url schema (pattern).
295
     *
296
     * @return bool
297
     */
298
    protected function checkSupportGetName()
299
    {
300
        return true;
301
    }
302
303
    /**
304
     * Creates a content type under test with $fieldSettings and
305
     * $validatorConfiguration.
306
     *
307
     * $typeCreateOverride and $fieldCreateOverride can be used to selectively
308
     * override settings on the type create struct and field create struct.
309
     *
310
     * @param mixed $fieldSettings
311
     * @param mixed $validatorConfiguration
312
     * @param array $typeCreateOverride
313
     * @param array $fieldCreateOverride
314
     *
315
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
316
     */
317
    protected function createContentType($fieldSettings, $validatorConfiguration, array $typeCreateOverride = [], array $fieldCreateOverride = [])
318
    {
319
        $repository = $this->getRepository();
320
        $contentTypeService = $repository->getContentTypeService();
321
322
        $contentTypeIdentifier = 'test-' . $this->getTypeName();
323
324
        try {
325
            return $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier);
326
        } catch (Repository\Exceptions\NotFoundException $e) {
327
            // Move on to creating Content Type
328
        }
329
330
        $createStruct = $contentTypeService->newContentTypeCreateStruct(
331
            $contentTypeIdentifier
332
        );
333
        $createStruct->mainLanguageCode = $this->getOverride('mainLanguageCode', $typeCreateOverride, 'eng-GB');
334
        $createStruct->remoteId = $this->getTypeName();
335
        $createStruct->names = $this->getOverride('names', $typeCreateOverride, ['eng-GB' => 'Test']);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getOverride('name...ay('eng-GB' => 'Test')) of type * is incompatible with the declared type array of property $names.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
336
        $createStruct->creatorId = 14;
337
        $createStruct->creationDate = $this->createDateTime();
338
339
        if ($this->checkSupportGetName()) {
340
            $createStruct->nameSchema = '<name> <data>';
341
            $createStruct->urlAliasSchema = '<data>';
342
        }
343
344
        $nameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring');
345
        $nameFieldCreate->names = ['eng-GB' => 'Title'];
346
        $nameFieldCreate->fieldGroup = 'main';
347
        $nameFieldCreate->position = 1;
348
        $nameFieldCreate->isTranslatable = true;
349
        $createStruct->addFieldDefinition($nameFieldCreate);
350
351
        $dataFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('data', $this->getTypeName());
352
        $dataFieldCreate->names = $this->getOverride('names', $fieldCreateOverride, ['eng-GB' => 'Title']);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getOverride('name...y('eng-GB' => 'Title')) of type * is incompatible with the declared type array of property $names.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
353
        $dataFieldCreate->fieldGroup = 'main';
354
        $dataFieldCreate->position = 2;
355
        $dataFieldCreate->isTranslatable = $this->getOverride('isTranslatable', $fieldCreateOverride, false);
356
357
        // Custom settings
358
        $dataFieldCreate->fieldSettings = $fieldSettings;
359
        $dataFieldCreate->validatorConfiguration = $validatorConfiguration;
360
361
        $createStruct->addFieldDefinition($dataFieldCreate);
362
363
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
364
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
365
366
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
367
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
368
369
        return $contentType;
370
    }
371
372
    /**
373
     * Retrieves a value for $key from $overrideValues, falling back to
374
     * $default.
375
     *
376
     * @param string $key
377
     * @param array $overrideValues
378
     * @param mixed $default
379
     *
380
     * @return mixed
381
     */
382
    protected function getOverride($key, array $overrideValues, $default)
383
    {
384
        return isset($overrideValues[$key]) ? $overrideValues[$key] : $default;
385
    }
386
387
    /**
388
     * @covers \eZ\Publish\Core\FieldType\FieldType::isEmptyValue
389
     * @dataProvider providerForTestIsEmptyValue
390
     */
391
    public function testIsEmptyValue($value)
392
    {
393
        $this->assertTrue($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value));
394
    }
395
396
    abstract public function providerForTestIsEmptyValue();
397
398
    /**
399
     * @covers \eZ\Publish\Core\FieldType\FieldType::isEmptyValue
400
     * @dataProvider providerForTestIsNotEmptyValue
401
     */
402
    public function testIsNotEmptyValue($value)
403
    {
404
        $this->assertFalse($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value));
405
    }
406
407
    abstract public function providerForTestIsNotEmptyValue();
408
409
    /**
410
     * @depends testCreateContentType
411
     */
412
    public function testContentTypeField($contentType)
413
    {
414
        $this->assertSame(
415
            $this->getTypeName(),
416
            $contentType->fieldDefinitions[1]->fieldTypeIdentifier
417
        );
418
    }
419
420
    /**
421
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType
422
     * @depends testCreateContentType
423
     */
424
    public function testLoadContentTypeField()
425
    {
426
        $contentType = $this->testCreateContentType();
427
428
        $repository = $this->getRepository();
429
        $contentTypeService = $repository->getContentTypeService();
430
431
        return $contentTypeService->loadContentType($contentType->id);
432
    }
433
434
    /**
435
     * @depends testLoadContentTypeField
436
     */
437
    public function testLoadContentTypeFieldType($contentType)
438
    {
439
        $this->assertSame(
440
            $this->getTypeName(),
441
            $contentType->fieldDefinitions[1]->fieldTypeIdentifier
442
        );
443
444
        return $contentType->fieldDefinitions[1];
445
    }
446
447 View Code Duplication
    public function testSettingsSchema()
448
    {
449
        $repository = $this->getRepository();
450
        $fieldTypeService = $repository->getFieldTypeService();
451
        $fieldType = $fieldTypeService->getFieldType($this->getTypeName());
452
453
        $this->assertEquals(
454
            $this->getSettingsSchema(),
455
            $fieldType->getSettingsSchema()
456
        );
457
    }
458
459
    /**
460
     * @depends testLoadContentTypeFieldType
461
     */
462
    public function testLoadContentTypeFieldData(FieldDefinition $fieldDefinition)
463
    {
464
        $this->assertEquals(
465
            $this->getTypeName(),
466
            $fieldDefinition->fieldTypeIdentifier,
467
            'Loaded fieldTypeIdentifier does not match.'
468
        );
469
        $this->assertEquals(
470
            $this->getValidFieldSettings(),
471
            $fieldDefinition->fieldSettings,
472
            'Loaded fieldSettings do not match.'
473
        );
474
        $this->assertEquals(
475
            $this->getValidValidatorConfiguration(),
476
            $fieldDefinition->validatorConfiguration,
477
            'Loaded validatorConfiguration does not match.'
478
        );
479
    }
480
481
    /**
482
     * @depends testCreateContentType
483
     */
484
    public function testCreateContentTypeFailsWithInvalidFieldSettings()
485
    {
486
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException::class);
487
488
        $this->createContentType(
489
            $this->getInvalidFieldSettings(),
490
            $this->getValidValidatorConfiguration()
491
        );
492
    }
493
494
    public function testValidatorSchema()
495
    {
496
        $repository = $this->getRepository();
497
        $fieldTypeService = $repository->getFieldTypeService();
498
        $fieldType = $fieldTypeService->getFieldType($this->getTypeName());
499
500
        $this->assertEquals(
501
            $this->getValidatorSchema(),
502
            $fieldType->getValidatorConfigurationSchema()
503
        );
504
    }
505
506
    /**
507
     * @depends testCreateContentType
508
     */
509
    public function testCreateContentTypeFailsWithInvalidValidatorConfiguration()
510
    {
511
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException::class);
512
513
        $this->createContentType(
514
            $this->getValidFieldSettings(),
515
            $this->getInvalidValidatorConfiguration()
516
        );
517
    }
518
519
    /**
520
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent;
521
     * @depends testLoadContentTypeField
522
     */
523
    public function testCreateContent()
524
    {
525
        return $this->createContent($this->getValidCreationFieldData());
526
    }
527
528
    /**
529
     * Creates content with $fieldData.
530
     *
531
     * @param mixed $fieldData
532
     *
533
     * @return \eZ\Publish\API\Repository\Values\Content\Content
534
     */
535
    protected function createContent($fieldData, $contentType = null)
536
    {
537
        if ($contentType === null) {
538
            $contentType = $this->testCreateContentType();
539
        }
540
541
        $repository = $this->getRepository();
542
        $contentService = $repository->getContentService();
543
544
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US');
545
        $createStruct->setField('name', 'Test object');
546
        $createStruct->setField(
547
            'data',
548
            $fieldData
549
        );
550
551
        $createStruct->remoteId = 'abcdef0123456789abcdef0123456789';
552
        $createStruct->alwaysAvailable = true;
553
554
        return $contentService->createContent($createStruct);
555
    }
556
557
    /**
558
     * Create multilingual content of given name and FT-specific data.
559
     *
560
     * @param array $names Content names in the form of <code>[languageCode => name]</code>
561
     * @param array $fieldData FT-specific data in the form of <code>[languageCode => data]</code>
562
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
563
     *
564
     * @return \eZ\Publish\API\Repository\Values\Content\Content
565
     *
566
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
567
     * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException
568
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
569
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
570
     */
571
    protected function createMultilingualContent(array $names, array $fieldData, array $locationCreateStructs = [])
572
    {
573
        self::assertEquals(array_keys($names), array_keys($fieldData), 'Languages passed to names and data differ');
574
575
        $contentType = $this->createContentType(
576
            $this->getValidFieldSettings(),
577
            $this->getValidValidatorConfiguration(),
578
            $this->getValidContentTypeConfiguration(),
579
            array_merge(
580
                $this->getValidFieldConfiguration(),
581
                ['isTranslatable' => true]
582
            )
583
        );
584
585
        $repository = $this->getRepository();
586
        $contentService = $repository->getContentService();
587
588
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US');
589
        foreach ($names as $languageCode => $name) {
590
            $createStruct->setField('name', $name, $languageCode);
591
        }
592
        foreach ($fieldData as $languageCode => $value) {
593
            $createStruct->setField('data', $value, $languageCode);
594
        }
595
596
        $createStruct->remoteId = md5(uniqid('', true) . microtime());
597
        $createStruct->alwaysAvailable = true;
598
599
        return $contentService->createContent($createStruct, $locationCreateStructs);
600
    }
601
602
    /**
603
     * @depends testCreateContent
604
     */
605
    public function testCreatedFieldType($content)
606
    {
607
        foreach ($content->getFields() as $field) {
608
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
609
                return $field;
610
            }
611
        }
612
613
        $this->fail('Custom field not found.');
614
    }
615
616
    /**
617
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion
618
     * @depends testCreateContent
619
     */
620
    public function testPublishContent()
621
    {
622
        $draft = $this->testCreateContent();
623
624
        if (!$draft->getVersionInfo()->isDraft()) {
625
            $this->markTestSkipped('Provided content object is not a draft.');
626
        }
627
628
        $repository = $this->getRepository();
629
        $contentService = $repository->getContentService();
630
631
        return $contentService->publishVersion($draft->getVersionInfo());
632
    }
633
634
    /**
635
     * @depends testPublishContent
636
     */
637
    public function testPublishedFieldType($content)
638
    {
639
        foreach ($content->getFields() as $field) {
640
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
641
                return $field;
642
            }
643
        }
644
645
        $this->fail('Custom field not found.');
646
    }
647
648
    /**
649
     * @depends testPublishContent
650
     */
651
    public function testPublishedName(Content $content)
652
    {
653
        $this->assertEquals(
654
            $content->getFieldValue('name') . ' ' . $this->getFieldName(),
655
            $content->contentInfo->name
656
        );
657
    }
658
659
    /**
660
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
661
     * @depends testCreateContent
662
     */
663 View Code Duplication
    public function testLoadField()
664
    {
665
        $content = $this->testCreateContent();
666
667
        $repository = $this->getRepository();
668
        $contentService = $repository->getContentService();
669
670
        return $contentService->loadContent($content->contentInfo->id);
671
    }
672
673
    /**
674
     * @depends testLoadField
675
     */
676
    public function testLoadFieldType()
677
    {
678
        $content = $this->testCreateContent();
679
680
        foreach ($content->getFields() as $field) {
681
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
682
                return $field;
683
            }
684
        }
685
686
        $this->fail('Custom field not found.');
687
    }
688
689
    /**
690
     * @depends testLoadFieldType
691
     */
692
    public function testLoadExternalData()
693
    {
694
        $this->assertFieldDataLoadedCorrect($this->testLoadFieldType());
0 ignored issues
show
Bug introduced by
It seems like $this->testLoadFieldType() can be null; however, assertFieldDataLoadedCorrect() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
695
    }
696
697
    public function testCreateContentWithEmptyFieldValue()
698
    {
699
        /** @var \eZ\Publish\Core\FieldType\FieldType $fieldType */
700
        $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName());
701
702
        return $this->createContent($fieldType->getEmptyValue());
703
    }
704
705
    /**
706
     * Test that publishing (and thus indexing) content with an empty field value does not fail.
707
     *
708
     * @depends testCreateContentWithEmptyFieldValue
709
     *
710
     * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft
711
     */
712
    public function testPublishContentWithEmptyFieldValue(Content $contentDraft)
713
    {
714
        $this->getRepository(false)->getContentService()->publishVersion(
715
            $contentDraft->versionInfo
716
        );
717
    }
718
719
    /**
720
     * @depends testCreateContentWithEmptyFieldValue
721
     */
722
    public function testCreatedEmptyFieldValue($content)
723
    {
724
        foreach ($content->getFields() as $field) {
725
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
726
                return $field;
727
            }
728
        }
729
730
        $this->fail('Custom field not found.');
731
    }
732
733
    /**
734
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent
735
     * @depends testCreateContentWithEmptyFieldValue
736
     * @group xx
737
     */
738 View Code Duplication
    public function testLoadEmptyFieldValue()
739
    {
740
        $content = $this->testCreateContentWithEmptyFieldValue();
741
742
        $repository = $this->getRepository();
743
        $contentService = $repository->getContentService();
744
745
        return $contentService->loadContent($content->contentInfo->id);
746
    }
747
748
    /**
749
     * @depends testLoadEmptyFieldValue
750
     */
751
    public function testLoadEmptyFieldValueType($content)
752
    {
753
        foreach ($content->getFields() as $field) {
754
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
755
                return $field;
756
            }
757
        }
758
759
        $this->fail('Custom field not found.');
760
    }
761
762
    /**
763
     * @depends testLoadEmptyFieldValueType
764
     */
765
    public function testLoadEmptyFieldValueData($field)
766
    {
767
        /** @var \eZ\Publish\Core\FieldType\FieldType $fieldType */
768
        $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName());
769
770
        // @todo either test this not using acceptValue, or add to API (but is not meant for high level API, so..)
771
        $refObject = new \ReflectionObject($fieldType);
772
        $refProperty = $refObject->getProperty('internalFieldType');
773
        $refProperty->setAccessible(true);
774
        $spiFieldType = $refProperty->getValue($fieldType);
775
776
        $this->assertEquals(
777
            $fieldType->getEmptyValue(),
778
            $spiFieldType->acceptValue($field->value)
779
        );
780
    }
781
782
    /**
783
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
784
     * @depends testLoadFieldType
785
     */
786
    public function testUpdateField()
787
    {
788
        return $this->updateContent($this->getValidUpdateFieldData());
789
    }
790
791
    /**
792
     * Updates the standard published content object with $fieldData.
793
     *
794
     * @param mixed $fieldData
795
     * @param bool $setField If false the update struct will be empty (field value will not be set)
796
     *
797
     * @return \eZ\Publish\API\Repository\Values\Content\Content
798
     */
799
    public function updateContent($fieldData, $setField = true)
800
    {
801
        $content = $this->testPublishContent();
802
803
        $repository = $this->getRepository();
804
        $contentService = $repository->getContentService();
805
806
        $draft = $contentService->createContentDraft($content->contentInfo);
807
808
        $updateStruct = $contentService->newContentUpdateStruct();
809
        if ($setField) {
810
            $updateStruct->setField(
811
                $this->customFieldIdentifier,
812
                $fieldData
813
            );
814
        }
815
816
        return $contentService->updateContent($draft->versionInfo, $updateStruct);
817
    }
818
819
    /**
820
     * @depends testUpdateField
821
     */
822
    public function testUpdateTypeFieldStillAvailable($content)
823
    {
824
        foreach ($content->getFields() as $field) {
825
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
826
                return $field;
827
            }
828
        }
829
830
        $this->fail('Custom field not found.');
831
    }
832
833
    /**
834
     * @depends testUpdateTypeFieldStillAvailable
835
     */
836
    public function testUpdatedDataCorrect(Field $field)
837
    {
838
        $this->assertUpdatedFieldDataLoadedCorrect($field);
839
    }
840
841
    /**
842
     * Tests creeating a new version keeps the existing value.
843
     *
844
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent
845
     */
846
    public function testUpdateFieldNoNewContent()
847
    {
848
        return $this->updateContent(null, false);
849
    }
850
851
    /**
852
     * @depends testUpdateFieldNoNewContent
853
     */
854
    public function testUpdateNoNewContentTypeFieldStillAvailable($content)
855
    {
856
        foreach ($content->getFields() as $field) {
857
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
858
                return $field;
859
            }
860
        }
861
862
        $this->fail('Custom field not found.');
863
    }
864
865
    /**
866
     * @depends testUpdateNoNewContentTypeFieldStillAvailable
867
     */
868
    public function testUpdatedNoNewContentDataCorrect(Field $field)
869
    {
870
        $this->assertFieldDataLoadedCorrect($field);
871
    }
872
873
    /**
874
     * @depends testCreateContent
875
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent
876
     */
877
    public function testCopyField($content)
878
    {
879
        $content = $this->testCreateContent();
880
881
        $repository = $this->getRepository();
882
        $contentService = $repository->getContentService();
883
884
        $locationService = $repository->getLocationService();
885
        $parentLocationId = $this->generateId('location', 2);
886
        $locationCreate = $locationService->newLocationCreateStruct($parentLocationId);
887
888
        $copied = $contentService->copyContent($content->contentInfo, $locationCreate);
889
890
        $this->assertNotSame(
891
            $content->contentInfo->id,
892
            $copied->contentInfo->id
893
        );
894
895
        return $contentService->loadContent($copied->id);
896
    }
897
898
    /**
899
     * @depends testCopyField
900
     */
901
    public function testCopiedFieldType($content)
902
    {
903
        foreach ($content->getFields() as $field) {
904
            if ($field->fieldDefIdentifier === $this->customFieldIdentifier) {
905
                return $field;
906
            }
907
        }
908
909
        $this->fail('Custom field not found.');
910
    }
911
912
    /**
913
     * @depends testCopiedFieldType
914
     */
915
    public function testCopiedExternalData(Field $field)
916
    {
917
        $this->assertCopiedFieldDataLoadedCorrectly($field);
918
    }
919
920
    /**
921
     * @depends testCopyField
922
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::deleteContent
923
     */
924
    public function testDeleteContent($content)
0 ignored issues
show
Unused Code introduced by
The parameter $content is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
925
    {
926
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
927
928
        $content = $this->testPublishContent();
929
930
        $repository = $this->getRepository();
931
        $contentService = $repository->getContentService();
932
933
        $contentService->deleteContent($content->contentInfo);
934
935
        $contentService->loadContent($content->contentInfo->id);
936
    }
937
938
    /**
939
     * Tests failing content creation.
940
     *
941
     * @param mixed $failingValue
942
     * @param string $expectedException
943
     *
944
     * @dataProvider provideInvalidCreationFieldData
945
     * @dep_ends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent
946
     */
947
    public function testCreateContentFails($failingValue, $expectedException)
948
    {
949
        try {
950
            $this->createContent($failingValue);
951
952
            $this->fail('Expected exception not thrown.');
953
        } catch (PHPUnit_Framework_AssertionFailedError $e) {
0 ignored issues
show
Bug introduced by
The class PHPUnit_Framework_AssertionFailedError does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
954
            throw $e;
955
        } catch (Exception $e) {
956
            $this->assertInstanceOf(
957
                $expectedException,
958
                $e
959
            );
960
        }
961
    }
962
963
    /**
964
     * Tests failing content update.
965
     *
966
     * @param mixed $failingValue
967
     * @param string $expectedException
968
     *
969
     * @dataProvider provideInvalidUpdateFieldData
970
     */
971
    public function testUpdateContentFails($failingValue, $expectedException)
972
    {
973
        $this->expectException($expectedException);
974
        $this->updateContent($failingValue);
975
    }
976
977
    protected function removeFieldDefinition()
978
    {
979
        $repository = $this->getRepository();
980
        $contentService = $repository->getContentService();
981
        $contentTypeService = $repository->getContentTypeService();
982
        $content = $this->testPublishContent();
983
984
        $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId);
985
        $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType);
986
        $fieldDefinition = $contentTypeDraft->getFieldDefinition('data');
987
988
        $contentTypeService->removeFieldDefinition($contentTypeDraft, $fieldDefinition);
0 ignored issues
show
Bug introduced by
It seems like $fieldDefinition defined by $contentTypeDraft->getFieldDefinition('data') on line 986 can be null; however, eZ\Publish\API\Repositor...removeFieldDefinition() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
989
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
990
991
        return $contentService->loadContent($content->id);
992
    }
993
994
    /**
995
     * Tests removal of field definition from the ContentType of the Content.
996
     */
997
    public function testRemoveFieldDefinition()
998
    {
999
        $content = $this->removeFieldDefinition();
1000
1001
        $this->assertCount(1, $content->getFields());
1002
        $this->assertNull($content->getFieldValue('data'));
1003
    }
1004
1005
    protected function addFieldDefinition()
1006
    {
1007
        $repository = $this->getRepository();
1008
        $contentService = $repository->getContentService();
1009
        $contentTypeService = $repository->getContentTypeService();
1010
        $content = $this->removeFieldDefinition();
1011
1012
        $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId);
1013
        $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType);
1014
1015
        $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct(
1016
            'data',
1017
            $this->getTypeName()
1018
        );
1019
1020
        $fieldDefinitionCreateStruct->names = $this->getOverride('names', $this->getValidFieldConfiguration(), [$contentType->mainLanguageCode => $this->getTypeName()]);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getOverride('name... $this->getTypeName())) of type * is incompatible with the declared type array of property $names.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1021
        $fieldDefinitionCreateStruct->validatorConfiguration = $this->getValidValidatorConfiguration();
1022
        $fieldDefinitionCreateStruct->fieldSettings = $this->getValidFieldSettings();
1023
        $fieldDefinitionCreateStruct->defaultValue = null;
1024
1025
        $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct);
1026
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1027
1028
        return $contentService->loadContent($content->id);
1029
    }
1030
1031
    /**
1032
     * Tests addition of field definition from the ContentType of the Content.
1033
     */
1034
    public function testAddFieldDefinition()
1035
    {
1036
        $content = $this->addFieldDefinition();
1037
1038
        $this->assertCount(2, $content->getFields());
1039
1040
        $this->assertTrue(
1041
            $this->getRepository()->getFieldTypeService()->getFieldType(
1042
                $this->getTypeName()
1043
            )->isEmptyValue(
1044
                $content->getFieldValue('data')
1045
            )
1046
        );
1047
    }
1048
1049
    /**
1050
     * @dataProvider provideToHashData
1051
     */
1052 View Code Duplication
    public function testToHash($value, $expectedHash)
1053
    {
1054
        $repository = $this->getRepository();
1055
        $fieldTypeService = $repository->getFieldTypeService();
1056
        $fieldType = $fieldTypeService->getFieldType($this->getTypeName());
1057
1058
        $this->assertEquals(
1059
            $expectedHash,
1060
            $fieldType->toHash($value)
1061
        );
1062
    }
1063
1064
    /**
1065
     * @depends testCreateContent
1066
     * @dataProvider provideFromHashData
1067
     * @todo: Requires correct registered FieldTypeService, needs to be
1068
     *        maintained!
1069
     */
1070 View Code Duplication
    public function testFromHash($hash, $expectedValue)
1071
    {
1072
        $repository = $this->getRepository();
1073
        $fieldTypeService = $repository->getFieldTypeService();
1074
        $fieldType = $fieldTypeService->getFieldType($this->getTypeName());
1075
1076
        $this->assertEquals(
1077
            $expectedValue,
1078
            $fieldType->fromHash($hash)
1079
        );
1080
    }
1081
1082
    /**
1083
     * Test that exceeding default version archive limit has no effect on a published content.
1084
     */
1085
    public function testExceededVersionArchiveLimitHasNoEffectOnContent()
1086
    {
1087
        $repository = $this->getRepository();
1088
        $contentService = $repository->getContentService();
1089
        $contentDraft = $this->createContent($this->getValidCreationFieldData());
1090
        $publishedContent = $contentService->publishVersion($contentDraft->versionInfo);
1091
        // update and publish content to exceed version archive limit
1092
        $contentUpdateStruct = $contentService->newContentUpdateStruct();
1093
        $contentUpdateStruct->setField('data', $this->getValidUpdateFieldData());
1094
        for ($i = 0; $i < static::VERSION_ARCHIVE_LIMIT + 1; ++$i) {
1095
            $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo);
1096
            $contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct);
1097
            $publishedContent = $contentService->publishVersion($contentDraft->versionInfo);
1098
        }
1099
1100
        $loadedContent = $contentService->loadContent(
1101
            $publishedContent->contentInfo->id,
1102
            ['eng-US']
1103
        );
1104
        $this->assertUpdatedFieldDataLoadedCorrect($loadedContent->getField('data'));
0 ignored issues
show
Bug introduced by
It seems like $loadedContent->getField('data') can be null; however, assertUpdatedFieldDataLoadedCorrect() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1105
    }
1106
1107
    /**
1108
     * Test that deleting new draft does not affect data of published version.
1109
     */
1110
    public function testDeleteDraftOfPublishedContentDoesNotDeleteData()
1111
    {
1112
        $repository = $this->getRepository();
1113
        $contentService = $repository->getContentService();
1114
        $fieldType = $repository->getFieldTypeService()->getFieldType($this->getTypeName());
1115
1116
        $contentDraft = $this->testCreateContent();
1117
        $publishedContent = $contentService->publishVersion($contentDraft->versionInfo);
1118
1119
        $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo);
1120
1121
        $contentService->deleteVersion($contentDraft->versionInfo);
1122
        $loadedContent = $contentService->loadContent($publishedContent->contentInfo->id, ['eng-US']);
1123
1124
        self::assertFalse(
1125
            $fieldType->isEmptyValue($loadedContent->getField('data')->value)
1126
        );
1127
    }
1128
1129
    /**
1130
     * Test creating new translation from existing content with empty field.
1131
     */
1132
    public function testUpdateContentWithNewTranslationOnEmptyField()
1133
    {
1134
        $repository = $this->getRepository();
1135
        $contentService = $repository->getContentService();
1136
1137
        $content = $this->testCreateContentWithEmptyFieldValue();
1138
        $publishedContent = $contentService->publishVersion($content->versionInfo);
1139
1140
        $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo);
1141
        $updateStruct = $contentService->newContentUpdateStruct();
1142
        $updateStruct->setField(
1143
            'data',
1144
            $publishedContent->getFieldValue('data', 'eng-US'),
1145
            'eng-US'
1146
        );
1147
        $updateStruct->initialLanguageCode = 'eng-GB';
1148
        $updatedContentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct);
1149
        $contentService->publishVersion($updatedContentDraft->versionInfo);
1150
    }
1151
1152
    /**
1153
     * Get proper multilingual FT-specific Values. It Can be overridden by a Field Type test case.
1154
     *
1155
     * @param string[] $languageCodes List of languages to create data for
1156
     *
1157
     * @return array an array in the form of <code>[languageCode => data]</code>
1158
     */
1159
    public function getValidMultilingualFieldData(array $languageCodes)
1160
    {
1161
        $data = [];
1162
        foreach ($languageCodes as $languageCode) {
1163
            $data[$languageCode] = $this->getValidCreationFieldData();
1164
        }
1165
1166
        return $data;
1167
    }
1168
1169
    /**
1170
     * Test that removing Translation from all Versions works for data from a Field Type.
1171
     *
1172
     * @covers \eZ\Publish\API\Repository\ContentService::deleteTranslation
1173
     */
1174
    public function testDeleteTranslation()
1175
    {
1176
        $repository = $this->getRepository();
1177
        $contentService = $repository->getContentService();
1178
1179
        $languageCodes = ['eng-US', 'ger-DE'];
1180
1181
        $fieldName = $this->getFieldName();
1182
        $names = [];
1183
        foreach ($languageCodes as $languageCode) {
1184
            $names[$languageCode] = "{$languageCode} {$fieldName}";
1185
        }
1186
1187
        $fieldData = $this->getValidMultilingualFieldData($languageCodes);
1188
1189
        $content = $contentService->publishVersion(
1190
            $this->createMultilingualContent($names, $fieldData)->versionInfo
1191
        );
1192
1193
        // create one more Version
1194
        $publishedContent = $contentService->publishVersion(
1195
            $contentService->createContentDraft($content->contentInfo)->versionInfo
1196
        );
1197
1198
        // create Draft
1199
        $contentService->createContentDraft($content->contentInfo);
1200
1201
        // create copy of content in all Versions to use it for comparision later on
1202
        $contentByVersion = [];
1203
        foreach ($contentService->loadVersions($content->contentInfo) as $versionInfo) {
1204
            $contentByVersion[$versionInfo->versionNo] = $contentService->loadContent(
1205
                $content->id,
1206
                null,
1207
                $versionInfo->versionNo
1208
            );
1209
        }
1210
1211
        // delete Translation from all available Versions
1212
        $contentService->deleteTranslation($publishedContent->contentInfo, 'ger-DE');
1213
1214
        // check if are Versions have valid Translation
1215
        foreach ($contentService->loadVersions($publishedContent->contentInfo) as $versionInfo) {
1216
            // check if deleted Translation does not exist
1217
            self::assertEquals(['eng-US'], array_keys($versionInfo->getNames()));
1218
            self::assertEquals(['eng-US'], $versionInfo->languageCodes);
1219
1220
            // load Content of a Version to access other fields data
1221
            $versionContent = $contentService->loadContent(
1222
                $content->id,
1223
                null,
1224
                $versionInfo->versionNo
1225
            );
1226
            // check if deleted Translation for Field Type data does not exist
1227
            self::assertEmpty($versionContent->getFieldsByLanguage('ger-DE'));
1228
            self::assertEmpty($versionContent->getField('data', 'ger-DE'));
1229
1230
            // check if the remaining Translation is still valid
1231
            $expectedContent = $contentByVersion[$versionContent->versionInfo->versionNo];
1232
            self::assertNotEmpty($versionContent->getFieldsByLanguage('eng-US'));
1233
            self::assertEquals(
1234
                $expectedContent->getField('name', 'eng-US'),
1235
                $versionContent->getField('name', 'eng-US')
1236
            );
1237
            self::assertEquals(
1238
                $expectedContent->getField('data', 'eng-US'),
1239
                $versionContent->getField('data', 'eng-US')
1240
            );
1241
        }
1242
    }
1243
}
1244