blob: 40f5281492c7633255221e768025ad0ca77156d5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.oak.plugins.document;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Collections;
import java.util.Random;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
import org.apache.jackrabbit.oak.plugins.document.DocumentNodeState.Children;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.junit.Rule;
import org.junit.Test;
import com.google.common.collect.Lists;
/**
* A set of simple tests.
*/
public class SimpleTest {
@Rule
public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider();
@Rule
public MongoConnectionFactory connectionFactory = new MongoConnectionFactory();
private static final boolean MONGO_DB = false;
// private static final boolean MONGO_DB = true;
@Test
public void pathToId() {
assertEquals("0:/", Utils.getIdFromPath("/"));
assertEquals("/", Utils.getPathFromId("0:/"));
assertEquals("1:/test", Utils.getIdFromPath("/test"));
assertEquals("/test", Utils.getPathFromId("1:/test"));
assertEquals("10:/1/2/3/3/4/6/7/8/9/a", Utils.getIdFromPath("/1/2/3/3/4/6/7/8/9/a"));
assertEquals("/1/2/3/3/4/6/7/8/9/a", Utils.getPathFromId("10:/1/2/3/3/4/6/7/8/9/a"));
}
@Test
public void pathDepth() {
assertEquals(0, Utils.pathDepth(""));
assertEquals(0, Utils.pathDepth("/"));
assertEquals(1, Utils.pathDepth("1/"));
assertEquals(2, Utils.pathDepth("/a/"));
assertEquals(2, Utils.pathDepth("/a/b"));
assertEquals(3, Utils.pathDepth("/a/b/c"));
assertEquals(2, Utils.pathDepth("a/b/c"));
}
@Test
public void addNodeGetNode() {
DocumentMK mk = builderProvider.newBuilder().open();
DocumentStore s = mk.getDocumentStore();
DocumentNodeStore ns = mk.getNodeStore();
RevisionVector rev = RevisionVector.fromString(mk.getHeadRevision());
DocumentNodeState n = new DocumentNodeState(ns, Path.fromString("/test"), rev,
Collections.singleton(ns.createPropertyState("name", "\"Hello\"")), false, null);
UpdateOp op = n.asOperation(rev.getRevision(ns.getClusterId()));
// mark as commit root
NodeDocument.setRevision(op, rev.getRevision(ns.getClusterId()), "c");
assertTrue(s.create(Collection.NODES, Lists.newArrayList(op)));
DocumentNodeState n2 = ns.getNode(Path.fromString("/test"), rev);
assertNotNull(n2);
PropertyState p = n2.getProperty("name");
assertNotNull(p);
assertEquals("Hello", p.getValue(Type.STRING));
}
@Test
public void nodeIdentifier() {
DocumentMK mk = createMK(true);
String rev0 = mk.getHeadRevision();
String rev1 = mk.commit("/", "+\"test\":{}", null, null);
String rev2 = mk.commit("/test", "+\"a\":{}", null, null);
String rev3 = mk.commit("/test", "+\"b\":{}", null, null);
String rev4 = mk.commit("/test", "^\"a/x\":1", null, null);
String r0 = mk.getNodes("/", rev0, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/@r1-0-1\",\":childNodeCount\":0}", r0);
String r1 = mk.getNodes("/", rev1, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/@r2-0-1\",\"test\":{},\":childNodeCount\":1}", r1);
String r2 = mk.getNodes("/", rev2, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/@r3-0-1\",\"test\":{},\":childNodeCount\":1}", r2);
String r3;
r3 = mk.getNodes("/", rev3, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/@r4-0-1\",\"test\":{},\":childNodeCount\":1}", r3);
r3 = mk.getNodes("/test", rev3, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/test@r4-0-1\",\"a\":{},\"b\":{},\":childNodeCount\":2}", r3);
String r4;
r4 = mk.getNodes("/", rev4, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/@r5-0-1\",\"test\":{},\":childNodeCount\":1}", r4);
r4 = mk.getNodes("/test", rev4, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/test@r5-0-1\",\"a\":{},\"b\":{},\":childNodeCount\":2}", r4);
r4 = mk.getNodes("/test/a", rev4, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/test/a@r5-0-1\",\"x\":1,\":childNodeCount\":0}", r4);
r4 = mk.getNodes("/test/b", rev4, 0, 0, Integer.MAX_VALUE, ":id");
assertEquals("{\":id\":\"/test/b@r4-0-1\",\":childNodeCount\":0}", r4);
}
@Test
public void conflict() {
DocumentMK mk = createMK();
mk.commit("/", "+\"a\": {}", null, null);
try {
mk.commit("/", "+\"b\": {} +\"a\": {}", null, null);
fail();
} catch (DocumentStoreException e) {
// expected
}
// the previous commit should be rolled back now,
// so this should work
mk.commit("/", "+\"b\": {}", null, null);
}
@Test
public void diff() {
DocumentMK mk = createMK();
String rev0 = mk.getHeadRevision();
String rev1 = mk.commit("/", "+\"t1\":{}", null, null);
String rev2 = mk.commit("/", "+\"t2\":{}", null, null);
String rev3 = mk.commit("/", "+\"t3\":{}", null, null);
String rev4 = mk.commit("/", "^\"t3/x\":1", null, null);
String r0 = mk.getNodes("/", rev0, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\":childNodeCount\":0}", r0);
String r1 = mk.getNodes("/", rev1, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"t1\":{},\":childNodeCount\":1}", r1);
String r2 = mk.getNodes("/", rev2, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"t1\":{},\"t2\":{},\":childNodeCount\":2}", r2);
String r3 = mk.getNodes("/", rev3, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"t1\":{},\"t2\":{},\"t3\":{},\":childNodeCount\":3}", r3);
String diff01 = mk.diff(rev0, rev1, "/", 0).trim();
assertEquals("+\"/t1\":{}", diff01);
String diff12 = mk.diff(rev1, rev2, "/", 0).trim();
assertEquals("+\"/t2\":{}", diff12);
String diff23 = mk.diff(rev2, rev3, "/", 0).trim();
assertEquals("+\"/t3\":{}", diff23);
String diff13 = mk.diff(rev1, rev3, "/", 0).trim();
assertThat(diff13, anyOf(equalTo("+\"/t2\":{}+\"/t3\":{}"), equalTo("+\"/t3\":{}+\"/t2\":{}")));
String diff34 = mk.diff(rev3, rev4, "/", 0).trim();
assertEquals("^\"/t3\":{}", diff34);
}
@Test
public void reAddDeleted() {
DocumentMK mk = createMK();
String rev0 = mk.getHeadRevision();
String rev1 = mk.commit("/", "+\"test\":{\"name\": \"Hello\"} ^ \"x\": 1", null, null);
String rev2 = mk.commit("/", "-\"test\" ^ \"x\": 2", null, null);
String rev3 = mk.commit("/", "+\"test\":{\"name\": \"Hallo\"} ^ \"x\": 3", null, null);
String test0 = mk.getNodes("/test", rev0, 0, 0, Integer.MAX_VALUE, null);
assertNull(null, test0);
String test1 = mk.getNodes("/test", rev1, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"name\":\"Hello\",\":childNodeCount\":0}", test1);
String test2 = mk.getNodes("/test", rev2, 0, 0, Integer.MAX_VALUE, null);
assertNull(null, test2);
String test3 = mk.getNodes("/test", rev3, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"name\":\"Hallo\",\":childNodeCount\":0}", test3);
mk.dispose();
}
@Test
public void reAddDeleted2() {
DocumentMK mk = createMK();
String rev = mk.commit("/", "+\"test\":{\"x\":\"1\",\"child\": {}}", null, null);
rev = mk.commit("/", "-\"test\"", rev, null);
rev = mk.commit("/", "+\"test\":{} +\"test2\": {}", null, null);
String test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\":childNodeCount\":0}", test);
String test2 = mk.getNodes("/test2", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\":childNodeCount\":0}", test2);
}
@Test
public void move() {
DocumentMK mk = createMK();
String rev = mk.commit("/", "+\"test\":{\"x\":\"1\",\"child\": {}}", null, null);
rev = mk.commit("/", ">\"test\": \"/test2\"", rev, null);
String test = mk.getNodes("/test2", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"x\":\"1\",\"child\":{},\":childNodeCount\":1}", test);
test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
assertNull(test);
}
@Test
public void copy() {
DocumentMK mk = createMK();
String rev = mk.commit("/", "+\"test\":{\"x\":\"1\",\"child\": {}}", null, null);
rev = mk.commit("/", "*\"test\": \"/test2\"", rev, null);
String test = mk.getNodes("/test2", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"x\":\"1\",\"child\":{},\":childNodeCount\":1}", test);
test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"x\":\"1\",\"child\":{},\":childNodeCount\":1}", test);
}
@Test
public void escapePropertyName() {
DocumentMK mk = createMK();
String rev = mk.commit(
"/", "+\"test1\":{\"name.first\": \"Hello\"} +\"test2\":{\"_id\": \"a\"} +\"test3\":{\"$x\": \"1\"}", null, null);
String test1 = mk.getNodes("/test1", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"name.first\":\"Hello\",\":childNodeCount\":0}", test1);
String test2 = mk.getNodes("/test2", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"_id\":\"a\",\":childNodeCount\":0}", test2);
String test3 = mk.getNodes("/test3", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"$x\":\"1\",\":childNodeCount\":0}", test3);
mk.dispose();
}
@Test
public void commit() {
DocumentMK mk = createMK();
DocumentNodeStore ns = mk.getNodeStore();
String rev = mk.commit("/", "+\"test\":{\"name\": \"Hello\"}", null, null);
String test = mk.getNodes("/test", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"name\":\"Hello\",\":childNodeCount\":0}", test);
String r0 = mk.commit("/test", "+\"a\":{\"name\": \"World\"}", null, null);
String r1 = mk.commit("/test", "+\"b\":{\"name\": \"!\"}", null, null);
test = mk.getNodes("/test", r0, 0, 0, Integer.MAX_VALUE, null);
DocumentNodeState n = ns.getNode(Path.ROOT, RevisionVector.fromString(r0));
assertNotNull(n);
Children c = ns.getChildren(n, "", Integer.MAX_VALUE);
assertEquals("[test]", c.toString());
n = ns.getNode(Path.fromString("/test"), RevisionVector.fromString(r1));
assertNotNull(n);
c = ns.getChildren(n, "", Integer.MAX_VALUE);
assertEquals("[a, b]", c.toString());
rev = mk.commit("", "^\"/test\":1", null, null);
test = mk.getNodes("/", rev, 0, 0, Integer.MAX_VALUE, null);
assertEquals("{\"test\":1,\"test\":{},\":childNodeCount\":1}", test);
// System.out.println(test);
}
@Test
public void delete() {
DocumentMK mk = createMK();
DocumentNodeStore ns = mk.getNodeStore();
mk.commit("/", "+\"testDel\":{\"name\": \"Hello\"}", null, null);
mk.commit("/testDel", "+\"a\":{\"name\": \"World\"}", null, null);
mk.commit("/testDel", "+\"b\":{\"name\": \"!\"}", null, null);
String r1 = mk.commit("/testDel", "+\"c\":{\"name\": \"!\"}", null, null);
DocumentNodeState n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r1));
assertNotNull(n);
Children c = ns.getChildren(n, "", Integer.MAX_VALUE);
assertEquals(3, c.children.size());
String r2 = mk.commit("/testDel", "-\"c\"", null, null);
n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r2));
assertNotNull(n);
c = ns.getChildren(n, "", Integer.MAX_VALUE);
assertEquals(2, c.children.size());
String r3 = mk.commit("/", "-\"testDel\"", null, null);
n = ns.getNode(Path.fromString("/testDel"), RevisionVector.fromString(r3));
assertNull(n);
}
@Test
public void escapeUnescape() {
DocumentMK mk = createMK();
String rev;
String nodes;
Random r = new Random(1);
for (int i = 0; i < 20; i++) {
int len = 1 + r.nextInt(5);
StringBuilder buff = new StringBuilder();
for (int j = 0; j < len; j++) {
buff.append((char) (32 + r.nextInt(128)));
}
String s = buff.toString();
String x2 = Utils.escapePropertyName(s);
String s2 = Utils.unescapePropertyName(x2);
if (!s.equals(s2)) {
assertEquals(s, s2);
}
if (s.indexOf('/') >= 0) {
continue;
}
JsopBuilder jsop = new JsopBuilder();
jsop.tag('+').key(s).object().key(s).value("x").endObject();
rev = mk.commit("/", jsop.toString(),
null, null);
nodes = mk.getNodes("/" + s, rev, 0, 0, 100, null);
jsop = new JsopBuilder();
jsop.object().key(s).value("x").
key(":childNodeCount").value(0).endObject();
String n = jsop.toString();
assertEquals(n, nodes);
nodes = mk.getNodes("/", rev, 0, 0, 100, null);
jsop = new JsopBuilder();
jsop.object().key(s).object().endObject().
key(":childNodeCount").value(1).endObject();
n = jsop.toString();
assertEquals(n, nodes);
jsop = new JsopBuilder();
jsop.tag('-').value(s);
rev = mk.commit("/", jsop.toString(), rev, null);
}
}
@Test
public void nodeAndPropertyNames() {
DocumentMK mk = createMK();
String rev;
String nodes;
for (String s : new String[] { "_", "$", "__", "_id", "$x", ".", ".\\", "x\\", "\\x", "first.name" }) {
String x2 = Utils.escapePropertyName(s);
String s2 = Utils.unescapePropertyName(x2);
if (!s.equals(s2)) {
assertEquals(s, s2);
}
JsopBuilder jsop = new JsopBuilder();
jsop.tag('+').key(s).object().key(s).value("x").endObject();
rev = mk.commit("/", jsop.toString(),
null, null);
nodes = mk.getNodes("/" + s, rev, 0, 0, 10, null);
jsop = new JsopBuilder();
jsop.object().key(s).value("x").
key(":childNodeCount").value(0).endObject();
String n = jsop.toString();
assertEquals(n, nodes);
nodes = mk.getNodes("/", rev, 0, 0, 10, null);
jsop = new JsopBuilder();
jsop.object().key(s).object().endObject().
key(":childNodeCount").value(1).endObject();
n = jsop.toString();
assertEquals(n, nodes);
jsop = new JsopBuilder();
jsop.tag('-').value(s);
rev = mk.commit("/", jsop.toString(), rev, null);
}
}
@Test
public void addAndMove() {
DocumentMK mk = createMK();
String head = mk.getHeadRevision();
head = mk.commit("",
"+\"/root\":{}\n" +
"+\"/root/a\":{}\n"+
"+\"/root/a/b\":{}\n",
head, "");
head = mk.commit("",
">\"/root/a\":\"/root/c\"\n",
head, "");
assertFalse(mk.nodeExists("/root/a", head));
assertTrue(mk.nodeExists("/root/c/b", head));
}
@Test
public void commitRoot() {
DocumentMK mk = createMK();
DocumentStore store = mk.getDocumentStore();
Revision head = Revision.fromString(mk.getHeadRevision());
head = Revision.fromString(mk.commit("", "+\"/test\":{\"foo\":{}}", head.toString(), null));
// root node must not have the revision
NodeDocument rootDoc = store.find(Collection.NODES, "0:/");
//As we update the childStatus flag the commit root would shift
//one layer above
// assertNotNull(rootDoc);
// assertFalse(rootDoc.containsRevision(head));
// test node must have head in revisions
NodeDocument node = store.find(Collection.NODES, "1:/test");
//assertNotNull(node);
//assertTrue(node.containsRevision(head));
// foo must not have head in revisions and must refer to test
// as commit root (depth = 1)
NodeDocument foo = store.find(Collection.NODES, "2:/test/foo");
assertNotNull(foo);
assertFalse(foo.containsRevision(head));
assertEquals(Path.ROOT, foo.getCommitRootPath(head));
head = Revision.fromString(mk.commit("", "+\"/bar\":{}+\"/test/foo/bar\":{}", head.toString(), null));
// root node is root of commit
rootDoc = store.find(Collection.NODES, "0:/");
assertNotNull(rootDoc);
assertTrue(rootDoc.containsRevision(head));
// /bar refers to root nodes a commit root
NodeDocument bar = store.find(Collection.NODES, "1:/bar");
assertNotNull(bar);
assertEquals(Path.ROOT, bar.getCommitRootPath(head));
// /test/foo/bar refers to root nodes a commit root
bar = store.find(Collection.NODES, "3:/test/foo/bar");
assertNotNull(bar);
assertEquals(Path.ROOT, bar.getCommitRootPath(head));
}
private DocumentMK createMK() {
return createMK(false);
}
private DocumentMK createMK(boolean useSimpleRevision) {
DocumentMK.Builder builder = builderProvider.newBuilder();
if (MONGO_DB) {
MongoConnection c = connectionFactory.getConnection();
MongoUtils.dropCollections(c.getDBName());
builder.setMongoDB(c.getMongoClient(), c.getDBName());
}
builder.setUseSimpleRevision(useSimpleRevision);
return builder.open();
}
}