Completed
Push — ezp_30827 ( c6ab75...809fcf )
by
unknown
14:52
created

testEmptyTrashForUserWithSubtreeLimitation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

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