Completed
Push — master ( ea5df1...ff8f52 )
by
unknown
14:08 queued 10s
created

testFindTrashItemsSortedByDateTrashed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 37
rs 9.328
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;
8
9
use eZ\Publish\API\Repository\Repository;
10
use eZ\Publish\API\Repository\URLAliasService;
11
use eZ\Publish\API\Repository\Values\Content\Content;
12
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
13
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
14
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
15
use eZ\Publish\API\Repository\Values\Content\Query;
16
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
17
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
18
use eZ\Publish\API\Repository\Exceptions\NotFoundException;
19
use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult;
20
use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem;
21
use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation;
22
use eZ\Publish\Core\Repository\Values\Content\TrashItem;
23
use eZ\Publish\Core\Repository\Values\Content\Location;
24
use DateTime;
25
26
/**
27
 * Test case for operations in the TrashService using in memory storage.
28
 *
29
 * @see eZ\Publish\API\Repository\TrashService
30
 * @group integration
31
 * @group trash
32
 */
33
class TrashServiceTest extends BaseTrashServiceTest
34
{
35
    /**
36
     * Test for the trash() method.
37
     *
38
     * @see \eZ\Publish\API\Repository\TrashService::trash()
39
     * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId
40
     */
41
    public function testTrash()
42
    {
43
        /* BEGIN: Use Case */
44
        $trashItem = $this->createTrashItem();
45
        /* END: Use Case */
46
47
        $this->assertInstanceOf(
48
            '\\eZ\\Publish\\API\\Repository\\Values\\Content\\TrashItem',
49
            $trashItem
50
        );
51
    }
52
53
    /**
54
     * Test for the trash() method.
55
     *
56
     * @see \eZ\Publish\API\Repository\TrashService::trash()
57
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
58
     */
59
    public function testTrashSetsExpectedTrashItemProperties()
60
    {
61
        $repository = $this->getRepository();
62
63
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
64
65
        // Load the location that will be trashed
66
        $location = $repository->getLocationService()
67
            ->loadLocationByRemoteId($mediaRemoteId);
68
69
        $expected = [
70
            'id' => $location->id,
71
            'depth' => $location->depth,
72
            'hidden' => $location->hidden,
73
            'invisible' => $location->invisible,
74
            'parentLocationId' => $location->parentLocationId,
75
            'pathString' => $location->pathString,
76
            'priority' => $location->priority,
77
            'remoteId' => $location->remoteId,
78
            'sortField' => $location->sortField,
79
            'sortOrder' => $location->sortOrder,
80
        ];
81
82
        $trashItem = $this->createTrashItem();
83
84
        $this->assertPropertiesCorrect($expected, $trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 82 can be null; however, eZ\Publish\API\Repositor...sertPropertiesCorrect() 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...
85
    }
86
87
    /**
88
     * Test for the trash() method.
89
     *
90
     * @see \eZ\Publish\API\Repository\TrashService::trash()
91
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
92
     */
93 View Code Duplication
    public function testTrashRemovesLocationFromMainStorage()
94
    {
95
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
96
97
        $repository = $this->getRepository();
98
99
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
100
101
        /* BEGIN: Use Case */
102
        $this->createTrashItem();
103
104
        // Load the location service
105
        $locationService = $repository->getLocationService();
106
107
        // This call will fail with a "NotFoundException", because the media
108
        // location was marked as trashed in the main storage
109
        $locationService->loadLocationByRemoteId($mediaRemoteId);
110
        /* END: Use Case */
111
    }
112
113
    /**
114
     * Test for the trash() method.
115
     *
116
     * @see \eZ\Publish\API\Repository\TrashService::trash()
117
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
118
     */
119
    public function testTrashRemovesChildLocationsFromMainStorage()
120
    {
121
        $repository = $this->getRepository();
122
123
        /* BEGIN: Use Case */
124
        $remoteIds = $this->createRemoteIdList();
125
126
        $this->createTrashItem();
127
128
        // All invocations to loadLocationByRemoteId() to one of the above
129
        // collected remoteIds will return in an "NotFoundException"
130
        /* END: Use Case */
131
132
        $locationService = $repository->getLocationService();
133
        foreach ($remoteIds as $remoteId) {
134
            try {
135
                $locationService->loadLocationByRemoteId($remoteId);
136
                $this->fail("Location '{$remoteId}' should exist.'");
137
            } catch (NotFoundException $e) {
138
                // echo $e->getFile(), ' +', $e->getLine(), PHP_EOL;
139
            }
140
        }
141
142
        $this->assertGreaterThan(
143
            0,
144
            count($remoteIds),
145
            "There should be at least one 'Community' child location."
146
        );
147
    }
148
149
    /**
150
     * Test for the trash() method.
151
     *
152
     * @see \eZ\Publish\API\Repository\TrashService::trash()
153
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
154
     */
155
    public function testTrashDecrementsChildCountOnParentLocation()
156
    {
157
        $repository = $this->getRepository();
158
        $locationService = $repository->getLocationService();
159
160
        $baseLocationId = $this->generateId('location', 1);
161
162
        $location = $locationService->loadLocation($baseLocationId);
163
164
        $childCount = $locationService->getLocationChildCount($location);
165
166
        $this->createTrashItem();
167
168
        $this->refreshSearch($repository);
169
170
        $this->assertEquals(
171
            $childCount - 1,
172
            $locationService->getLocationChildCount($location)
173
        );
174
    }
175
176
    /**
177
     * Test sending a location to trash updates Content mainLocation.
178
     *
179
     * @covers \eZ\Publish\API\Repository\TrashService::trash
180
     */
181
    public function testTrashUpdatesMainLocation()
182
    {
183
        $repository = $this->getRepository();
184
        $contentService = $repository->getContentService();
185
        $locationService = $repository->getLocationService();
186
        $trashService = $repository->getTrashService();
187
188
        $contentInfo = $contentService->loadContentInfo(42);
189
190
        // Create additional location that will become new main location
191
        $location = $locationService->createLocation(
192
            $contentInfo,
193
            new LocationCreateStruct(['parentLocationId' => 2])
194
        );
195
196
        $trashService->trash(
197
            $locationService->loadLocation($contentInfo->mainLocationId)
198
        );
199
200
        self::assertEquals(
201
            $location->id,
202
            $contentService->loadContentInfo(42)->mainLocationId
203
        );
204
    }
205
206
    /**
207
     * Test sending a location to trash.
208
     *
209
     * @covers \eZ\Publish\API\Repository\TrashService::trash
210
     */
211
    public function testTrashReturnsNull()
212
    {
213
        $repository = $this->getRepository();
214
        $contentService = $repository->getContentService();
215
        $locationService = $repository->getLocationService();
216
        $trashService = $repository->getTrashService();
217
218
        // Create additional location to trash
219
        $location = $locationService->createLocation(
220
            $contentService->loadContentInfo(42),
221
            new LocationCreateStruct(['parentLocationId' => 2])
222
        );
223
224
        $trashItem = $trashService->trash($location);
225
226
        self::assertNull($trashItem);
227
    }
228
229
    /**
230
     * Test for the loadTrashItem() method.
231
     *
232
     * @covers \eZ\Publish\API\Repository\TrashService::loadTrashItem
233
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
234
     */
235
    public function testLoadTrashItem()
236
    {
237
        $repository = $this->getRepository();
238
        $trashService = $repository->getTrashService();
239
240
        /* BEGIN: Use Case */
241
        $trashItem = $this->createTrashItem();
242
243
        // Reload the trash item
244
        $trashItemReloaded = $trashService->loadTrashItem($trashItem->id);
245
        /* END: Use Case */
246
247
        $this->assertInstanceOf(
248
            APITrashItem::class,
249
            $trashItemReloaded
250
        );
251
252
        $this->assertEquals(
253
            $trashItem->pathString,
254
            $trashItemReloaded->pathString
255
        );
256
257
        $this->assertEquals(
258
            $trashItem,
259
            $trashItemReloaded
260
        );
261
262
        $this->assertInstanceOf(
263
            DateTime::class,
264
            $trashItemReloaded->trashed
265
        );
266
267
        $this->assertEquals(
268
            $trashItem->trashed->getTimestamp(),
269
            $trashItemReloaded->trashed->getTimestamp()
270
        );
271
272
        $this->assertGreaterThan(
273
            0,
274
            $trashItemReloaded->trashed->getTimestamp()
275
        );
276
277
        $this->assertInstanceOf(
278
            Content::class,
279
            $content = $trashItemReloaded->getContent()
280
        );
281
        $this->assertEquals($trashItem->contentId, $content->contentInfo->id);
282
    }
283
284
    /**
285
     * Test for the loadTrashItem() method.
286
     *
287
     * @see \eZ\Publish\API\Repository\TrashService::loadTrashItem()
288
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testLoadTrashItem
289
     */
290
    public function testLoadTrashItemThrowsNotFoundException()
291
    {
292
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
293
294
        $repository = $this->getRepository();
295
296
        $nonExistingTrashId = $this->generateId('trash', 2342);
297
        /* BEGIN: Use Case */
298
        $trashService = $repository->getTrashService();
299
300
        // This call will fail with a "NotFoundException", because no trash item
301
        // with the ID 1342 should exist in an eZ Publish demo installation
302
        $trashService->loadTrashItem($nonExistingTrashId);
303
        /* END: Use Case */
304
    }
305
306
    /**
307
     * Test for the recover() method.
308
     *
309
     * @covers \eZ\Publish\API\Repository\TrashService::recover
310
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
311
     */
312
    public function testRecover()
313
    {
314
        $repository = $this->getRepository();
315
        $trashService = $repository->getTrashService();
316
        $locationService = $repository->getLocationService();
317
318
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
319
320
        /* BEGIN: Use Case */
321
        $trashItem = $this->createTrashItem();
322
323
        // Recover the trashed item
324
        $location = $trashService->recover($trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 321 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
325
326
        // Load the recovered location
327
        $locationReloaded = $locationService->loadLocationByRemoteId(
328
            $mediaRemoteId
329
        );
330
        /* END: Use Case */
331
332
        $this->assertInstanceOf(
333
            APILocation::class,
334
            $location
335
        );
336
337
        $this->assertEquals(
338
            $location,
339
            $locationReloaded
340
        );
341
342
        try {
343
            $trashService->loadTrashItem($trashItem->id);
344
            $this->fail('Trash item was not removed after being recovered.');
345
        } catch (NotFoundException $e) {
346
            // All well
347
        }
348
    }
349
350
    /**
351
     * Test recovering a non existing trash item results in a NotFoundException.
352
     *
353
     * @covers \eZ\Publish\API\Repository\TrashService::recover
354
     */
355 View Code Duplication
    public function testRecoverThrowsNotFoundExceptionForNonExistingTrashItem()
356
    {
357
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
358
359
        $repository = $this->getRepository();
360
        $trashService = $repository->getTrashService();
361
362
        $trashService->recover(
363
            $this->getTrashItemDouble(
364
                12364,
365
                12345,
366
                12363
367
            )
368
        );
369
    }
370
371
    /**
372
     * Test for the trash() method.
373
     *
374
     * @see \eZ\Publish\API\Repository\TrashService::recover()
375
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
376
     */
377
    public function testNotFoundAliasAfterRemoveIt()
378
    {
379
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
380
381
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
382
383
        $repository = $this->getRepository();
384
        $trashService = $repository->getTrashService();
385
        $urlAliasService = $repository->getURLAliasService();
386
        $locationService = $repository->getLocationService();
387
388
        // Double ->lookup() call because there where issue that one call was not enough to spot bug
389
        $urlAliasService->lookup('/Media');
390
        $urlAliasService->lookup('/Media');
391
392
        $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId);
393
        $trashService->trash($mediaLocation);
394
395
        $urlAliasService->lookup('/Media');
396
    }
397
398
    /**
399
     * Test for the recover() method.
400
     *
401
     * @see \eZ\Publish\API\Repository\TrashService::recover()
402
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
403
     */
404
    public function testAliasesForRemovedItems()
405
    {
406
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
407
408
        $repository = $this->getRepository();
409
        $trashService = $repository->getTrashService();
410
        $urlAliasService = $repository->getURLAliasService();
411
        $locationService = $repository->getLocationService();
412
413
        // Double ->lookup() call because there where issue that one call was not enough to spot bug
414
        $urlAliasService->lookup('/Media');
415
        $trashedLocationAlias = $urlAliasService->lookup('/Media');
416
417
        $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId);
418
        $trashItem = $trashService->trash($mediaLocation);
419
        $this->assertAliasNotExists($urlAliasService, '/Media');
420
421
        $this->createNewContentInPlaceTrashedOne($repository, $mediaLocation->parentLocationId);
422
423
        $createdLocationAlias = $urlAliasService->lookup('/Media');
424
425
        $this->assertNotEquals(
426
            $trashedLocationAlias->destination,
427
            $createdLocationAlias->destination,
428
            'Destination for /media url should changed'
429
        );
430
431
        $recoveredLocation = $trashService->recover($trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $trashService->trash($mediaLocation) on line 418 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
432
        $recoveredLocationAlias = $urlAliasService->lookup('/Media2');
433
        $recoveredLocationAliasReverse = $urlAliasService->reverseLookup($recoveredLocation);
434
435
        $this->assertEquals($recoveredLocationAlias->destination, $recoveredLocationAliasReverse->destination);
436
437
        $this->assertNotEquals($recoveredLocationAliasReverse->destination, $trashedLocationAlias->destination);
438
        $this->assertNotEquals($recoveredLocationAliasReverse->destination, $createdLocationAlias->destination);
439
    }
440
441
    /**
442
     * Test for the recover() method.
443
     *
444
     * @see \eZ\Publish\API\Repository\TrashService::recover()
445
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover
446
     */
447
    public function testRecoverDoesNotRestoreChildLocations()
448
    {
449
        $repository = $this->getRepository();
450
        $trashService = $repository->getTrashService();
451
        $locationService = $repository->getLocationService();
452
453
        $remoteIds = $this->createRemoteIdList();
454
455
        // Unset remote ID of actually restored location
456
        unset($remoteIds[array_search('3f6d92f8044aed134f32153517850f5a', $remoteIds)]);
457
458
        $trashItem = $this->createTrashItem();
459
460
        $trashService->recover($trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 458 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
461
462
        $this->assertGreaterThan(
463
            0,
464
            count($remoteIds),
465
            "There should be at least one 'Community' child location."
466
        );
467
468
        // None of the child locations will be available again
469
        foreach ($remoteIds as $remoteId) {
470
            try {
471
                $locationService->loadLocationByRemoteId($remoteId);
472
                $this->fail(
473
                    sprintf(
474
                        'Location with remote ID "%s" unexpectedly restored.',
475
                        $remoteId
476
                    )
477
                );
478
            } catch (NotFoundException $e) {
479
                // All well
480
            }
481
        }
482
483
        try {
484
            $trashService->loadTrashItem($trashItem->id);
485
            $this->fail('Trash item was not removed after being recovered.');
486
        } catch (NotFoundException $e) {
487
            // All well
488
        }
489
    }
490
491
    /**
492
     * Test for the recover() method.
493
     *
494
     * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem, $newParentLocation)
495
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover
496
     *
497
     * @todo Fix naming
498
     */
499
    public function testRecoverWithLocationCreateStructParameter()
500
    {
501
        $repository = $this->getRepository();
502
        $trashService = $repository->getTrashService();
503
        $locationService = $repository->getLocationService();
504
505
        $homeLocationId = $this->generateId('location', 2);
506
        /* BEGIN: Use Case */
507
        // $homeLocationId is the ID of the "Home" location in an eZ Publish
508
        // demo installation
509
510
        $trashItem = $this->createTrashItem();
511
512
        // Get the new parent location
513
        $newParentLocation = $locationService->loadLocation($homeLocationId);
514
515
        // Recover location with new location
516
        $location = $trashService->recover($trashItem, $newParentLocation);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 510 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
517
        /* END: Use Case */
518
519
        $this->assertPropertiesCorrect(
520
            [
521
                'remoteId' => $trashItem->remoteId,
522
                'parentLocationId' => $homeLocationId,
523
                // Not the full sub tree is restored
524
                'depth' => $newParentLocation->depth + 1,
525
                'hidden' => false,
526
                'invisible' => $trashItem->invisible,
527
                'pathString' => $newParentLocation->pathString . $this->parseId('location', $location->id) . '/',
528
                'priority' => 0,
529
                'sortField' => APILocation::SORT_FIELD_NAME,
530
                'sortOrder' => APILocation::SORT_ORDER_ASC,
531
            ],
532
            $location
533
        );
534
535
        try {
536
            $trashService->loadTrashItem($trashItem->id);
537
            $this->fail('Trash item was not removed after being recovered.');
538
        } catch (NotFoundException $e) {
539
            // All well
540
        }
541
    }
542
543
    /**
544
     * Test for the recover() method.
545
     *
546
     * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem)
547
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover
548
     */
549
    public function testRecoverIncrementsChildCountOnOriginalParent()
550
    {
551
        $repository = $this->getRepository();
552
        $trashService = $repository->getTrashService();
553
        $locationService = $repository->getLocationService();
554
555
        $location = $locationService->loadLocation($this->generateId('location', 1));
556
557
        $trashItem = $this->createTrashItem();
558
559
        $this->refreshSearch($repository);
560
561
        /* BEGIN: Use Case */
562
        $childCount = $locationService->getLocationChildCount($location);
563
564
        // Recover location with new location
565
        $trashService->recover($trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 557 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
566
        /* END: Use Case */
567
568
        $this->refreshSearch($repository);
569
570
        $this->assertEquals(
571
            $childCount + 1,
572
            $locationService->getLocationChildCount($location)
573
        );
574
575
        try {
576
            $trashService->loadTrashItem($trashItem->id);
577
            $this->fail('Trash item was not removed after being recovered.');
578
        } catch (NotFoundException $e) {
579
            // All well
580
        }
581
    }
582
583
    /**
584
     * Test for the recover() method.
585
     *
586
     * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem, $newParentLocation)
587
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecoverWithLocationCreateStructParameter
588
     */
589
    public function testRecoverWithLocationCreateStructParameterIncrementsChildCountOnNewParent()
590
    {
591
        $repository = $this->getRepository();
592
        $trashService = $repository->getTrashService();
593
        $locationService = $repository->getLocationService();
594
595
        $homeLocationId = $this->generateId('location', 2);
596
597
        $location = $locationService->loadLocation($homeLocationId);
598
599
        $childCount = $locationService->getLocationChildCount($location);
600
601
        /* BEGIN: Use Case */
602
        // $homeLocationId is the ID of the "Home" location in an eZ Publish
603
        // demo installation
604
605
        $trashItem = $this->createTrashItem();
606
607
        // Get the new parent location
608
        $newParentLocation = $locationService->loadLocation($homeLocationId);
609
610
        // Recover location with new location
611
        $trashService->recover($trashItem, $newParentLocation);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 605 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
612
        /* END: Use Case */
613
614
        $this->refreshSearch($repository);
615
616
        $this->assertEquals(
617
            $childCount + 1,
618
            $locationService->getLocationChildCount($location)
619
        );
620
621
        try {
622
            $trashService->loadTrashItem($trashItem->id);
623
            $this->fail('Trash item was not removed after being recovered.');
624
        } catch (NotFoundException $e) {
625
            // All well
626
        }
627
    }
628
629
    /**
630
     * Test recovering a location from trash to non existing location.
631
     *
632
     * @covers \eZ\Publish\API\Repository\TrashService::recover
633
     */
634
    public function testRecoverToNonExistingLocation()
635
    {
636
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
637
638
        $repository = $this->getRepository();
639
        $trashService = $repository->getTrashService();
640
        $locationService = $repository->getLocationService();
641
642
        $location = $locationService->loadLocation(44);
643
        $trashItem = $trashService->trash($location);
644
645
        $newParentLocation = new Location(
646
            [
647
                'id' => 123456,
648
                'parentLocationId' => 123455,
649
            ]
650
        );
651
        $trashService->recover($trashItem, $newParentLocation);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $trashService->trash($location) on line 643 can be null; however, eZ\Publish\API\Repository\TrashService::recover() 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...
652
    }
653
654
    /**
655
     * Test for the findTrashItems() method.
656
     *
657
     * @see \eZ\Publish\API\Repository\TrashService::findTrashItems()
658
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
659
     */
660
    public function testFindTrashItems()
661
    {
662
        $repository = $this->getRepository();
663
        $trashService = $repository->getTrashService();
664
665
        /* BEGIN: Use Case */
666
        $this->createTrashItem();
667
668
        // Create a search query for all trashed items
669
        $query = new Query();
670
        $query->filter = new Criterion\LogicalAnd(
671
            [
672
                new Criterion\Field('title', Criterion\Operator::LIKE, '*'),
673
            ]
674
        );
675
676
        // Load all trashed locations
677
        $searchResult = $trashService->findTrashItems($query);
678
        /* END: Use Case */
679
680
        $this->assertInstanceOf(
681
            SearchResult::class,
682
            $searchResult
683
        );
684
685
        // 4 trashed locations from the sub tree
686
        $this->assertEquals(4, $searchResult->count);
687
        $this->assertEquals(4, $searchResult->totalCount);
688
    }
689
690
    /**
691
     * @covers  \eZ\Publish\API\Repository\TrashService::findTrashItems
692
     *
693
     * @throws \ErrorException
694
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
695
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
696
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
697
     */
698
    public function testFindTrashItemsSortedByDateTrashed(): void
699
    {
700
        $repository = $this->getRepository();
701
        $trashService = $repository->getTrashService();
702
        $locationService = $repository->getLocationService();
703
704
        $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2);
705
        $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2);
706
707
        $firstTrashedItem = $trashService->trash(
708
            $locationService->loadLocation($folder1->contentInfo->mainLocationId)
709
        );
710
        $this->updateTrashedDate($firstTrashedItem->id, \time() - 100);
711
        $latestTrashItem = $trashService->trash(
712
            $locationService->loadLocation($folder2->contentInfo->mainLocationId)
713
        );
714
715
        $query = new Query();
716
        $query->filter = new Criterion\ContentId([
717
            $folder1->contentInfo->id,
718
            $folder2->contentInfo->id,
719
        ]);
720
721
        // Load all trashed locations, sorted by trashed date ASC
722
        $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_ASC)];
723
        $searchResult = $trashService->findTrashItems($query);
724
        self::assertEquals(2, $searchResult->totalCount);
725
        self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[0]->remoteId);
726
        self::assertEquals($latestTrashItem->remoteId, $searchResult->items[1]->remoteId);
727
728
        // Load all trashed locations, sorted by trashed date DESC
729
        $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_DESC)];
730
        $searchResult = $trashService->findTrashItems($query);
731
        self::assertEquals(2, $searchResult->totalCount);
732
        self::assertEquals($latestTrashItem->remoteId, $searchResult->items[0]->remoteId);
733
        self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[1]->remoteId);
734
    }
735
736
    /**
737
     * Test for the findTrashItems() method for it's result structure.
738
     *
739
     * @see \eZ\Publish\API\Repository\TrashService::findTrashItems()
740
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash
741
     */
742
    public function testFindTrashItemsLimits()
743
    {
744
        $repository = $this->getRepository();
745
        $trashService = $repository->getTrashService();
746
747
        $this->createTrashItem();
748
749
        // Create a search query for all trashed items
750
        $query = new Query();
751
        $query->limit = 2;
752
753
        // Load all trashed locations
754
        $searchResult = $trashService->findTrashItems($query);
755
756
        $this->assertInstanceOf(
757
            SearchResult::class,
758
            $searchResult
759
        );
760
761
        // 4 trashed locations from the sub tree, but only 2 in results
762
        $this->assertCount(2, $searchResult->items);
763
        $this->assertEquals(4, $searchResult->count);
764
        $this->assertEquals(4, $searchResult->totalCount);
765
    }
766
767
    /**
768
     * Test for the findTrashItems() method.
769
     *
770
     * @see \eZ\Publish\API\Repository\TrashService::findTrashItems()
771
     * @depends \eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems
772
     */
773
    public function testFindTrashItemsLimitedAccess()
774
    {
775
        $repository = $this->getRepository();
776
        $trashService = $repository->getTrashService();
777
778
        /* BEGIN: Use Case */
779
        $this->createTrashItem();
780
781
        // Create a search query for all trashed items
782
        $query = new Query();
783
        $query->filter = new Criterion\LogicalAnd(
784
            [
785
                new Criterion\Field('title', Criterion\Operator::LIKE, '*'),
786
            ]
787
        );
788
789
        // Create a user in the Editor user group.
790
        $user = $this->createUserVersion1();
791
792
        // Set the Editor user as current user, these users have no access to Trash by default.
793
        $repository->getPermissionResolver()->setCurrentUserReference($user);
794
795
        // Load all trashed locations
796
        $searchResult = $trashService->findTrashItems($query);
797
        /* END: Use Case */
798
799
        $this->assertInstanceOf(
800
            '\\eZ\\Publish\\API\\Repository\\Values\\Content\\SearchResult',
801
            $searchResult
802
        );
803
804
        // 0 trashed locations found, though 4 exist
805
        $this->assertEquals(0, $searchResult->count);
806
    }
807
808
    /**
809
     * Test for the emptyTrash() method.
810
     *
811
     * @see \eZ\Publish\API\Repository\TrashService::emptyTrash()
812
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems
813
     */
814
    public function testEmptyTrash()
815
    {
816
        $repository = $this->getRepository();
817
        $trashService = $repository->getTrashService();
818
        $contentService = $repository->getContentService();
819
820
        /* BEGIN: Use Case */
821
        $trashItem = $this->createTrashItem();
822
823
        // Empty the trash
824
        $trashService->emptyTrash();
825
826
        // Create a search query for all trashed items
827
        $query = new Query();
828
        $query->filter = new Criterion\LogicalAnd(
829
            [
830
                new Criterion\Field('title', Criterion\Operator::LIKE, '*'),
831
            ]
832
        );
833
        // Load all trashed locations, search result should be empty
834
        $searchResult = $trashService->findTrashItems($query);
835
        /* END: Use Case */
836
837
        $this->assertEquals(0, $searchResult->count);
838
839
        // Try to load content
840
        $this->expectException(NotFoundException::class);
841
        $contentService->loadContent($trashItem->contentId);
842
    }
843
844
    /**
845
     * Test for the emptyTrash() method with user which has subtree limitations.
846
     *
847
     * @see \eZ\Publish\API\Repository\TrashService::emptyTrash()
848
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems
849
     */
850
    public function testEmptyTrashForUserWithSubtreeLimitation()
851
    {
852
        $repository = $this->getRepository();
853
        $trashService = $repository->getTrashService();
854
        $contentService = $repository->getContentService();
855
856
        /* BEGIN: Use Case */
857
        $trashItem = $this->createTrashItem();
858
859
        $this->createRoleWithPolicies('roleTrashCleaner', [
860
            ['module' => 'content', 'function' => 'cleantrash'],
861
            ['module' => 'content', 'function' => 'read'],
862
        ]);
863
        $user = $this->createCustomUserWithLogin(
864
            'user',
865
            '[email protected]',
866
            'roleTrashCleaners',
867
            'roleTrashCleaner',
868
            new SubtreeLimitation(['limitationValues' => ['/1/2/']])
869
        );
870
        $repository->getPermissionResolver()->setCurrentUserReference($user);
871
872
        // Empty the trash
873
        $trashService->emptyTrash();
874
875
        // Create a search query for all trashed items
876
        $query = new Query();
877
        $query->filter = new Criterion\LogicalAnd(
878
            [
879
                new Criterion\Field('title', Criterion\Operator::LIKE, '*'),
880
            ]
881
        );
882
        // Load all trashed locations, search result should be empty
883
        $searchResult = $trashService->findTrashItems($query);
884
        /* END: Use Case */
885
886
        $this->assertEquals(0, $searchResult->totalCount);
887
888
        // Try to load content
889
        $this->expectException(NotFoundException::class);
890
        $contentService->loadContent($trashItem->contentId);
891
    }
892
893
    /**
894
     * Test for the deleteTrashItem() method.
895
     *
896
     * @see \eZ\Publish\API\Repository\TrashService::deleteTrashItem()
897
     * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems
898
     */
899
    public function testDeleteTrashItem()
900
    {
901
        $repository = $this->getRepository();
902
        $trashService = $repository->getTrashService();
903
        $locationService = $repository->getLocationService();
904
        $contentService = $repository->getContentService();
905
906
        $demoDesignLocationId = $this->generateId('location', 56);
907
        /* BEGIN: Use Case */
908
        // $demoDesignLocationId is the ID of the "Demo Design" location in an eZ
909
        // Publish demo installation
910
911
        $trashItem = $this->createTrashItem();
912
913
        // Trash one more location
914
        $trashService->trash(
915
            $locationService->loadLocation($demoDesignLocationId)
916
        );
917
918
        // Empty the trash
919
        $trashService->deleteTrashItem($trashItem);
0 ignored issues
show
Bug introduced by
It seems like $trashItem defined by $this->createTrashItem() on line 911 can be null; however, eZ\Publish\API\Repositor...vice::deleteTrashItem() 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...
920
921
        // Create a search query for all trashed items
922
        $query = new Query();
923
        $query->filter = new Criterion\LogicalAnd(
924
            [
925
                new Criterion\Field('title', Criterion\Operator::LIKE, '*'),
926
            ]
927
        );
928
929
        // Load all trashed locations, should only contain the Demo Design location
930
        $searchResult = $trashService->findTrashItems($query);
931
        /* END: Use Case */
932
933
        $foundIds = array_map(
934
            function ($trashItem) {
935
                return $trashItem->id;
936
            },
937
            $searchResult->items
938
        );
939
940
        $this->assertEquals(4, $searchResult->count);
941
        $this->assertTrue(
942
            in_array($demoDesignLocationId, $foundIds)
943
        );
944
945
        // Try to load Content
946
        $this->expectException(NotFoundException::class);
947
        $contentService->loadContent($trashItem->contentId);
948
    }
949
950
    /**
951
     * Test deleting a non existing trash item.
952
     *
953
     * @covers \eZ\Publish\API\Repository\TrashService::deleteTrashItem
954
     */
955 View Code Duplication
    public function testDeleteThrowsNotFoundExceptionForNonExistingTrashItem()
956
    {
957
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
958
959
        $repository = $this->getRepository();
960
        $trashService = $repository->getTrashService();
961
962
        $trashService->deleteTrashItem($this->getTrashItemDouble(
963
            12364,
964
            12345,
965
            12363
966
        ));
967
    }
968
969
    /**
970
     * Returns an array with the remoteIds of all child locations of the
971
     * <b>Community</b> location. It is stored in a local variable named
972
     * <b>$remoteIds</b>.
973
     *
974
     * @return string[]
975
     */
976
    private function createRemoteIdList()
977
    {
978
        $repository = $this->getRepository();
979
980
        /* BEGIN: Inline */
981
        // remoteId of the "Community" location in an eZ Publish demo installation
982
        $mediaRemoteId = '75c715a51699d2d309a924eca6a95145';
983
984
        // Load the location service
985
        $locationService = $repository->getLocationService();
986
987
        $remoteIds = [];
988
        $children = $locationService->loadLocationChildren($locationService->loadLocationByRemoteId($mediaRemoteId));
989
        foreach ($children->locations as $child) {
990
            $remoteIds[] = $child->remoteId;
991
            foreach ($locationService->loadLocationChildren($child)->locations as $grandChild) {
992
                $remoteIds[] = $grandChild->remoteId;
993
            }
994
        }
995
        /* END: Inline */
996
997
        return $remoteIds;
998
    }
999
1000
    /**
1001
     * @param Repository $repository
1002
     * @param int $parentLocationId
1003
     *
1004
     * @return \eZ\Publish\API\Repository\Values\Content\Content
1005
     */
1006 View Code Duplication
    protected function createNewContentInPlaceTrashedOne(Repository $repository, $parentLocationId)
1007
    {
1008
        $contentService = $repository->getContentService();
1009
        $locationService = $repository->getLocationService();
1010
        $contentTypeService = $repository->getContentTypeService();
1011
1012
        $contentType = $contentTypeService->loadContentTypeByIdentifier('forum');
1013
        $newContent = $contentService->newContentCreateStruct($contentType, 'eng-US');
1014
        $newContent->setField('name', 'Media');
1015
1016
        $location = $locationService->newLocationCreateStruct($parentLocationId);
1017
1018
        $draftContent = $contentService->createContent($newContent, [$location]);
1019
1020
        return $contentService->publishVersion($draftContent->versionInfo);
1021
    }
1022
1023
    /**
1024
     * @param URLAliasService $urlAliasService
1025
     * @param string $urlPath Url alias path
1026
     *
1027
     * @return \eZ\Publish\API\Repository\Values\Content\URLAlias
1028
     */
1029
    private function assertAliasExists(URLAliasService $urlAliasService, $urlPath)
1030
    {
1031
        $urlAlias = $urlAliasService->lookup($urlPath);
1032
1033
        $this->assertInstanceOf('\eZ\Publish\API\Repository\Values\Content\URLAlias', $urlAlias);
1034
1035
        return $urlAlias;
1036
    }
1037
1038
    /**
1039
     * @param URLAliasService $urlAliasService
1040
     * @param string $urlPath Url alias path
1041
     */
1042
    private function assertAliasNotExists(URLAliasService $urlAliasService, $urlPath)
0 ignored issues
show
Unused Code introduced by
The parameter $urlAliasService 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...
1043
    {
1044
        try {
1045
            $this->getRepository()->getURLAliasService()->lookup($urlPath);
1046
            $this->fail(sprintf('Alias [%s] should not exist', $urlPath));
1047
        } catch (\eZ\Publish\API\Repository\Exceptions\NotFoundException $e) {
1048
            $this->assertTrue(true);
1049
        }
1050
    }
1051
1052
    /**
1053
     * Get Test Double for TrashItem for exception testing and similar.
1054
     *
1055
     * @param int $trashId
1056
     * @param int $contentId
1057
     * @param int $parentLocationId
1058
     *
1059
     * @return \eZ\Publish\API\Repository\Values\Content\TrashItem
1060
     */
1061
    private function getTrashItemDouble(int $trashId, int $contentId = 44, int $parentLocationId = 2): APITrashItem
1062
    {
1063
        return new TrashItem([
1064
            'id' => $trashId,
1065
            'parentLocationId' => $parentLocationId,
1066
            'contentInfo' => new ContentInfo(['id' => $contentId]),
1067
        ]);
1068
    }
1069
}
1070