Passed
Push — 4 ( 5c2b28...a2ab1a )
by Maxime
13:32 queued 04:46
created

HierarchyTest::testPreventLoop()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 14
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\ORM\ValidationException;
6
use SilverStripe\Versioned\Versioned;
7
use SilverStripe\Dev\SapphireTest;
8
9
class HierarchyTest extends SapphireTest
10
{
11
    protected static $fixture_file = 'HierarchyTest.yml';
12
13
    protected static $extra_dataobjects = [
14
        HierarchyTest\TestObject::class,
15
        HierarchyTest\HideTestObject::class,
16
        HierarchyTest\HideTestSubObject::class,
17
        HierarchyTest\HierarchyOnSubclassTestObject::class,
18
        HierarchyTest\HierarchyOnSubclassTestSubObject::class,
19
    ];
20
21
    public static function getExtraDataObjects()
22
    {
23
        // Prevent setup breaking if versioned module absent
24
        if (class_exists(Versioned::class)) {
25
            return parent::getExtraDataObjects();
26
        }
27
        return [];
28
    }
29
30
    public function setUp()
31
    {
32
        parent::setUp();
33
34
        // Note: Soft support for versioned module optionality
35
        if (!class_exists(Versioned::class)) {
36
            $this->markTestSkipped('HierarchyTest requires the Versioned extension');
37
        }
38
    }
39
40
    /**
41
     * Test the Hierarchy prevents infinite loops.
42
     */
43
    public function testPreventLoop()
44
    {
45
        $this->expectException(ValidationException::class);
46
        $this->expectExceptionMessage(sprintf(
47
            'Infinite loop found within the "%s" hierarchy',
48
            HierarchyTest\TestObject::class
49
        ));
50
51
        /** @var HierarchyTest\TestObject $obj2 */
52
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
53
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
54
55
        $obj2->ParentID = $obj2aa->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
56
        $obj2->write();
57
    }
58
59
    /**
60
     * Test Hierarchy::AllHistoricalChildren().
61
     */
62
    public function testAllHistoricalChildren()
63
    {
64
        // Delete some objs
65
        $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b')->delete();
66
        $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3a')->delete();
67
        $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3')->delete();
68
69
        // Check that obj1-3 appear at the top level of the AllHistoricalChildren tree
70
        $this->assertEquals(
71
            ["Obj 1", "Obj 2", "Obj 3"],
72
            HierarchyTest\TestObject::singleton()->AllHistoricalChildren()->column('Title')
73
        );
74
75
        // Check numHistoricalChildren
76
        $this->assertEquals(3, HierarchyTest\TestObject::singleton()->numHistoricalChildren());
77
78
        // Check that both obj 2 children are returned
79
        /** @var HierarchyTest\TestObject $obj2 */
80
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
81
        $this->assertEquals(
82
            ["Obj 2a", "Obj 2b"],
83
            $obj2->AllHistoricalChildren()->column('Title')
0 ignored issues
show
Bug introduced by
The method AllHistoricalChildren() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
            $obj2->/** @scrutinizer ignore-call */ 
84
                   AllHistoricalChildren()->column('Title')
Loading history...
84
        );
85
86
        // Check numHistoricalChildren
87
        $this->assertEquals(2, $obj2->numHistoricalChildren());
0 ignored issues
show
Bug introduced by
The method numHistoricalChildren() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

87
        $this->assertEquals(2, $obj2->/** @scrutinizer ignore-call */ numHistoricalChildren());
Loading history...
88
89
90
        // Obj 3 has been deleted; let's bring it back from the grave
91
        /** @var HierarchyTest\TestObject $obj3 */
92
        $obj3 = Versioned::get_including_deleted(
93
            HierarchyTest\TestObject::class,
94
            "\"Title\" = 'Obj 3'"
95
        )->First();
96
97
        // Check that all obj 3 children are returned
98
        $this->assertEquals(
99
            ["Obj 3a", "Obj 3b", "Obj 3c", "Obj 3d"],
100
            $obj3->AllHistoricalChildren()->column('Title')
101
        );
102
103
        // Check numHistoricalChildren
104
        $this->assertEquals(4, $obj3->numHistoricalChildren());
105
    }
106
107
    public function testNumChildren()
108
    {
109
        /** @var HierarchyTest\TestObject $obj1 */
110
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
111
        /** @var HierarchyTest\TestObject $obj2 */
112
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
113
        /** @var HierarchyTest\TestObject $obj3 */
114
        $obj3 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3');
115
        /** @var HierarchyTest\TestObject $obj2a */
116
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
117
        /** @var HierarchyTest\TestObject $obj2b */
118
        $obj2b = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b');
119
        /** @var HierarchyTest\TestObject $obj3a */
120
        $obj3a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3a');
121
        /** @var HierarchyTest\TestObject $obj3b */
122
        $obj3b = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj3d');
123
124
        $this->assertEquals(0, $obj1->numChildren());
0 ignored issues
show
Bug introduced by
The method numChildren() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
        $this->assertEquals(0, $obj1->/** @scrutinizer ignore-call */ numChildren());
Loading history...
125
        $this->assertEquals(2, $obj2->numChildren());
126
        $this->assertEquals(4, $obj3->numChildren());
127
        $this->assertEquals(2, $obj2a->numChildren());
128
        $this->assertEquals(0, $obj2b->numChildren());
129
        $this->assertEquals(2, $obj3a->numChildren());
130
        $this->assertEquals(0, $obj3b->numChildren());
131
        $obj1Child1 = new HierarchyTest\TestObject();
132
        $obj1Child1->ParentID = $obj1->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
133
        $obj1Child1->write();
134
        $this->assertEquals(
135
            $obj1->numChildren(false),
136
            1,
137
            'numChildren() caching can be disabled through method parameter'
138
        );
139
        $obj1Child2 = new HierarchyTest\TestObject();
140
        $obj1Child2->ParentID = $obj1->ID;
141
        $obj1Child2->write();
142
        $obj1->flushCache();
143
        $this->assertEquals(
144
            $obj1->numChildren(),
145
            2,
146
            'numChildren() caching can be disabled by flushCache()'
147
        );
148
    }
149
150
    public function testNumChildrenHierarchyOnSubclass()
151
    {
152
        /** @var HierarchyTest\HierarchyOnSubclassTestObject $obj5 */
153
        $obj5 = $this->objFromFixture(HierarchyTest\HierarchyOnSubclassTestObject::class, 'obj5');
154
155
        $this->assertFalse(
156
            $obj5->hasMethod('numChildren'),
157
            'numChildren() cannot be called on object without Hierarchy extension'
158
        );
159
160
        /** @var HierarchyTest\HierarchyOnSubclassTestSubObject $obj5a */
161
        $obj5a = $this->objFromFixture(HierarchyTest\HierarchyOnSubclassTestSubObject::class, 'obj5a');
162
        /** @var HierarchyTest\HierarchyOnSubclassTestSubObject $obj5b */
163
        $obj5b = $this->objFromFixture(HierarchyTest\HierarchyOnSubclassTestSubObject::class, 'obj5b');
164
165
        $this->assertEquals(2, $obj5a->numChildren());
0 ignored issues
show
Bug introduced by
The method numChildren() does not exist on SilverStripe\ORM\Tests\H...OnSubclassTestSubObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

165
        $this->assertEquals(2, $obj5a->/** @scrutinizer ignore-call */ numChildren());
Loading history...
166
        $this->assertEquals(1, $obj5b->numChildren());
167
168
        $obj5bChild2 = new HierarchyTest\HierarchyOnSubclassTestSubObject();
169
        $obj5bChild2->ParentID = $obj5b->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\ORM\Tests\H...OnSubclassTestSubObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
170
        $obj5bChild2->write();
171
        $this->assertEquals(
172
            $obj5b->numChildren(false),
173
            2,
174
            'numChildren() caching can be disabled through method parameter'
175
        );
176
        $obj5bChild3 = new HierarchyTest\HierarchyOnSubclassTestSubObject();
177
        $obj5bChild3->ParentID = $obj5b->ID;
178
        $obj5bChild3->write();
179
        $obj5b->flushCache();
180
        $this->assertEquals(
181
            $obj5b->numChildren(),
182
            3,
183
            'numChildren() caching can be disabled by flushCache()'
184
        );
185
    }
186
187
    public function testLoadDescendantIDListIntoArray()
188
    {
189
        /** @var HierarchyTest\TestObject $obj2 */
190
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
191
        /** @var HierarchyTest\TestObject $obj2a */
192
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
193
        $obj2b = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b');
194
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
195
        $obj2ab = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2ab');
196
197
        $obj2IdList = $obj2->getDescendantIDList();
0 ignored issues
show
Bug introduced by
The method getDescendantIDList() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

197
        /** @scrutinizer ignore-call */ 
198
        $obj2IdList = $obj2->getDescendantIDList();
Loading history...
198
        $obj2aIdList = $obj2a->getDescendantIDList();
199
200
        $this->assertContains($obj2a->ID, $obj2IdList);
201
        $this->assertContains($obj2b->ID, $obj2IdList);
202
        $this->assertContains($obj2aa->ID, $obj2IdList);
203
        $this->assertContains($obj2ab->ID, $obj2IdList);
204
        $this->assertEquals(4, count($obj2IdList));
205
206
        $this->assertContains($obj2aa->ID, $obj2aIdList);
207
        $this->assertContains($obj2ab->ID, $obj2aIdList);
208
        $this->assertEquals(2, count($obj2aIdList));
209
    }
210
211
    /**
212
     * The "only deleted from stage" argument to liveChildren() should exclude
213
     * any page that has been moved to another location on the stage site
214
     */
215
    public function testLiveChildrenOnlyDeletedFromStage()
216
    {
217
        /** @var HierarchyTest\TestObject $obj1 */
218
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
219
        /** @var HierarchyTest\TestObject $obj2 */
220
        $obj2 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2');
221
        /** @var HierarchyTest\TestObject $obj2a */
222
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
223
        /** @var HierarchyTest\TestObject $obj2b */
224
        $obj2b = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2b');
225
226
        // Get a published set of objects for our fixture
227
        $obj1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
0 ignored issues
show
Bug introduced by
The method copyVersionToStage() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
        $obj1->/** @scrutinizer ignore-call */ 
228
               copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
Loading history...
228
        $obj2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
229
        $obj2a->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
230
        $obj2b->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
231
232
        // Then delete 2a from stage and move 2b to a sub-node of 1.
233
        $obj2a->delete();
234
        $obj2b->ParentID = $obj1->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
235
        $obj2b->write();
236
237
        // Get live children, excluding pages that have been moved on the stage site
238
        $children = $obj2->liveChildren(true, true)->column("Title");
0 ignored issues
show
Bug introduced by
The method liveChildren() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

238
        $children = $obj2->/** @scrutinizer ignore-call */ liveChildren(true, true)->column("Title");
Loading history...
239
240
        // 2a has been deleted from stage and should be shown
241
        $this->assertContains("Obj 2a", $children);
242
243
        // 2b has merely been moved to a different parent and so shouldn't be shown
244
        $this->assertNotContains("Obj 2b", $children);
245
    }
246
247
    public function testBreadcrumbs()
248
    {
249
        /** @var HierarchyTest\TestObject $obj1 */
250
        $obj1 = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj1');
251
        /** @var HierarchyTest\TestObject $obj2a */
252
        $obj2a = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2a');
253
        /** @var HierarchyTest\TestObject $obj2aa */
254
        $obj2aa = $this->objFromFixture(HierarchyTest\TestObject::class, 'obj2aa');
255
256
        $this->assertEquals('Obj 1', $obj1->getBreadcrumbs());
0 ignored issues
show
Bug introduced by
The method getBreadcrumbs() does not exist on SilverStripe\ORM\Tests\HierarchyTest\TestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

256
        $this->assertEquals('Obj 1', $obj1->/** @scrutinizer ignore-call */ getBreadcrumbs());
Loading history...
257
        $this->assertEquals('Obj 2 &raquo; Obj 2a', $obj2a->getBreadcrumbs());
258
        $this->assertEquals('Obj 2 &raquo; Obj 2a &raquo; Obj 2aa', $obj2aa->getBreadcrumbs());
259
    }
260
261
    public function testNoHideFromHierarchy()
262
    {
263
        /** @var HierarchyTest\HideTestObject $obj4 */
264
        $obj4 = $this->objFromFixture(HierarchyTest\HideTestObject::class, 'obj4');
265
        $obj4->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
0 ignored issues
show
Bug introduced by
The method copyVersionToStage() does not exist on SilverStripe\ORM\Tests\H...rchyTest\HideTestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

265
        $obj4->/** @scrutinizer ignore-call */ 
266
               copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
Loading history...
266
267
        foreach ($obj4->stageChildren() as $child) {
0 ignored issues
show
Bug introduced by
The method stageChildren() does not exist on SilverStripe\ORM\Tests\H...rchyTest\HideTestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

267
        foreach ($obj4->/** @scrutinizer ignore-call */ stageChildren() as $child) {
Loading history...
268
            $child->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
269
        }
270
        $this->assertEquals($obj4->stageChildren()->Count(), 2);
271
        $this->assertEquals($obj4->liveChildren()->Count(), 2);
0 ignored issues
show
Bug introduced by
The method liveChildren() does not exist on SilverStripe\ORM\Tests\H...rchyTest\HideTestObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

271
        $this->assertEquals($obj4->/** @scrutinizer ignore-call */ liveChildren()->Count(), 2);
Loading history...
272
    }
273
274
    public function testHideFromHierarchy()
275
    {
276
        HierarchyTest\HideTestObject::config()->update(
277
            'hide_from_hierarchy',
278
            [ HierarchyTest\HideTestSubObject::class ]
279
        );
280
        /** @var HierarchyTest\HideTestObject $obj4 */
281
        $obj4 = $this->objFromFixture(HierarchyTest\HideTestObject::class, 'obj4');
282
        $obj4->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
283
284
        // load without using stage children otherwise it'll bbe filtered before it's publish
285
        // we need to publish all of them, and expect liveChildren to return some.
286
        $children = HierarchyTest\HideTestObject::get()
287
            ->filter('ParentID', (int)$obj4->ID)
288
            ->exclude('ID', (int)$obj4->ID);
289
290
        /** @var HierarchyTest\HideTestObject $child */
291
        foreach ($children as $child) {
292
            $child->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
293
        }
294
        $this->assertEquals($obj4->stageChildren()->Count(), 1);
295
        $this->assertEquals($obj4->liveChildren()->Count(), 1);
296
    }
297
}
298