org.onehippo.repository.mock.MockNodeTest.java Source code

Java tutorial

Introduction

Here is the source code for org.onehippo.repository.mock.MockNodeTest.java

Source

/*
 *  Copyright 2012-2013 Hippo B.V. (http://www.onehippo.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.onehippo.repository.mock;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.jcr.Item;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;

import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.util.ISO8601;
import org.junit.Ignore;
import org.junit.Test;
import org.onehippo.repository.util.JcrConstants;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

public class MockNodeTest {

    @Test
    public void emptyNode() throws RepositoryException {
        MockNode empty = MockNode.root();
        assertTrue(empty.isNode());
        assertEquals("/", empty.getPath());
        assertEquals("", empty.getName());
        assertEquals("rep:root", empty.getPrimaryNodeType().getName());
        assertFalse(empty.hasProperties());
        assertFalse(empty.getProperties().hasNext());
        assertEquals(0, empty.getDepth());
    }

    @Test
    public void copyConstructor() throws RepositoryException {
        MockNode node = MockNode.root().addNode("test", "nt:unstructured");
        node.setProperty("testProp", "foo");
        node.addMixin("testMixin");

        MockNode copy = new MockNode("copy", "newPrimaryType", node);
        copy.setParent(node.getParent());
        assertEquals("copy", copy.getName());
        assertEquals("newPrimaryType", copy.getPrimaryNodeType().getName());
        assertEquals("foo", copy.getProperty("testProp").getString());
        assertTrue(copy.isNodeType("testMixin"));

        // updating the original node should not affect the copy
        node.setProperty("testProp", "newValue");
        assertEquals("foo", copy.getProperty("testProp").getString());

        // removing a property from the copy should not affect the original node
        copy.removeProperty("testProp");
        assertFalse(copy.hasProperty("testProp"));
        assertTrue(node.hasProperty("testProp"));
    }

    @Test
    public void propertyIsSet() throws RepositoryException {
        MockNode node = MockNode.root();
        node.setProperty("prop", "value");

        assertTrue(node.hasProperty("prop"));
        Property actual = node.getProperty("prop");
        assertFalse(actual.isMultiple());
        assertEquals("value", actual.getString());
        assertEquals("/prop", actual.getPath());
        assertEquals(node, actual.getParent());
        assertEquals(1, actual.getDepth());
    }

    @Test
    public void propertyIsOverwritten() throws RepositoryException {
        MockNode node = MockNode.root();
        node.setProperty("prop", "value1");
        node.setProperty("prop", "value2");

        assertTrue(node.hasProperty("prop"));
        Property actual = node.getProperty("prop");
        assertFalse(actual.isMultiple());
        assertEquals("value2", actual.getString());
        assertEquals("/prop", actual.getPath());
        assertEquals(node, actual.getParent());
    }

    @Test
    public void multiplePropertyIsSet() throws RepositoryException {
        MockNode node = MockNode.root();
        String[] values = { "value1", "value2" };
        node.setProperty("prop", values);

        assertTrue(node.hasProperty("prop"));
        Property actual = node.getProperty("prop");
        assertTrue(actual.isMultiple());
        assertEquals("/prop", actual.getPath());
        assertEquals(node, actual.getParent());

        MockValue[] expected = new MockValue[2];
        expected[0] = new MockValue(PropertyType.STRING, "value1");
        expected[1] = new MockValue(PropertyType.STRING, "value2");
        assertArrayEquals(expected, actual.getValues());
    }

    @Test
    public void multiplePropertyIsOverwritten() throws RepositoryException {
        MockNode node = MockNode.root();
        String[] values1 = { "value1.1", "value1.2" };
        node.setProperty("prop", values1);
        String[] values2 = { "value2.1", "value2.2" };
        node.setProperty("prop", values2);

        assertTrue(node.hasProperty("prop"));
        Property actual = node.getProperty("prop");
        assertTrue(actual.isMultiple());

        MockValue[] expected = new MockValue[2];
        expected[0] = new MockValue(PropertyType.STRING, "value2.1");
        expected[1] = new MockValue(PropertyType.STRING, "value2.2");
        assertArrayEquals(expected, actual.getValues());
    }

    @Test
    public void multiplePropertyOfOneValue() throws RepositoryException {
        MockNode node = MockNode.root();
        String[] values = { "value" };
        node.setProperty("prop", values);

        assertTrue(node.hasProperty("prop"));
        Property actual = node.getProperty("prop");
        assertTrue(actual.isMultiple());

        MockValue[] expected = new MockValue[1];
        expected[0] = new MockValue(PropertyType.STRING, "value");
        assertArrayEquals(expected, actual.getValues());
    }

    @Test
    public void propertyIsReturnedByIterator() throws RepositoryException {
        MockNode node = MockNode.root();
        node.setProperty("prop", "value");

        PropertyIterator iterator = node.getProperties();

        assertEquals(1, iterator.getSize());
        assertTrue(iterator.hasNext());

        Property property = iterator.nextProperty();
        assertEquals("prop", property.getName());
        assertEquals("value", property.getString());

        assertFalse(iterator.hasNext());
    }

    @Test
    public void propertiesAreReturnedByIterator() throws RepositoryException {
        MockNode node = MockNode.root();
        node.setProperty("prop1", "value1");
        node.setProperty("prop2", "value2");

        PropertyIterator iterator = node.getProperties();

        assertEquals(2, iterator.getSize());

        Map<String, String> expected = new HashMap<String, String>();
        expected.put("prop1", "value1");
        expected.put("prop2", "value2");

        // Test whether all pairs in the map are returned property names and values.
        // The iterator can return the properties in random order, hence the remove-from-map-once-seen construct.
        for (int i = 0; i < 2; i++) {
            assertTrue(iterator.hasNext());

            Property property = iterator.nextProperty();

            String actualName = property.getName();
            assertTrue(expected.containsKey(actualName));

            String expectedValue = expected.get(actualName);
            assertEquals(expectedValue, property.getString());

            expected.remove(actualName);
        }

        assertFalse("There should be only 2 properties", iterator.hasNext());
    }

    @Test(expected = PathNotFoundException.class)
    public void unknownProperty() throws RepositoryException {
        MockNode node = MockNode.root();
        node.getProperty("noSuchProperty");
    }

    @Test
    public void propertyCanBeRemoved() throws RepositoryException {
        MockNode node = MockNode.root();
        node.setProperty("prop", "value");

        assertTrue(node.hasProperty("prop"));

        Property prop = node.getProperty("prop");
        prop.remove();

        assertFalse(node.hasProperty("prop"));
        assertNoParent("Removed property should not have a parent", prop);
    }

    @Test(expected = PathNotFoundException.class)
    public void childNotFound() throws RepositoryException {
        MockNode node = MockNode.root();
        node.getNode("noSuchChild");
    }

    @Test
    public void sessionSaveIsIgnored() throws RepositoryException {
        MockNode root = MockNode.root();
        Session session = root.getSession();
        assertNotNull(session);
        session.save();
    }

    @Test
    public void nodeCanBeAdded() throws RepositoryException {
        MockNode root = MockNode.root();

        MockNode child = new MockNode("child");
        root.addNode(child);

        assertEquals(1, root.getNodes().getSize());
        assertSame(child, root.getNode("child"));
        assertEquals(1, child.getDepth());
    }

    @Test
    public void nodeCanBeAddedWithPrimaryType() throws RepositoryException {
        MockNode root = MockNode.root();

        Node child = root.addNode("child", "nt:unstructured");
        assertEquals("nt:unstructured", child.getPrimaryNodeType().getName());

        assertEquals(1, root.getNodes().getSize());
        assertSame(child, root.getNode("child"));
    }

    @Test
    public void nodeCanBeAddedWithPrimaryTypeInConstructor() throws RepositoryException {
        MockNode node = new MockNode("child", "nt:unstructured");
        assertEquals("nt:unstructured", node.getPrimaryNodeType().getName());
    }

    @Test
    public void nodeCanAddedBeWithRelativePath() throws RepositoryException {
        MockNode root = MockNode.root();
        MockNode child = new MockNode("child");
        root.addNode(child);

        Node grandchild = root.addNode("child/grandchild", "nt:unstructured");
        assertEquals("nt:unstructured", grandchild.getPrimaryNodeType().getName());

        assertEquals("Root node should still have only one child", 1, root.getNodes().getSize());
        assertEquals("Child node should have one child", 1, child.getNodes().getSize());
        assertSame(grandchild, child.getNode("grandchild"));
        assertEquals(2, grandchild.getDepth());
    }

    @Test(expected = PathNotFoundException.class)
    public void nodeAddedWithRelativePathButMissingIntermediateNodeThrowsException() throws RepositoryException {
        MockNode root = MockNode.root();
        Node grandchild = root.addNode("child/grandchild", "nt:unstructured");
    }

    @Test
    public void hasIdentifier() {
        MockNode node = new MockNode("foo");
        assertNotNull("A mock node should have an identifier", node.getIdentifier());
    }

    @Test
    public void rootHasSpecificIdentifier() throws RepositoryException {
        assertEquals("cafebabe-cafe-babe-cafe-babecafebabe", MockNode.root().getIdentifier());
    }

    @Test
    public void identifierOfNodesInSameTreeIsUnique() throws RepositoryException {
        MockNode root = MockNode.root();

        MockNode node1 = new MockNode("one");
        root.addNode(node1);

        MockNode node2 = new MockNode("two");
        root.addNode(node2);

        assertFalse("A mock node should have a identifier that is unique in the tree it is part of",
                node1.getIdentifier().equals(node2.getIdentifier()));
    }

    @Test
    public void rootIsOfTypeRepRoot() throws RepositoryException {
        assertTrue("Root node should be of type 'rep:root'", MockNode.root().isNodeType("rep:root"));
    }

    @Test
    public void nodeTypeMatchesPrimaryType() throws RepositoryException {
        MockNode node = new MockNode("test");
        node.setPrimaryType("my:type");
        assertTrue("Node should be of type equal to its primary type", node.isNodeType("my:type"));
    }

    @Test
    public void nodeWithoutPrimaryTypeIsNoType() {
        MockNode node = new MockNode("test");
        assertFalse(node.isNodeType("some:type"));
    }

    @Test
    public void childNodeCanBeRemoved() throws RepositoryException {
        final MockNode root = MockNode.root();

        Node child = root.addNode("child", "nt:unstructured");
        assertEquals("Child node should have been added", 1, root.getNodes().getSize());

        child.remove();
        assertFalse("Root should not have the removed node as a child", root.hasNode("child"));
        assertEquals("Removed child should no longer exist", 0, root.getNodes().getSize());
        assertNoParent("Removed child should not have a parent", child);
    }

    @Test
    public void rootCanBeRemoved() throws RepositoryException {
        MockNode.root().remove();
    }

    @Test
    public void iteratedChildrenCanBeRemoved() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.addNode("child1", "nt:unstructured");
        root.addNode("child2", "nt:unstructured");

        NodeIterator children = root.getNodes();
        while (children.hasNext()) {
            children.nextNode().remove();
        }

        assertEquals(0, root.getNodes().getSize());
    }

    @Test
    public void singlePropertyHasDefinition() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.setProperty("test", "value");

        final PropertyDefinition definition = root.getProperty("test").getDefinition();

        assertEquals("test", definition.getName());
        assertFalse("Single property should not be defined as multiple", definition.isMultiple());
    }

    @Test
    public void multiplePropertyHasDefinition() throws RepositoryException {
        final MockNode root = MockNode.root();
        final String[] values = { "one", "two" };
        root.setProperty("test", values);

        final PropertyDefinition definition = root.getProperty("test").getDefinition();

        assertEquals("test", definition.getName());
        assertTrue("Multiple property should not defined as multiple", definition.isMultiple());
    }

    @Test(expected = ItemNotFoundException.class)
    public void primaryItemNotDefined() throws RepositoryException {
        MockNode.root().getPrimaryItem();
    }

    @Test(expected = ItemNotFoundException.class)
    public void primaryItemNodeDoesNotExist() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.setPrimaryItemName("child");
        root.getPrimaryItem();
    }

    @Test
    public void primaryItemNode() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.setPrimaryItemName("child");
        final Node child = root.addNode("child", "nt:unstructured");

        final Item primaryItem = root.getPrimaryItem();
        assertSame(child, primaryItem);
    }

    @Test
    public void primaryItemProperty() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.setPrimaryItemName("prop");
        root.setProperty("prop", "value");

        final Item primaryItem = root.getPrimaryItem();
        assertFalse("Primary item should be a property", primaryItem.isNode());
        assertEquals("prop", primaryItem.getName());
    }

    @Test
    public void primaryNodeTypeKnowsPrimaryItemNameIsNotSet() throws RepositoryException {
        final MockNode root = MockNode.root();
        assertNull(root.getPrimaryNodeType().getPrimaryItemName());
    }

    @Test
    public void primaryNodeTypeKnowsPrimaryItemName() throws RepositoryException {
        final MockNode root = MockNode.root();
        root.setPrimaryItemName("foo");
        assertEquals("foo", root.getPrimaryNodeType().getPrimaryItemName());
    }

    @Test
    public void snsNodesSupportedByDefault() throws RepositoryException {
        MockNode root = MockNode.root();
        MockNode folder1 = root.addNode("folder1", "nt:unstructured");
        MockNode folder2 = folder1.addNode("folder2", "nt:unstructured");
        folder2 = folder1.addNode("folder2", "nt:unstructured");
        MockNode sns1 = folder2.addNode("sns", "un:unstructured");
        assertEquals("sns", sns1.getName());
        assertEquals("/folder1/folder2[2]/sns", sns1.getPath());
        MockNode sns2 = folder2.addNode("sns", "un:unstructured");
        assertEquals("sns", sns2.getName());
        assertEquals("/folder1/folder2[2]/sns[2]", sns2.getPath());
        assertNotSame(sns1, sns2);
        assertEquals(sns1.getName(), sns2.getName());
    }

    @Test
    public void snsNodesSupportingDisabled() throws RepositoryException {
        MockNode root = MockNode.root();
        MockNode folder1 = root.addNode("folder1", "nt:unstructured");
        folder1.setSameNameSiblingSupported(false);
        MockNode folder2 = folder1.addNode("folder2", "nt:unstructured");

        try {
            folder2 = folder1.addNode("folder2", "nt:unstructured");
            fail("SNS node disabled.");
        } catch (ConstraintViolationException e) {
            // as expected.
        }
    }

    @Test
    public void getNodesWithNamePattern() throws RepositoryException {
        MockNode root = MockNode.root();
        MockNode folder = root.addNode("folder1", "nt:unstructured");
        MockNode handle = folder.addNode("document1", "nt:unstructured");
        handle.addNode("document1", "nt:unstructured");
        handle.addNode("document1", "nt:unstructured");
        handle.addNode("document1", "nt:unstructured");
        handle.addNode("hippo:request", "nt:unstructured");

        Set<String> variantPaths = new HashSet<String>();
        NodeIterator nodeIt = handle.getNodes("document*");
        assertEquals(3, nodeIt.getSize());

        while (nodeIt.hasNext()) {
            variantPaths.add(nodeIt.nextNode().getPath());
        }

        assertTrue(variantPaths.contains("/folder1/document1/document1"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[2]"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[3]"));

        variantPaths.clear();

        nodeIt = handle.getNodes("document*|hippo:*");
        assertEquals(4, nodeIt.getSize());

        while (nodeIt.hasNext()) {
            variantPaths.add(nodeIt.nextNode().getPath());
        }

        assertTrue(variantPaths.contains("/folder1/document1/document1"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[2]"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[3]"));
        assertTrue(variantPaths.contains("/folder1/document1/hippo:request"));

        variantPaths.clear();

        nodeIt = handle.getNodes(new String[] { "document*", "hippo:*", "doc*" });
        assertEquals(4, nodeIt.getSize());

        while (nodeIt.hasNext()) {
            variantPaths.add(nodeIt.nextNode().getPath());
        }

        assertTrue(variantPaths.contains("/folder1/document1/document1"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[2]"));
        assertTrue(variantPaths.contains("/folder1/document1/document1[3]"));
        assertTrue(variantPaths.contains("/folder1/document1/hippo:request"));
    }

    @Test
    public void getPropertiesWithNamePattern() throws RepositoryException {
        MockNode root = MockNode.root();
        MockNode node = root.addNode("node1", "nt:unstructured");
        node.setProperty("prop1", "value1");
        node.setProperty("prop2", "value2");
        node.setProperty("hippo:holder", "editor");

        Set<String> propPaths = new HashSet<String>();
        PropertyIterator propIt = node.getProperties("prop*");
        assertEquals(2, propIt.getSize());

        while (propIt.hasNext()) {
            propPaths.add(propIt.nextProperty().getPath());
        }

        assertTrue(propPaths.contains("/node1/prop1"));
        assertTrue(propPaths.contains("/node1/prop2"));

        propPaths.clear();

        propIt = node.getProperties("prop* | hippo:*");
        assertEquals(3, propIt.getSize());

        while (propIt.hasNext()) {
            propPaths.add(propIt.nextProperty().getPath());
        }

        assertTrue(propPaths.contains("/node1/prop1"));
        assertTrue(propPaths.contains("/node1/prop2"));
        assertTrue(propPaths.contains("/node1/hippo:holder"));

        propPaths.clear();

        propIt = node.getProperties(new String[] { "prop*", "hippo:*", "pro*" });
        assertEquals(3, propIt.getSize());

        while (propIt.hasNext()) {
            propPaths.add(propIt.nextProperty().getPath());
        }

        assertTrue(propPaths.contains("/node1/prop1"));
        assertTrue(propPaths.contains("/node1/prop2"));
        assertTrue(propPaths.contains("/node1/hippo:holder"));
    }

    @Test
    public void testVariousTypesOfProperties() throws RepositoryException, IOException {
        MockNode root = MockNode.root();
        MockNode node = root.addNode("node1", "nt:unstructured");

        Calendar now = Calendar.getInstance();
        byte[] binaryData = new byte[1];

        node.setProperty("string1", "stringvalue1");
        node.setProperty("boolean1", true);
        node.setProperty("long1", Long.MAX_VALUE);
        node.setProperty("date1", now);
        node.setProperty("double1", Double.MAX_VALUE);
        node.setProperty("bigdecimal1", BigDecimal.TEN);
        node.setProperty("binary", new MockBinary(new ByteArrayInputStream(binaryData)));

        assertTrue(node.getProperty("boolean1").getBoolean());
        assertEquals(Long.MAX_VALUE, node.getProperty("long1").getLong());
        assertEquals(ISO8601.format(now), ISO8601.format(node.getProperty("date1").getDate()));
        assertEquals(Double.toString(Double.MAX_VALUE), Double.toString(node.getProperty("double1").getDouble()));
        assertEquals(BigDecimal.TEN, node.getProperty("bigdecimal1").getDecimal());
        assertEquals("stringvalue1", node.getProperty("string1").getString());
        assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(binaryData),
                node.getProperty("binary").getBinary().getStream()));

        node.setProperty("stringarray1", new String[] { "stringvalue1", "stringvalue2" });
        node.setProperty("booleanarray1", new MockValue[] { new MockValue(PropertyType.BOOLEAN, "true"),
                new MockValue(PropertyType.BOOLEAN, "false") });
        node.setProperty("longarray1", new MockValue[] { new MockValue(PropertyType.LONG, "123"),
                new MockValue(PropertyType.LONG, "456") });
        node.setProperty("datearray1",
                new MockValue[] { new MockValue(PropertyType.DATE, "2013-10-30T00:00:00.000Z"),
                        new MockValue(PropertyType.DATE, "2013-10-31T00:00:00.000Z") });
        node.setProperty("doublearray1", new MockValue[] { new MockValue(PropertyType.DOUBLE, "1.23"),
                new MockValue(PropertyType.DOUBLE, "4.56") });
        node.setProperty("bigdecimalarray1", new MockValue[] { new MockValue(PropertyType.DECIMAL, "1.23E3"),
                new MockValue(PropertyType.DECIMAL, "4.56E3") });

        Value[] values = node.getProperty("stringarray1").getValues();
        assertEquals(2, values.length);
        assertEquals("stringvalue1", values[0].getString());
        assertEquals("stringvalue2", values[1].getString());

        values = node.getProperty("booleanarray1").getValues();
        assertEquals(2, values.length);
        assertEquals(true, values[0].getBoolean());
        assertEquals(false, values[1].getBoolean());

        values = node.getProperty("longarray1").getValues();
        assertEquals(2, values.length);
        assertEquals(123, values[0].getLong());
        assertEquals(456, values[1].getLong());

        values = node.getProperty("datearray1").getValues();
        assertEquals(2, values.length);
        assertEquals("2013-10-30T00:00:00.000Z", ISO8601.format(values[0].getDate()));
        assertEquals("2013-10-31T00:00:00.000Z", ISO8601.format(values[1].getDate()));

        values = node.getProperty("doublearray1").getValues();
        assertEquals(2, values.length);
        assertEquals(Double.toString(1.23d), Double.toString(values[0].getDouble()));
        assertEquals(Double.toString(4.56), Double.toString(values[1].getDouble()));

        values = node.getProperty("bigdecimalarray1").getValues();
        assertEquals(2, values.length);
        assertEquals(new BigDecimal("1.23E3"), values[0].getDecimal());
        assertEquals(new BigDecimal("4.56E3"), values[1].getDecimal());
    }

    @Test
    public void testGetNodeWithRelPath() throws Exception {
        MockNode root = MockNode.root();
        MockNode folder = root.addNode("folder1", "nt:unstructured");
        folder.addNode("node1", "nt:unstructured");
        MockNode node2 = folder.addNode("node2", "nt:unstructured");
        MockNode node21 = node2.addNode("node21", "nt:unstructured");
        node21.setProperty("tag", "node21");
        MockNode node22 = node2.addNode("node22", "nt:unstructured");
        node22.setProperty("tag", "node22");
        MockNode node22sns = node2.addNode("node22", "nt:unstructured");
        node22sns.setProperty("tag", "node22sns");

        assertNotNull(root.getNode("folder1"));
        assertNotNull(folder.getNode("node1"));
        assertNotNull(folder.getNode("node2"));
        assertNotNull(folder.getNode("node2/node21"));
        assertEquals("node21", folder.getNode("node2/node21").getProperty("tag").getString());
        assertNotNull(folder.getNode("node2/node22"));
        assertEquals("node22", folder.getNode("node2/node22").getProperty("tag").getString());
        assertNotNull(folder.getNode("node2/node22[1]"));
        assertEquals("node22", folder.getNode("node2/node22[1]").getProperty("tag").getString());
        assertNotNull(folder.getNode("node2/node22[2]"));
        assertEquals("node22sns", folder.getNode("node2/node22[2]").getProperty("tag").getString());
    }

    @Test
    public void testHasNodeWithRelPath() throws Exception {
        MockNode root = MockNode.root();
        MockNode folder = root.addNode("folder1", "nt:unstructured");
        folder.addNode("node1", "nt:unstructured");
        MockNode node2 = folder.addNode("node2", "nt:unstructured");
        MockNode node21 = node2.addNode("node21", "nt:unstructured");
        node21.setProperty("tag", "node21");
        MockNode node22 = node2.addNode("node22", "nt:unstructured");
        node22.setProperty("tag", "node22");
        MockNode node22sns = node2.addNode("node22", "nt:unstructured");
        node22sns.setProperty("tag", "node22sns");

        assertTrue(root.hasNode("folder1"));
        assertTrue(folder.hasNode("node1"));
        assertTrue(folder.hasNode("node2"));
        assertTrue(folder.hasNode("node2/node21"));
        assertTrue(folder.hasNode("node2/node22"));
        assertTrue(folder.hasNode("node2/node22[1]"));
        assertTrue(folder.hasNode("node2/node22[2]"));
    }

    @Test
    public void testGetPropertyWithRelPath() throws Exception {
        MockNode root = MockNode.root();
        MockNode folder = root.addNode("folder1", "nt:unstructured");
        folder.addNode("node1", "nt:unstructured");
        MockNode node2 = folder.addNode("node2", "nt:unstructured");
        MockNode node21 = node2.addNode("node21", "nt:unstructured");
        node21.setProperty("tag", "node21");
        MockNode node22 = node2.addNode("node22", "nt:unstructured");
        node22.setProperty("tag", "node22");
        MockNode node22sns = node2.addNode("node22", "nt:unstructured");
        node22sns.setProperty("tag", "node22sns");

        assertEquals("node21", folder.getProperty("node2/node21/tag").getString());
        assertEquals("node22", folder.getProperty("node2/node22/tag").getString());
        assertEquals("node22", folder.getProperty("node2/node22[1]/tag").getString());
        assertEquals("node22sns", folder.getProperty("node2/node22[2]/tag").getString());
    }

    @Test
    public void testHasPropertyWithRelPath() throws Exception {
        MockNode root = MockNode.root();
        MockNode folder = root.addNode("folder1", "nt:unstructured");
        folder.addNode("node1", "nt:unstructured");
        MockNode node2 = folder.addNode("node2", "nt:unstructured");
        MockNode node21 = node2.addNode("node21", "nt:unstructured");
        node21.setProperty("tag", "node21");
        MockNode node22 = node2.addNode("node22", "nt:unstructured");
        node22.setProperty("tag", "node22");
        MockNode node22sns = node2.addNode("node22", "nt:unstructured");
        node22sns.setProperty("tag", "node22sns");

        assertTrue(folder.hasProperty("node2/node21/tag"));
        assertTrue(folder.hasProperty("node2/node22/tag"));
        assertTrue(folder.hasProperty("node2/node22[1]/tag"));
        assertTrue(folder.hasProperty("node2/node22[2]/tag"));
    }

    @Test(expected = UnsupportedRepositoryOperationException.class)
    public void checkinNonVersionableNode() throws RepositoryException {

        final MockNode root = MockNode.root();
        final VersionManager versionManager = root.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(root.getPath());
    }

    @Test
    public void checkinVersionableNode() throws RepositoryException {
        MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");
        assertTrue(node.isCheckedOut());
        assertTrue(node.getProperty(JcrConstants.JCR_IS_CHECKED_OUT).getBoolean());
        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());
        assertFalse(node.isCheckedOut());
        assertFalse(node.getProperty(JcrConstants.JCR_IS_CHECKED_OUT).getBoolean());
    }

    @Test
    public void frozenNode_test_frozen_uuid_and_node_types() throws RepositoryException, IOException {
        MockNode node = MockNode.root().addNode("node", "nt:unstructured");
        node.addMixin("mix:versionable");
        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        final Version versioned = versionManager.checkin(node.getPath());
        final Node frozenNode = versioned.getFrozenNode();
        assertEquals(node.getIdentifier(), frozenNode.getProperty(JcrConstants.JCR_FROZEN_UUID).getString());
        assertEquals("nt:unstructured", frozenNode.getProperty(JcrConstants.JCR_FROZEN_PRIMARY_TYPE).getString());
        assertFalse("mix:versionable should be skipped from frozen node",
                frozenNode.hasProperty(JcrConstants.JCR_FROZEN_MIXIN_TYPES));

    }

    @Test
    public void frozenNode_test_frozen_uuid_primary_with_mixin_types_besides_versionable()
            throws RepositoryException, IOException {
        MockNode node = MockNode.root().addNode("node", "nt:unstructured");
        node.addMixin("mix:versionable");
        node.addMixin("mix:etag");

        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        final Version versioned = versionManager.checkin(node.getPath());
        final Node frozenNode = versioned.getFrozenNode();

        assertEquals(node.getIdentifier(), frozenNode.getProperty(JcrConstants.JCR_FROZEN_UUID).getString());
        assertEquals("nt:unstructured", frozenNode.getProperty(JcrConstants.JCR_FROZEN_PRIMARY_TYPE).getString());
        assertTrue(frozenNode.hasProperty(JcrConstants.JCR_FROZEN_MIXIN_TYPES));
        assertEquals("mix:etag",
                frozenNode.getProperty(JcrConstants.JCR_FROZEN_MIXIN_TYPES).getValues()[0].getString());
    }

    @Test
    public void frozenNode_test_various_property_types() throws RepositoryException, IOException {
        MockNode node = MockNode.root().addNode("test", "nt:unstructured");
        node.addMixin("mix:versionable");
        node.setProperty("string", "string");
        node.setProperty("true", true);
        node.setProperty("false", false);
        node.setProperty("long", 10L);
        node.setProperty("binary", new MockBinary(IOUtils.toInputStream("hello world!")));
        node.setProperty("double", 10D);
        final BigDecimal bigDecimal = new BigDecimal(1.0000000012123d);
        node.setProperty("bigDecimal", bigDecimal);
        final Calendar cal = Calendar.getInstance();
        node.setProperty("cal", cal);

        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        final Version version = versionManager.checkin(node.getPath());

        assertEquals("string", version.getFrozenNode().getProperty("string").getString());
        assertEquals(true, version.getFrozenNode().getProperty("true").getBoolean());
        assertEquals(false, version.getFrozenNode().getProperty("false").getBoolean());
        assertEquals(10L, version.getFrozenNode().getProperty("long").getLong());
        assertEquals("hello world!",
                IOUtils.toString(version.getFrozenNode().getProperty("binary").getBinary().getStream()));
        assertEquals(10D, version.getFrozenNode().getProperty("double").getDouble(), 001D);
        assertEquals(bigDecimal, version.getFrozenNode().getProperty("bigDecimal").getDecimal());
        assertEquals(ISO8601.format(cal), ISO8601.format(node.getProperty("cal").getDate()));
    }

    @Test
    public void cannotModifyCheckedInNode() throws RepositoryException {
        final MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");
        node.setProperty("test", "foo");
        node.addNode("child", "nt:unstructured");
        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.setProperty("test", "bar");
            }
        });
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.getProperty("test").remove();
            }
        });
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.addNode("foo", "nt:unstructured");
            }
        });
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.getNode("child").remove();
            }
        });
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.remove();
            }
        });
        assertVersionException(new Code() {
            public void execute() throws RepositoryException {
                node.setPrimaryType("bar");
            }
        });
    }

    @Test(expected = UnsupportedRepositoryOperationException.class)
    public void checkoutNonVersionableNode() throws RepositoryException {

        final MockNode root = MockNode.root();
        final VersionManager versionManager = root.getSession().getWorkspace().getVersionManager();
        versionManager.checkout(root.getPath());
    }

    @Test
    public void checkoutCheckedInNode() throws RepositoryException {
        MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");

        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());
        versionManager.checkout(node.getPath());

        assertTrue(node.isCheckedOut());
        assertTrue(node.getProperty(JcrConstants.JCR_IS_CHECKED_OUT).getBoolean());
    }

    @Test
    public void canModifyCheckedOutNode() throws RepositoryException {
        final MockNode root = MockNode.root();
        final MockNode node = root.addNode("test", "nt:base");
        node.addMixin("mix:versionable");
        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());
        versionManager.checkout(node.getPath());

        node.setProperty("testProp", "bar");
        assertTrue(node.hasProperty("testProp"));
        final Property test = node.getProperty("testProp");
        assertEquals("bar", test.getString());
        test.remove();
        assertFalse(node.hasProperty("test"));

        assertFalse(node.hasNode("foo"));
        node.addNode("foo", "nt:unstructured");
        assertTrue(node.hasNode("foo"));

        assertTrue(root.hasNode("test"));
        node.remove();
        assertFalse(root.hasNode("test"));
    }

    @Test
    public void getRootVersion() throws RepositoryException, InterruptedException {
        final MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");

        final MockVersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        final MockVersionHistory versionHistory = versionManager.getVersionHistory(node.getPath());
        final Version rootVersion = versionHistory.getRootVersion();
        final Node frozenNode = rootVersion.getFrozenNode();

        assertEquals(node.getIdentifier(), frozenNode.getProperty(JcrConstants.JCR_FROZEN_UUID).getString());
    }

    @Test
    public void getAllVersions() throws RepositoryException, InterruptedException {
        final MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");
        node.setProperty("testProp", "a");

        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());
        versionManager.checkout(node.getPath());
        node.setProperty("testProp", "b");
        versionManager.checkin(node.getPath());

        final VersionHistory versionHistory = versionManager.getVersionHistory(node.getPath());
        final VersionIterator allVersions = versionHistory.getAllVersions();

        assertEquals(2, allVersions.getSize());

        final Version a = allVersions.nextVersion();
        assertEquals("b", a.getFrozenNode().getProperty("testProp").getString());

        final Version b = allVersions.nextVersion();
        assertEquals("a", b.getFrozenNode().getProperty("testProp").getString());
    }

    @Test
    public void getVersion() throws RepositoryException {
        final MockNode node = MockNode.root().addNode("test", "nt:base");
        node.addMixin("mix:versionable");
        node.setProperty("testProp", "a");
        final VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
        versionManager.checkin(node.getPath());

        final VersionHistory versionHistory = versionManager.getVersionHistory(node.getPath());

        final Version version = versionHistory.getVersion("1.0");

        assertEquals("a", version.getFrozenNode().getProperty("testProp").getString());
    }

    private static void assertNoParent(String message, Item item) throws RepositoryException {
        try {
            item.getParent();
            fail(message);
        } catch (ItemNotFoundException expected) {
            // everything is fine
        }
    }

    private static void assertVersionException(Code code) throws RepositoryException {
        try {
            code.execute();
            fail("Expected a VersionException to be thrown");
        } catch (VersionException expected) {
        }
    }

    private interface Code {
        public void execute() throws RepositoryException;
    }

}