Skip to content

Commit cf50084

Browse files
committed
Merge pull request #94 from fazy/cloning-prevent-sns-create
[WIP] Prevent creating same name siblings
2 parents f225159 + cfcadbe commit cf50084

File tree

3 files changed

+143
-27
lines changed

3 files changed

+143
-27
lines changed

fixtures/10_Writing/clone.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,33 @@
9393
</sv:property>
9494
</sv:node>
9595
</sv:node>
96+
<sv:node sv:name="testWorkspaceCloneNonCorresponding">
97+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
98+
<sv:value>nt:unstructured</sv:value>
99+
</sv:property>
100+
<sv:node sv:name="sourceRemoveExisting">
101+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
102+
<sv:value>nt:unstructured</sv:value>
103+
</sv:property>
104+
<sv:property sv:name="jcr:mixinTypes" sv:type="Name">
105+
<sv:value>mix:referenceable</sv:value>
106+
</sv:property>
107+
<sv:property sv:name="jcr:uuid" sv:type="String">
108+
<sv:value>b33fbe21-d3d8-4f52-8188-ad18a62dea9b</sv:value>
109+
</sv:property>
110+
</sv:node>
111+
<sv:node sv:name="sourceNoRemoveExisting">
112+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
113+
<sv:value>nt:unstructured</sv:value>
114+
</sv:property>
115+
<sv:property sv:name="jcr:mixinTypes" sv:type="Name">
116+
<sv:value>mix:referenceable</sv:value>
117+
</sv:property>
118+
<sv:property sv:name="jcr:uuid" sv:type="String">
119+
<sv:value>1ef2d543-a889-48a3-ae46-a97cf4767e95</sv:value>
120+
</sv:property>
121+
</sv:node>
122+
</sv:node>
96123
<sv:node sv:name="testWorkspaceCorrespondingNode">
97124
<sv:property sv:name="jcr:primaryType" sv:type="Name">
98125
<sv:value>nt:unstructured</sv:value>

fixtures/general/additionalWorkspace.xml

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,51 @@
1414
xmlns:sv="http://www.jcp.org/jcr/sv/1.0"
1515
xmlns:rep="internal"
1616

17-
sv:name="tests_general_additional_workspace">
17+
sv:name="tests_additional_workspace">
18+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
19+
<sv:value>nt:unstructured</sv:value>
20+
</sv:property>
21+
<sv:node sv:name="testWorkspaceCloneNonCorresponding">
1822
<sv:property sv:name="jcr:primaryType" sv:type="Name">
23+
<sv:value>nt:unstructured</sv:value>
24+
</sv:property>
25+
<sv:node sv:name="destRemoveExisting">
26+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
27+
<sv:value>nt:unstructured</sv:value>
28+
</sv:property>
29+
<sv:property sv:name="jcr:mixinTypes" sv:type="Name">
30+
<sv:value>mix:referenceable</sv:value>
31+
</sv:property>
32+
<sv:property sv:name="jcr:uuid" sv:type="String">
33+
<sv:value>f8019868-3533-4519-a077-9c8601950627</sv:value>
34+
</sv:property>
35+
</sv:node>
36+
<sv:node sv:name="destNoRemoveExisting">
37+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
1938
<sv:value>nt:unstructured</sv:value>
39+
</sv:property>
40+
<sv:property sv:name="jcr:mixinTypes" sv:type="Name">
41+
<sv:value>mix:referenceable</sv:value>
42+
</sv:property>
43+
<sv:property sv:name="jcr:uuid" sv:type="String">
44+
<sv:value>9aee563c-b5b7-44f7-a16d-f94116f4dfba</sv:value>
45+
</sv:property>
46+
</sv:node>
47+
</sv:node>
48+
<sv:node sv:name="testWorkspaceCloneReferenceable">
49+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
50+
<sv:value>nt:unstructured</sv:value>
2051
</sv:property>
52+
<sv:node sv:name="destExistingNode">
53+
<sv:property sv:name="jcr:primaryType" sv:type="Name">
54+
<sv:value>nt:unstructured</sv:value>
55+
</sv:property>
56+
<sv:property sv:name="jcr:mixinTypes" sv:type="Name">
57+
<sv:value>mix:referenceable</sv:value>
58+
</sv:property>
59+
<sv:property sv:name="jcr:uuid" sv:type="String">
60+
<sv:value>a6e94d5f-6aee-44c8-878e-afca80d3e41c</sv:value>
61+
</sv:property>
62+
</sv:node>
63+
</sv:node>
2164
</sv:node>

tests/10_Writing/CloneMethodsTest.php

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ public function testCloneReferenceableWithChild()
8787
}
8888

8989
/**
90-
* Clone a referenceable node, then clone again with 'remove existing' feature.
90+
* Clone a referenceable node, then clone again with removeExisting = true
91+
* This should overwrite the existing, corresponding node (same UUID)
9192
*/
9293
public function testCloneReferenceableRemoveExisting()
9394
{
@@ -127,6 +128,9 @@ public function testCloneReferenceableRemoveExisting()
127128
}
128129

129130
/**
131+
* Clone a referenceable node, then clone again with removeExisting = false
132+
* This should cause an exception, even with a corresponding node (same UUID)
133+
*
130134
* @expectedException \PHPCR\ItemExistsException
131135
*/
132136
public function testCloneReferenceableNoRemoveExisting()
@@ -149,6 +153,10 @@ public function testCloneReferenceableNoRemoveExisting()
149153
}
150154

151155
/**
156+
* Clone a referenceable node, then clone again with removeExisting = false.
157+
* Even though the second clone is to a new location, because a corresponding node (same UUID)
158+
* already exists in the destination workspace, an exception should still be thrown.
159+
*
152160
* @expectedException \PHPCR\ItemExistsException
153161
*/
154162
public function testCloneNoRemoveExistingNewLocation()
@@ -171,6 +179,55 @@ public function testCloneNoRemoveExistingNewLocation()
171179
self::$destWs->cloneFrom($this->srcWsName, $srcNode, $secondDstNode, false);
172180
}
173181

182+
/**
183+
* Check that we don't inadvertently create same name siblings (SNS) with removeExisting = true.
184+
* This can happen when cloning from one workspace to another, when a node already exists at the
185+
* destination but is not a corresponding node (the nodes have different UUIDs)
186+
*
187+
* @expectedException \PHPCR\ItemExistsException
188+
*/
189+
public function testExistingNonCorrespondingNodeRemoveExisting()
190+
{
191+
$this->skipIfSameNameSiblingsSupported();
192+
193+
$srcNode = '/tests_write_manipulation_clone/testWorkspaceCloneNonCorresponding/sourceRemoveExisting';
194+
$dstNode = '/tests_additional_workspace/testWorkspaceCloneNonCorresponding/destRemoveExisting';
195+
196+
self::$destWs->cloneFrom($this->srcWsName, $srcNode, $dstNode, true);
197+
}
198+
199+
/**
200+
* Check that we don't inadvertently create same name siblings (SNS) with removeExisting = false.
201+
* This can happen when cloning from one workspace to another, when a node already exists at the
202+
* destination but is not a corresponding node (the nodes have different UUIDs)
203+
*
204+
* @expectedException \PHPCR\ItemExistsException
205+
*/
206+
public function testExistingNonCorrespondingNodeNoRemoveExisting()
207+
{
208+
$this->skipIfSameNameSiblingsSupported();
209+
210+
$srcNode = '/tests_write_manipulation_clone/testWorkspaceCloneNonCorresponding/sourceNoRemoveExisting';
211+
$dstNode = '/tests_additional_workspace/testWorkspaceCloneNonCorresponding/destNoRemoveExisting';
212+
213+
self::$destWs->cloneFrom($this->srcWsName, $srcNode, $dstNode, false);
214+
}
215+
216+
/**
217+
* Test when source node is non-referenceable but a referenceable node exists at destination path
218+
*
219+
* @expectedException \PHPCR\ItemExistsException
220+
*/
221+
public function testReferenceableDestNodeWithNonReferenceableSourceNode()
222+
{
223+
$this->skipIfSameNameSiblingsSupported();
224+
225+
$srcNode = '/tests_write_manipulation_clone/testWorkspaceClone/nonReferenceable';
226+
$dstNode = '/tests_additional_workspace/testWorkspaceCloneReferenceable/destExistingNode';
227+
228+
self::$destWs->cloneFrom($this->srcWsName, $srcNode, $dstNode, true);
229+
}
230+
174231
/**
175232
* @expectedException \PHPCR\NoSuchWorkspaceException
176233
*/
@@ -256,10 +313,14 @@ public function testCloneNonReferenceable()
256313
}
257314

258315
/**
259-
* Clone a non-referenceable node, then clone again with 'remove existing' feature.
316+
* Clone a non-referenceable node, then clone again with removeExisting = true
317+
*
318+
* @expectedException \PHPCR\ItemExistsException
260319
*/
261320
public function testCloneRemoveExistingNonReferenceable()
262321
{
322+
$this->skipIfSameNameSiblingsSupported();
323+
263324
$srcNode = '/tests_write_manipulation_clone/testWorkspaceClone/nonReferenceableRemoveExisting';
264325
$dstNode = $srcNode;
265326
$destSession = self::$destWs->getSession();
@@ -272,39 +333,17 @@ public function testCloneRemoveExistingNonReferenceable()
272333
$this->checkNodeProperty($clonedNode, 'jcr:primaryType', 'nt:unstructured');
273334
$this->checkNodeProperty($clonedNode, 'foo', 'bar_4');
274335

275-
// Update the source node after cloning it
276-
$node = $this->srcWs->getSession()->getNode($srcNode);
277-
$node->setProperty('foo', 'bar-updated');
278-
$node->setProperty('newProperty', 'hello');
279-
$this->srcWs->getSession()->save();
280-
281336
// Clone the updated source node
282337
self::$destWs->cloneFrom($this->srcWsName, $srcNode, $dstNode, true);
283-
284-
$this->renewDestinationSession();
285-
286-
// Check the first cloned node again; it should not have changed
287-
$clonedNode = $destSession->getNode($dstNode);
288-
$this->assertInstanceOf('PHPCR\NodeInterface', $clonedNode);
289-
$this->assertCount(2, $clonedNode->getProperties());
290-
$this->checkNodeProperty($clonedNode, 'jcr:primaryType', 'nt:unstructured');
291-
$this->checkNodeProperty($clonedNode, 'foo', 'bar_4');
292-
293-
// Second cloned node created with [2] appended to name
294-
$replacedDstNode = $srcNode . '[2]';
295-
$clonedReplacedNode = self::$destWs->getSession()->getNode($replacedDstNode);
296-
$this->assertInstanceOf('PHPCR\NodeInterface', $clonedReplacedNode);
297-
$this->assertCount(3, $clonedReplacedNode->getProperties());
298-
$this->checkNodeProperty($clonedReplacedNode, 'jcr:primaryType', 'nt:unstructured');
299-
$this->checkNodeProperty($clonedReplacedNode, 'foo', 'bar-updated');
300-
$this->checkNodeProperty($clonedReplacedNode, 'newProperty', 'hello');
301338
}
302339

303340
/**
304341
* @expectedException \PHPCR\ItemExistsException
305342
*/
306343
public function testCloneNonReferenceableNoRemoveExisting()
307344
{
345+
$this->skipIfSameNameSiblingsSupported();
346+
308347
$srcNode = '/tests_write_manipulation_clone/testWorkspaceClone/nonReferenceableNoRemoveExisting';
309348
$dstNode = $srcNode;
310349
$destSession = self::$destWs->getSession();
@@ -498,4 +537,11 @@ private function renewDestinationSession()
498537
$destSession = self::$loader->getRepository()->login(self::$loader->getCredentials(), self::$destWsName);
499538
self::$destWs = $destSession->getWorkspace();
500539
}
540+
541+
private function skipIfSameNameSiblingsSupported()
542+
{
543+
if (self::$staticSharedFixture['session']->getRepository()->getDescriptor('node.type.management.same.name.siblings.supported')) {
544+
$this->markTestSkipped('Test does not yet cover repositories that support same name siblings.');
545+
}
546+
}
501547
}

0 commit comments

Comments
 (0)