You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2011/12/27 19:17:40 UTC

svn commit: r1224964 [2/2] - in /jackrabbit/sandbox/jackrabbit-microkernel/src: main/java/org/apache/jackrabbit/ main/java/org/apache/jackrabbit/state/ main/java/org/apache/jackrabbit/utils/ test/java/org/apache/jackrabbit/ test/java/org/apache/jackrab...

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/state/TransientSpaceTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/state/TransientSpaceTest.java?rev=1224964&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/state/TransientSpaceTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/state/TransientSpaceTest.java Tue Dec 27 18:17:39 2011
@@ -0,0 +1,837 @@
+package org.apache.jackrabbit.state;
+
+import org.apache.jackrabbit.Path;
+import org.apache.jackrabbit.json.FullJsonParser;
+import org.apache.jackrabbit.json.JsonValue;
+import org.apache.jackrabbit.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.json.JsonValue.JsonObject;
+import org.apache.jackrabbit.json.JsonValue.Type;
+import org.apache.jackrabbit.json.UnescapingJsonTokenizer;
+import org.apache.jackrabbit.mk.MicroKernelFactory;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.api.MicroKernelException;
+import org.apache.jackrabbit.mk.store.NotFoundException;
+import org.apache.jackrabbit.state.ChangeTree.NodeDelta;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.internal.matchers.TypeSafeMatcher;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.RepositoryException;
+
+import static org.apache.jackrabbit.state.TransientSpaceTest.MicrokernelMatcher.propertyExists;
+import static org.apache.jackrabbit.state.TransientSpaceTest.MicrokernelMatcher.propertyHasValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class TransientSpaceTest {
+    private static final Path ROOT = Path.create("root");
+
+    private MicroKernel mk;
+    private TransientSpace transientSpace;
+
+    @Before
+    public void setup() {
+        mk = MicroKernelFactory.getInstance("fs:target/transient-space-test;clean");
+        mk.commit("", "+\"/root\":{}", mk.getHeadRevision(), "");
+
+        transientSpace = new TransientSpace("root", mk, mk.getHeadRevision());
+    }
+    
+    @After
+    public void tearDown() {
+        mk.dispose();
+    }
+
+    @Test(expected = ItemNotFoundException.class)
+    public void getNotExistingNode() throws ItemNotFoundException {
+        transientSpace.getNode(ROOT.concat("any"));
+    }
+    
+    @Test
+    public void addNode() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("added");
+        
+        Path addedPath = ROOT.concat("added");
+        NodeDelta added = transientSpace.getNode(addedPath);
+        assertEquals(addedPath, added.getPath());
+
+        transientSpace.save();
+        added = transientSpace.getNode(addedPath);
+        assertEquals(addedPath, added.getPath());
+    }
+
+    @Test
+    public void addNodes() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("a").addNode("b").addNode("c");
+        transientSpace.save();
+
+        NodeDelta c = transientSpace.getNode(ROOT.concat("a/b/c"));
+        c.addNode("d").addNode("e");
+        c.addNode("f");
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b/c/d/e")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b/c/f")));
+
+        transientSpace.save();
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b/c/d/e")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b/c/f")));
+    }
+
+    @Test(expected = ItemNotFoundException.class)
+    public void removeNotExistingNode() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.removeNode("remove");
+        transientSpace.save();
+    }
+
+    @Test
+    public void removeExistingNode() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("added");
+        transientSpace.save();
+
+        Path addedPath = ROOT.concat("added");
+        assertTrue(transientSpace.nodeExists(addedPath));
+        
+        root = transientSpace.getNode(ROOT);
+        root.removeNode("added");
+        assertFalse(transientSpace.nodeExists(addedPath));
+        transientSpace.save();
+        assertFalse(transientSpace.nodeExists(addedPath));
+    }
+
+    @Test
+    public void removeTransientNode() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("added");
+
+        Path addedPath = ROOT.concat("added");
+        assertTrue(transientSpace.nodeExists(addedPath));
+
+        root.removeNode("added");
+        assertFalse(transientSpace.nodeExists(addedPath));
+        transientSpace.save();
+        assertFalse(transientSpace.nodeExists(addedPath));
+    }
+
+    @Test(expected = ItemNotFoundException.class)
+    public void removeRemovedNode() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("added");
+        transientSpace.save();
+
+        Path addedPath = ROOT.concat("added");
+        NodeDelta added = transientSpace.getNode(addedPath);
+        added.addNode("added2");
+
+        assertTrue(added.hasNode("added2"));
+        added.removeNode("added2");
+        assertFalse(added.hasNode("added2"));
+        added.removeNode("added2");
+    }
+
+    @Test
+    public void moveTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target");
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveExisting() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test(expected = ItemNotFoundException.class)
+    public void moveNotExistingSource() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+    }
+
+    @Test(expected = ItemNotFoundException.class)
+    public void moveToNotExistingTarget() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+    }
+
+    @Test(expected = ItemExistsException.class)
+    public void moveToExistingTarget() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target").addNode("moved");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+    }
+
+    @Test
+    public void rename() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("source/moved"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/moved")));
+    }
+    
+    @Test
+    public void moveSubTree() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        NodeDelta b = root.addNode("a").addNode("b");
+        b.addNode("c").addNode("d");
+        NodeDelta y = root.addNode("v").addNode("w");
+        y.addNode("x").addNode("y");
+        transientSpace.save();
+
+        b = transientSpace.getNode(ROOT.concat("a/b"));
+        b.moveNode("c", y.getPath().concat("c-moved"));
+        
+        assertFalse(transientSpace.nodeExists(ROOT.concat("a/b/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("v/w/x/y")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("v/w/c-moved/d")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("a/b/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("a/b")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("v/w/x/y")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("v/w/c-moved/d")));
+    }
+    
+    @Test
+    public void modifyThenMove() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node").addNode("c");
+        root.addNode("target");
+        transientSpace.save();
+        
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        NodeDelta moveNode = sourceNode.getNode("node");
+        moveNode.addNode("a").addNode("b");
+        moveNode.removeNode("c");
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a/b")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a/b")));
+    }
+
+    @Test
+    public void moveThenModify() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node").addNode("c");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        NodeDelta movedNode = transientSpace.getNode(ROOT.concat("target/moved"));
+        movedNode.addNode("a").addNode("b");
+        movedNode.removeNode("c");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a/b")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a/b")));
+    }
+    
+    @Test
+    public void modifyThenMoveThenModify() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node").addNode("c1");
+        root.getNode("source").getNode("node").addNode("c2");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        NodeDelta moveNode = sourceNode.getNode("node");
+        moveNode.addNode("a1").addNode("b1");
+        moveNode.removeNode("c1");
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+
+        NodeDelta movedNode = transientSpace.getNode(ROOT.concat("target/moved"));
+        movedNode.addNode("a2").addNode("b2");
+        movedNode.removeNode("c2");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a1/b1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a2/b2")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a1/b1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved/c2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved/a2/b2")));
+    }
+
+    @Test
+    public void moveThenDelete() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        root.getNode("target").removeNode("moved");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveFromAddedThenDelete() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source");
+        root.addNode("target");
+        transientSpace.save();
+
+        root.getNode("source").addNode("node");
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        root.getNode("target").removeNode("moved");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveToAddedThenDelete() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        transientSpace.save();
+
+        root.addNode("target");
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        root.getNode("target").removeNode("moved");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveFromAddedToAddedThenDelete() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source");
+        transientSpace.save();
+
+        root.getNode("source").addNode("node");
+        root.addNode("target");
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        root.getNode("target").removeNode("moved");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+    
+    @Test
+    public void moveMoved() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target1");
+        root.addNode("target2");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target1/moved1"));
+        NodeDelta target1 = root.getNode("target1");
+        target1.moveNode("moved1", ROOT.concat("target2/moved2"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+    }
+
+    @Test
+    public void moveMovedThenDelete() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target1");
+        root.addNode("target2");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target1/moved1"));
+        NodeDelta target1 = root.getNode("target1");
+        target1.moveNode("moved1", ROOT.concat("target2/moved2"));
+        root.getNode("target2").removeNode("moved2");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+    }
+
+    @Test
+    public void moveMovedThenDeleteTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source");
+        transientSpace.save();
+        root.getNode("source").addNode("node");
+        root.addNode("target1");
+        root.addNode("target2");
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target1/moved1"));
+        NodeDelta target1 = root.getNode("target1");
+        target1.moveNode("moved1", ROOT.concat("target2/moved2"));
+        root.getNode("target2").removeNode("moved2");
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+    }
+
+    @Test
+    public void moveForthAndBack() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target");
+        transientSpace.save();
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        NodeDelta target = root.getNode("target");
+        target.moveNode("moved", ROOT.concat("source/node"));
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveForthAndBackTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source");
+        root.getNode("source").addNode("node");
+        transientSpace.save();
+        root.addNode("target");
+
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+        NodeDelta target = root.getNode("target");
+        target.moveNode("moved", ROOT.concat("source/node"));
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveFromTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("target");
+        transientSpace.save();
+
+        root.addNode("source1").addNode("node");
+        NodeDelta sourceNode = transientSpace.getNode(ROOT.concat("source1"));
+        sourceNode.moveNode("node", ROOT.concat("target/moved"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved")));
+    }
+
+    @Test
+    public void moveIntoMove() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node1").addNode("child");
+        root.getNode("source").addNode("node2");
+        root.addNode("target");
+        transientSpace.save();
+
+        root.getNode("source").moveNode("node1", ROOT.concat("target/moved1"));
+        root.getNode("source").moveNode("node2", ROOT.concat("target/moved1/child/moved2"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved1/child/moved2")));
+
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved1/child/moved2")));
+    }
+
+    @Test
+    public void moveIntoMoveTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node1").addNode("child");
+        root.getNode("source").addNode("node2");
+        root.addNode("target");
+
+        root.getNode("source").moveNode("node1", ROOT.concat("target/moved1"));
+        root.getNode("source").moveNode("node2", ROOT.concat("target/moved1/child/moved2"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved1/child/moved2")));
+
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node2")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target/moved1/child/moved2")));
+    }
+
+    @Test
+    public void moveOutOfMove() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node1").addNode("child");
+        root.addNode("target1");
+        root.addNode("target2");
+        transientSpace.save();
+
+        root.getNode("source").moveNode("node1", ROOT.concat("target1/moved1"));
+        root.getNode("target1").getNode("moved1").moveNode("child", ROOT.concat("target2/moved2"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1/child")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1/child")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+    }
+
+    @Test
+    public void moveOutOfMoveTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node1").addNode("child");
+        root.addNode("target1");
+        root.addNode("target2");
+
+        root.getNode("source").moveNode("node1", ROOT.concat("target1/moved1"));
+        root.getNode("target1").getNode("moved1").moveNode("child", ROOT.concat("target2/moved2"));
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1/child")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+
+        transientSpace.save();
+
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target1/moved1")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1/moved1/child")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved2")));
+    }
+
+    @Test
+    public void moveMovedParent() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target1");
+        root.addNode("target2");
+        transientSpace.save();
+
+        root.getNode("source").moveNode("node", ROOT.concat("target1/moved"));
+        root.moveNode("target1", ROOT.concat("target2/moved-target1"));
+        
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved-target1/moved")));
+
+        transientSpace.save();
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved-target1/moved")));
+    }
+
+    @Test
+    public void moveMovedParentTransient() throws RepositoryException {
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.addNode("source").addNode("node");
+        root.addNode("target1");
+        root.addNode("target2");
+
+        root.getNode("source").moveNode("node", ROOT.concat("target1/moved"));
+        root.moveNode("target1", ROOT.concat("target2/moved-target1"));
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1")));
+        assertTrue(transientSpace.nodeExists(ROOT.concat("target2/moved-target1/moved")));
+
+        transientSpace.save();
+
+        assertTrue(transientSpace.nodeExists(ROOT.concat("source")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("source/node")));
+        assertFalse(transientSpace.nodeExists(ROOT.concat("target1")));
+    }
+
+    @Test
+    public void setPropertyTest() throws RepositoryException {
+        // Set property
+        NodeDelta root = transientSpace.getNode(ROOT);
+        root.setValue("prop", JsonAtom.number(42));
+        transientSpace.save();
+        assertThat(mk, propertyHasValue(ROOT.concat("prop"), 42));
+
+        // Remove property
+        root.setValue("prop", null);
+        transientSpace.save();
+        assertThat(mk, CoreMatchers.not(propertyExists(ROOT.concat("prop"))));
+
+        // Transiently remove property
+        root.setValue("prop", JsonAtom.number(42));
+        root.setValue("prop", null);
+        transientSpace.save();
+        assertThat(mk, CoreMatchers.not(propertyExists(ROOT.concat("prop"))));
+
+        // Set property on transiently added parent
+        root.addNode("node1").addNode("node2").setValue("prop", JsonAtom.string("value"));
+        transientSpace.save();
+        assertThat(mk, propertyHasValue(ROOT.concat("node1/node2/prop"), "value"));
+
+        // Set property and move parent
+        root.addNode("source").addNode("node").setValue("prop1", JsonAtom.number(42));
+        root.getNode("source").getNode("node").setValue("prop2", JsonAtom.number(43));
+        root.addNode("target");
+        transientSpace.save();
+        assertThat(mk, propertyHasValue(ROOT.concat("source/node/prop1"), 42));
+        assertThat(mk, propertyHasValue(ROOT.concat("source/node/prop2"), 43));
+
+        root.getNode("source").moveNode("node", ROOT.concat("target/moved"));
+        root.getNode("target").getNode("moved").setValue("prop2", null);
+        transientSpace.save();
+        assertThat(mk, CoreMatchers.not(propertyExists(ROOT.concat("source/node/prop1"))));
+        assertThat(mk, propertyHasValue(ROOT.concat("target/moved/prop1"), 42));
+        assertThat(mk, CoreMatchers.not(propertyExists(ROOT.concat("source/node/prop2"))));
+        assertThat(mk, CoreMatchers.not(propertyExists(ROOT.concat("target/moved/prop2"))));
+    }
+
+    public static class MicrokernelMatcher {
+        private MicrokernelMatcher() { }
+
+        public static Matcher<MicroKernel> propertyHasValue(final Path path, final int value) {
+            return new TypeSafeMatcher<MicroKernel>() {
+
+                @Override
+                public boolean matchesSafely(MicroKernel mk) {
+                    JsonObject node = parseJson(mk.getNodes(path.getParent().getMkPath(), mk.getHeadRevision()));
+                    JsonValue prop = node.get(path.getName());
+                    return prop != null && prop.type() == Type.NUMBER
+                            && Integer.parseInt(prop.asAtom().value()) == value;
+                }
+
+                @Override
+                public void describeTo(Description description) {
+                    description.appendText(path + " = ").appendValue(value);
+                }
+            };
+        }
+
+        public static Matcher<MicroKernel> propertyHasValue(final Path path, final String value) {
+            return new TypeSafeMatcher<MicroKernel>() {
+
+                @Override
+                public boolean matchesSafely(MicroKernel mk) {
+                    JsonObject node = parseJson(mk.getNodes(path.getParent().getMkPath(), mk.getHeadRevision()));
+                    JsonValue prop = node.get(path.getName());
+                    return prop != null && prop.type() == Type.STRING
+                            && prop.asAtom().value().equals(value);
+                }
+
+                @Override
+                public void describeTo(Description description) {
+                    description.appendText(path + " = ").appendValue(value);
+                }
+            };
+        }
+
+        public static Matcher<MicroKernel> propertyExists(final Path path) {
+            return new TypeSafeMatcher<MicroKernel>() {
+
+                @Override
+                public boolean matchesSafely(MicroKernel mk) {
+                    try {
+                        JsonObject node = parseJson(mk.getNodes(path.getParent().getMkPath(), mk.getHeadRevision()));
+                        JsonValue prop = node.get(path.getName());
+                        return prop != null;
+                    }
+                    catch (MicroKernelException e) {
+                        if (e.getCause() instanceof NotFoundException) {
+                            return false;
+                        }
+                        else {
+                            throw e;
+                        }
+                    }
+                }
+
+                @Override
+                public void describeTo(Description description) {
+                    description.appendText("Property exists").appendValue(path);
+                }
+            };
+        }
+
+        //------------------------------------------< private >---
+
+        private static JsonObject parseJson(String json) {
+            return FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+        }
+    }
+
+}