You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2015/06/11 14:09:17 UTC

svn commit: r1684861 [5/8] - in /jackrabbit/oak/trunk: ./ oak-remote/ oak-remote/src/ oak-remote/src/main/ oak-remote/src/main/java/ oak-remote/src/main/java/org/ oak-remote/src/main/java/org/apache/ oak-remote/src/main/java/org/apache/jackrabbit/ oak-...

Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcher.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcher.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcher.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcher.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,35 @@
+/*
+ * 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.remote.http.matcher;
+
+import javax.servlet.http.HttpServletRequest;
+
+class MethodMatcher implements Matcher {
+
+    private final String method;
+
+    public MethodMatcher(String method) {
+        this.method = method;
+    }
+
+    @Override
+    public boolean match(HttpServletRequest request) {
+        return request.getMethod().toLowerCase().equals(method.toLowerCase());
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcher.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcher.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcher.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcher.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,42 @@
+/*
+ * 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.remote.http.matcher;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.regex.Pattern;
+
+class PathMatcher implements Matcher {
+
+    private final Pattern pattern;
+
+    public PathMatcher(Pattern pattern) {
+        this.pattern = pattern;
+    }
+
+    @Override
+    public boolean match(HttpServletRequest request) {
+        String requestPath = request.getPathInfo();
+
+        if (requestPath == null) {
+            return false;
+        }
+
+        return pattern.matcher(requestPath).matches();
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/osgi/RemoteServletRegistration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/osgi/RemoteServletRegistration.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/osgi/RemoteServletRegistration.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/osgi/RemoteServletRegistration.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,92 @@
+/*
+ * 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.remote.osgi;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.remote.RemoteRepository;
+import org.apache.jackrabbit.oak.remote.content.ContentRemoteRepository;
+import org.apache.jackrabbit.oak.remote.http.RemoteServlet;
+import org.osgi.service.http.HttpService;
+
+import java.util.Map;
+
+import static org.apache.felix.scr.annotations.ConfigurationPolicy.REQUIRE;
+
+@Component(
+        metatype = true,
+        policy = REQUIRE,
+        label = "Apache Jackrabbit Oak Remote HTTP API",
+        description = "The HTTP binding of the Remote API for a Jackrabbit Oak repository"
+)
+@Properties({
+        @Property(
+                name = "url",
+                value = {"/api"},
+                label = "Mount URL",
+                description = "Where the root application is exposed in the URL namespace"
+        )
+})
+public class RemoteServletRegistration {
+
+    @Reference
+    private HttpService httpService;
+
+    @Reference
+    private ContentRepository contentRepository;
+
+    @Activate
+    public void activate(Map properties) {
+        registerServlet(getUrl(properties));
+    }
+
+    @Deactivate
+    public void deactivate(Map properties) {
+        unregisterServlet(getUrl(properties));
+    }
+
+    private void registerServlet(String url) {
+        try {
+            httpService.registerServlet(url, getRemoteServlet(), null, null);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void unregisterServlet(String url) {
+        httpService.unregister(url);
+    }
+
+    private String getUrl(Map properties) {
+        return (String) properties.get("url");
+    }
+
+    private RemoteServlet getRemoteServlet() {
+        return new RemoteServlet(getRemoteRepository());
+    }
+
+    private RemoteRepository getRemoteRepository() {
+        return new ContentRemoteRepository(contentRepository);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperationTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperationTest.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperationTest.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperationTest.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,79 @@
+/*
+ * 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.remote.content;
+
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.remote.RemoteCommitException;
+import org.junit.Test;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class AddContentRemoteOperationTest {
+
+    public AddContentRemoteOperation createOperation(String path) {
+        return new AddContentRemoteOperation(path);
+    }
+
+    @Test
+    public void testAddNode() throws Exception {
+        Tree parent = mock(Tree.class);
+        doReturn(true).when(parent).exists();
+
+        Tree tree = mock(Tree.class);
+        doReturn(false).when(tree).exists();
+        doReturn(parent).when(tree).getParent();
+        doReturn("test").when(tree).getName();
+
+        Root root = mock(Root.class);
+        doReturn(tree).when(root).getTree("/test");
+
+        createOperation("/test").apply(root);
+
+        verify(parent).addChild("test");
+    }
+
+    @Test(expected = RemoteCommitException.class)
+    public void testAddNodeWithExistingTree() throws Exception {
+        Tree tree = mock(Tree.class);
+        doReturn(true).when(tree).exists();
+
+        Root root = mock(Root.class);
+        doReturn(tree).when(root).getTree("/test");
+
+        createOperation("/test").apply(root);
+    }
+
+    @Test(expected = RemoteCommitException.class)
+    public void testAddNodeWithNonExistingParent() throws Exception {
+        Tree parent = mock(Tree.class);
+        doReturn(false).when(parent).exists();
+
+        Tree tree = mock(Tree.class);
+        doReturn(false).when(tree).exists();
+        doReturn(parent).when(tree).getParent();
+
+        Root root = mock(Root.class);
+        doReturn(tree).when(root).getTree("/test");
+
+        createOperation("/test").apply(root);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepositoryTest.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepositoryTest.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepositoryTest.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,86 @@
+/*
+ * 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.remote.content;
+
+import com.google.common.collect.Sets;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.remote.RemoteCredentials;
+import org.apache.jackrabbit.oak.remote.RemoteLoginException;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.jcr.Credentials;
+import javax.security.auth.login.LoginException;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ContentRemoteRepositoryTest {
+
+    private ContentRemoteRepository createRepository() {
+        return createRepository(mock(ContentRepository.class));
+    }
+
+    private ContentRemoteRepository createRepository(ContentRepository repository) {
+        return new ContentRemoteRepository(repository);
+    }
+
+    @Test
+    public void testCreateBasicCredentials() {
+        assertNotNull(createRepository().createBasicCredentials("admin", "admin".toCharArray()));
+    }
+
+    @Test
+    @Ignore
+    public void testCreateImpersonationCredentials() {
+        assertNotNull(createRepository().createImpersonationCredentials(Sets.newHashSet("admin")));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testLoginWithNullCredentials() throws Exception {
+        createRepository().login(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testLoginWithInvalidCredentials() throws Exception {
+        createRepository().login(mock(RemoteCredentials.class));
+    }
+
+    @Test
+    public void testSuccessfulLoginWithBasicCredentials() throws Exception {
+        ContentRepository repository = mock(ContentRepository.class);
+        when(repository.login(any(Credentials.class), anyString())).thenReturn(mock(ContentSession.class));
+
+        ContentRemoteRepository remoteRepository = createRepository(repository);
+        assertNotNull(remoteRepository.login(remoteRepository.createBasicCredentials("admin", "admin".toCharArray())));
+    }
+
+    @Test(expected = RemoteLoginException.class)
+    public void testUnsuccessfulLoginWithBasicCredentials() throws Exception {
+        ContentRepository repository = mock(ContentRepository.class);
+        when(repository.login(any(Credentials.class), anyString())).thenThrow(LoginException.class);
+
+        ContentRemoteRepository remoteRepository = createRepository(repository);
+        remoteRepository.login(remoteRepository.createBasicCredentials("admin", "admin".toCharArray()));
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultTest.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultTest.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultTest.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,449 @@
+/*
+ * 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.remote.content;
+
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.remote.RemoteValue;
+import org.apache.jackrabbit.util.ISO8601;
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+
+public class ContentRemoteResultTest {
+
+    private ContentRemoteResult createResult(ResultRow row) {
+        return new ContentRemoteResult(mock(ContentRemoteBinaries.class), row);
+    }
+
+    private ContentRemoteResult createResult(ContentRemoteBinaries binaries, ResultRow row) {
+        return new ContentRemoteResult(binaries, row);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testColumnNotAvailable() {
+        ResultRow row = mock(ResultRow.class);
+        doThrow(IllegalArgumentException.class).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        result.getColumnValue("column");
+    }
+
+    @Test
+    public void testStringColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.STRING).when(value).getType();
+        doReturn("value").when(value).getValue(Type.STRING);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asText());
+    }
+
+    @Test
+    public void testBinaryColumn() {
+        Blob blob = mock(Blob.class);
+
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.BINARY).when(value).getType();
+        doReturn(blob).when(value).getValue(Type.BINARY);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteBinaries binaries = mock(ContentRemoteBinaries.class);
+        doReturn("id").when(binaries).put(blob);
+
+        ContentRemoteResult result = createResult(binaries, row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("id", remoteValue.asBinaryId());
+    }
+
+    @Test
+    public void testLongColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.LONG).when(value).getType();
+        doReturn(42L).when(value).getValue(Type.LONG);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(42L, remoteValue.asLong().longValue());
+    }
+
+    @Test
+    public void testDoubleColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DOUBLE).when(value).getType();
+        doReturn(4.2).when(value).getValue(Type.DOUBLE);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(4.2, remoteValue.asDouble().doubleValue(), 1e-5);
+    }
+
+    @Test
+    public void testDateColumn() {
+        Date now = new Date();
+
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DATE).when(value).getType();
+        doReturn(toFormattedDate(now)).when(value).getValue(Type.DATE);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(now.getTime(), remoteValue.asDate().longValue());
+    }
+
+    @Test
+    public void testBooleanColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.BOOLEAN).when(value).getType();
+        doReturn(true).when(value).getValue(Type.BOOLEAN);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(true, remoteValue.asBoolean());
+    }
+
+    @Test
+    public void testNameColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.NAME).when(value).getType();
+        doReturn("value").when(value).getValue(Type.NAME);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asName());
+    }
+
+    @Test
+    public void testPathColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.PATH).when(value).getType();
+        doReturn("value").when(value).getValue(Type.PATH);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asPath());
+    }
+
+    @Test
+    public void testReferenceColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.REFERENCE).when(value).getType();
+        doReturn("value").when(value).getValue(Type.REFERENCE);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asReference());
+    }
+
+    @Test
+    public void testWeakReferenceColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.WEAKREFERENCE).when(value).getType();
+        doReturn("value").when(value).getValue(Type.WEAKREFERENCE);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asWeakReference());
+    }
+
+    @Test
+    public void testUriColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.URI).when(value).getType();
+        doReturn("value").when(value).getValue(Type.URI);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals("value", remoteValue.asUri());
+    }
+
+    @Test
+    public void testDecimalColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DECIMAL).when(value).getType();
+        doReturn(BigDecimal.ONE).when(value).getValue(Type.DECIMAL);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(BigDecimal.ONE, remoteValue.asDecimal());
+    }
+
+    @Test
+    public void testMultiStringColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.STRINGS).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.STRINGS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiText());
+    }
+
+    @Test
+    public void testMultiBinaryColumn() {
+        Blob first = mock(Blob.class);
+        Blob second = mock(Blob.class);
+
+        ContentRemoteBinaries binaries = mock(ContentRemoteBinaries.class);
+        doReturn("first").when(binaries).put(first);
+        doReturn("second").when(binaries).put(second);
+
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.BINARIES).when(value).getType();
+        doReturn(asList(first, second)).when(value).getValue(Type.BINARIES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(binaries, row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("first", "second"), remoteValue.asMultiBinaryId());
+    }
+
+    @Test
+    public void testMultiLongColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.LONGS).when(value).getType();
+        doReturn(asList(4L, 2L)).when(value).getValue(Type.LONGS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList(4L, 2L), remoteValue.asMultiLong());
+    }
+
+    @Test
+    public void testMultiDoubleColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DOUBLES).when(value).getType();
+        doReturn(asList(4.0, 2.0)).when(value).getValue(Type.DOUBLES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList(4.0, 2.0), remoteValue.asMultiDouble());
+    }
+
+    @Test
+    public void testMultiDateColumn() {
+        Date first = new Date(4);
+        Date second = new Date(2);
+
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DATES).when(value).getType();
+        doReturn(toFormattedDates(first, second)).when(value).getValue(Type.DATES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList(4L, 2L), remoteValue.asMultiDate());
+    }
+
+    @Test
+    public void testMultiBooleanColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.BOOLEANS).when(value).getType();
+        doReturn(asList(true, false)).when(value).getValue(Type.BOOLEANS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList(true, false), remoteValue.asMultiBoolean());
+    }
+
+    @Test
+    public void testMultiNameColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.NAMES).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.NAMES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiName());
+    }
+
+    @Test
+    public void testMultiPathColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.PATHS).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.PATHS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiPath());
+    }
+
+    @Test
+    public void testMultiReferenceColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.REFERENCES).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.REFERENCES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiReference());
+    }
+
+    @Test
+    public void testMultiWeakReferenceColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.WEAKREFERENCES).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.WEAKREFERENCES);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiWeakReference());
+    }
+
+    @Test
+    public void testMultiUriColumn() {
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.URIS).when(value).getType();
+        doReturn(asList("a", "b")).when(value).getValue(Type.URIS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList("a", "b"), remoteValue.asMultiUri());
+    }
+
+    @Test
+    public void testMultiDecimalColumn() {
+        BigDecimal first = new BigDecimal(4);
+        BigDecimal second = new BigDecimal(2);
+
+        PropertyValue value = mock(PropertyValue.class);
+        doReturn(Type.DECIMALS).when(value).getType();
+        doReturn(asList(first, second)).when(value).getValue(Type.DECIMALS);
+
+        ResultRow row = mock(ResultRow.class);
+        doReturn(value).when(row).getValue("column");
+
+        ContentRemoteResult result = createResult(row);
+        RemoteValue remoteValue = result.getColumnValue("column");
+        assertEquals(asList(first, second), remoteValue.asMultiDecimal());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSelectorNotAvailable() {
+        ResultRow row = mock(ResultRow.class);
+        doThrow(IllegalArgumentException.class).when(row).getPath("selector");
+        createResult(row).getSelectorPath("selector");
+    }
+
+    @Test
+    public void testSelector() {
+        ResultRow row = mock(ResultRow.class);
+        doReturn("path").when(row).getPath("selector");
+        assertEquals("path", createResult(row).getSelectorPath("selector"));
+    }
+
+    private Iterable<String> toFormattedDates(Date... dates) {
+        List<String> result = newArrayList();
+
+        for (Date date : dates) {
+            result.add(toFormattedDate(date));
+        }
+
+        return result;
+    }
+
+    private String toFormattedDate(Date date) {
+        return ISO8601.format(toCalendar(date));
+    }
+
+    private Calendar toCalendar(Date date) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeInMillis(date.getTime());
+        return calendar;
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultsTest.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultsTest.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResultsTest.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,67 @@
+/*
+ * 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.remote.content;
+
+import org.apache.jackrabbit.oak.api.Result;
+import org.junit.Test;
+
+import static com.google.common.collect.Iterables.elementsEqual;
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class ContentRemoteResultsTest {
+
+    private ContentRemoteResults createContentRemoteResults(Result result) {
+        return new ContentRemoteResults(createContentRemoteBinaries(), result);
+    }
+
+    private ContentRemoteBinaries createContentRemoteBinaries() {
+        return mock(ContentRemoteBinaries.class);
+    }
+
+    @Test
+    public void testGetTotal() {
+        Result result = mock(Result.class);
+        doReturn(42L).when(result).getSize();
+        assertEquals(42L, createContentRemoteResults(result).getTotal());
+    }
+
+    @Test
+    public void testGetColumns() {
+        String[] columns = {"a", "b"};
+
+        Result result = mock(Result.class);
+        doReturn(columns).when(result).getColumnNames();
+
+        assertTrue(elementsEqual(asList(columns), createContentRemoteResults(result).getColumns()));
+    }
+
+    @Test
+    public void testGetSelectors() {
+        String[] selectors = {"a", "b"};
+
+        Result result = mock(Result.class);
+        doReturn(selectors).when(result).getSelectorNames();
+
+        assertTrue(elementsEqual(asList(selectors), createContentRemoteResults(result).getSelectors()));
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteSessionTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteSessionTest.java?rev=1684861&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteSessionTest.java (added)
+++ jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteSessionTest.java Thu Jun 11 12:09:15 2015
@@ -0,0 +1,542 @@
+/*
+ * 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.remote.content;
+
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.remote.RemoteBinaryFilters;
+import org.apache.jackrabbit.oak.remote.RemoteBinaryId;
+import org.apache.jackrabbit.oak.remote.RemoteCommitException;
+import org.apache.jackrabbit.oak.remote.RemoteOperation;
+import org.apache.jackrabbit.oak.remote.RemoteRevision;
+import org.apache.jackrabbit.oak.remote.RemoteTreeFilters;
+import org.apache.jackrabbit.oak.remote.RemoteValue;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ContentRemoteSessionTest {
+
+    private ContentRemoteSession createSession() {
+        return createSession(mock(ContentSession.class));
+    }
+
+    private ContentRemoteSession createSession(ContentRemoteBinaries binaries) {
+        return new ContentRemoteSession(mock(ContentSession.class), mock(ContentRemoteRevisions.class), binaries);
+    }
+
+    private ContentRemoteSession createSession(ContentSession session, ContentRemoteBinaries binaries) {
+        return new ContentRemoteSession(session, mock(ContentRemoteRevisions.class), binaries);
+    }
+
+    private ContentRemoteSession createSession(ContentSession session, ContentRemoteRevisions revisions) {
+        return new ContentRemoteSession(session, revisions, mock(ContentRemoteBinaries.class));
+    }
+
+    private ContentRemoteSession createSession(ContentSession session) {
+        return new ContentRemoteSession(session, mock(ContentRemoteRevisions.class), mock(ContentRemoteBinaries.class));
+    }
+
+    @Test
+    public void testReadLastRevision() {
+        assertNotNull(createSession().readLastRevision());
+    }
+
+    @Test
+    public void testReadLastRevisionAsString() {
+        Root root = mock(Root.class);
+
+        AuthInfo authInfo = mock(AuthInfo.class);
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(authInfo).when(session).getAuthInfo();
+        doReturn(root).when(session).getLatestRoot();
+
+        ContentRemoteRevisions revisions = mock(ContentRemoteRevisions.class);
+        doReturn("id").when(revisions).put(authInfo, root);
+
+        assertEquals("id", createSession(session, revisions).readLastRevision().asString());
+    }
+
+    @Test
+    public void testReadRevision() {
+        Root root = mock(Root.class);
+
+        AuthInfo authInfo = mock(AuthInfo.class);
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(authInfo).when(session).getAuthInfo();
+
+        ContentRemoteRevisions revisions = mock(ContentRemoteRevisions.class);
+        doReturn(root).when(revisions).get(authInfo, "id");
+
+        assertNotNull(createSession(session, revisions).readRevision("id"));
+    }
+
+    @Test
+    public void testReadRevisionAsString() {
+        Root root = mock(Root.class);
+
+        AuthInfo authInfo = mock(AuthInfo.class);
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(authInfo).when(session).getAuthInfo();
+
+        ContentRemoteRevisions revisions = mock(ContentRemoteRevisions.class);
+        doReturn(root).when(revisions).get(authInfo, "id");
+
+        assertEquals("id", createSession(session, revisions).readRevision("id").asString());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadTreeWithNullRevision() throws Exception {
+        createSession().readTree(null, "/", new RemoteTreeFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadTreeWithInvalidRevision() throws Exception {
+        createSession().readTree(mock(RemoteRevision.class), "/", new RemoteTreeFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadTreeWithNullPath() throws Exception {
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        createSession().readTree(revision, null, new RemoteTreeFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadTreeWithInvalidPath() throws Exception {
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        createSession().readTree(revision, "invalid", new RemoteTreeFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadTreeWithNullFilters() throws Exception {
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        createSession().readTree(revision, "/", null);
+    }
+
+    @Test
+    public void testReadNonExistingTree() throws Exception {
+        Tree tree = mock(Tree.class);
+        when(tree.exists()).thenReturn(false);
+
+        Root root = mock(Root.class);
+        when(root.getTree(anyString())).thenReturn(tree);
+
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        doReturn(root).when(revision).getRoot();
+
+        assertNull(createSession().readTree(revision, "/", new RemoteTreeFilters()));
+    }
+
+    @Test
+    public void testReadExistingTree() throws Exception {
+        Tree tree = mock(Tree.class);
+        when(tree.exists()).thenReturn(true);
+
+        Root root = mock(Root.class);
+        when(root.getTree(anyString())).thenReturn(tree);
+
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        doReturn(root).when(revision).getRoot();
+
+        assertNotNull(createSession().readTree(revision, "/", new RemoteTreeFilters()));
+    }
+
+    @Test
+    public void testCreateAddOperation() {
+        assertNotNull(createSession().createAddOperation("/test", new HashMap<String, RemoteValue>()));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateAddOperationWithNullPath() {
+        createSession().createAddOperation(null, new HashMap<String, RemoteValue>());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateAddOperationWithInvalidPath() {
+        createSession().createAddOperation("invalid", new HashMap<String, RemoteValue>());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateAddOperationWithRootPath() {
+        createSession().createAddOperation("/", new HashMap<String, RemoteValue>());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createAddOperationWithNullProperties() {
+        createSession().createAddOperation("/test", null);
+    }
+
+    @Test
+    public void testCreateRemoveOperation() {
+        assertNotNull(createSession().createRemoveOperation("/test"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRemoveOperationWithNullPath() {
+        createSession().createRemoveOperation(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRemoveOperationWithInvalidPath() {
+        createSession().createRemoveOperation("invalid");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRemoveOperationWithRootPath() {
+        createSession().createRemoveOperation("/");
+    }
+
+    @Test
+    public void testCreateSetOperation() {
+        assertNotNull(createSession().createSetOperation("/test", "name", RemoteValue.toText("value")));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSetOperationWithNullPath() {
+        createSession().createSetOperation(null, "name", RemoteValue.toText("value"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSetOperationWithInvalidPath() {
+        createSession().createSetOperation("invalid", "name", RemoteValue.toText("value"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSetOperationWithNullName() {
+        createSession().createSetOperation("/test", null, RemoteValue.toText("value"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSetOperationWithEmptyName() {
+        createSession().createSetOperation("/test", "", RemoteValue.toText("value"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSetOperationWithNullValue() {
+        createSession().createSetOperation("/test", "name", null);
+    }
+
+    @Test
+    public void testCreateUnsetOperation() {
+        assertNotNull(createSession().createUnsetOperation("/test", "name"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateUnsetOperationWithNullPath() {
+        createSession().createUnsetOperation(null, "name");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateUnsetOperationWithInvalidPath() {
+        createSession().createUnsetOperation("invalid", "name");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateUnsetOperationWithNullName() {
+        createSession().createUnsetOperation("/test", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateUnsetOperationWithEmptyName() {
+        createSession().createUnsetOperation("/test", "");
+    }
+
+    @Test
+    public void createCopyOperation() {
+        assertNotNull(createSession().createCopyOperation("/source", "/target"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithNullSourcePath() {
+        createSession().createCopyOperation(null, "/target");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithInvalidSourcePath() {
+        createSession().createCopyOperation("invalid", "/target");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithNullTargetPath() {
+        createSession().createCopyOperation("/source", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithInvalidTargetPath() {
+        createSession().createCopyOperation("/source", "invalid");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithSameSourceAndTargetPath() {
+        createSession().createCopyOperation("/same", "/same");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createCopyOperationWithSourceAncestorOfTarget() {
+        createSession().createCopyOperation("/source", "/source/target");
+    }
+
+    @Test
+    public void createMoveOperation() {
+        assertNotNull(createSession().createMoveOperation("/source", "/target"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithNullSourcePath() {
+        createSession().createMoveOperation(null, "/target");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithInvalidSourcePath() {
+        createSession().createMoveOperation("invalid", "/target");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithNullTargetPath() {
+        createSession().createMoveOperation("/source", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithInvalidTargetPath() {
+        createSession().createMoveOperation("/source", "invalid");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithSameSourceAndTargetPath() {
+        createSession().createMoveOperation("/same", "/same");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createMoveOperationWithSourceAncestorOfTarget() {
+        createSession().createMoveOperation("/source", "/source/target");
+    }
+
+    @Test
+    public void createAggregateOperation() {
+        assertNotNull(createSession().createAggregateOperation(new ArrayList<RemoteOperation>()));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createAggregateOperationWithNullList() {
+        createSession().createAggregateOperation(null);
+    }
+
+    @Test
+    public void testCommit() throws Exception {
+        Root root = mock(Root.class);
+
+        ContentRemoteOperation operation = mock(ContentRemoteOperation.class);
+
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        doReturn(root).when(revision).getRoot();
+
+        assertNotNull(createSession().commit(revision, operation));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCommitWithNullRevision() throws Exception {
+        createSession().commit(null, mock(ContentRemoteOperation.class));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCommitWithInvalidRevision() throws Exception {
+        createSession().commit(mock(RemoteRevision.class), mock(ContentRemoteOperation.class));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCommitWithNullOperation() throws Exception {
+        createSession().commit(mock(ContentRemoteRevision.class), null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCommitWithInvalidOperation() throws Exception {
+        createSession().commit(mock(ContentRemoteRevision.class), mock(RemoteOperation.class));
+    }
+
+    @Test(expected = RemoteCommitException.class)
+    public void testCommitWithOperationThrowingException() throws Exception {
+        Root root = mock(Root.class);
+
+        ContentRemoteOperation operation = mock(ContentRemoteOperation.class);
+        doThrow(RemoteCommitException.class).when(operation).apply(root);
+
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        doReturn(root).when(revision).getRoot();
+
+        createSession().commit(revision, operation);
+    }
+
+    @Test(expected = RemoteCommitException.class)
+    public void testCommitWithConflictingCommit() throws Exception {
+        Root root = mock(Root.class);
+        doThrow(CommitFailedException.class).when(root).commit();
+
+        ContentRemoteRevision revision = mock(ContentRemoteRevision.class);
+        doReturn(root).when(revision).getRoot();
+
+        createSession().commit(revision, mock(ContentRemoteOperation.class));
+    }
+
+    @Test
+    public void testReadBinaryId() {
+        Blob blob = mock(Blob.class);
+
+        ContentRemoteBinaries binaries = mock(ContentRemoteBinaries.class);
+        doReturn(blob).when(binaries).get("id");
+
+        ContentRemoteSession remoteSession = createSession(binaries);
+        assertNotNull(remoteSession.readBinaryId("id"));
+    }
+
+    @Test
+    public void testReadBinaryIdAsString() {
+        Blob blob = mock(Blob.class);
+
+        ContentRemoteBinaries binaries = mock(ContentRemoteBinaries.class);
+        doReturn(blob).when(binaries).get("id");
+
+        ContentRemoteSession remoteSession = createSession(binaries);
+        assertEquals("id", remoteSession.readBinaryId("id").asString());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryIdWithNullReference() {
+        createSession().readBinaryId(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryIdWithEmptyReference() {
+        createSession().readBinaryId("");
+    }
+
+    @Test
+    public void testReadBinaryIdWithInvalidReference() {
+        Root root = mock(Root.class);
+        doReturn(null).when(root).getBlob(anyString());
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(root).when(session).getLatestRoot();
+
+        ContentRemoteSession remoteSession = createSession(session);
+        assertNull(remoteSession.readBinaryId("id"));
+    }
+
+    @Test
+    public void testReadBinary() {
+        Blob blob = mock(Blob.class);
+
+        ContentRemoteBinaryId binaryId = mock(ContentRemoteBinaryId.class);
+        doReturn(blob).when(binaryId).asBlob();
+
+        assertNotNull(createSession().readBinary(binaryId, new RemoteBinaryFilters()));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryWithNullId() {
+        createSession().readBinary(null, new RemoteBinaryFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryWithInvalidId() {
+        createSession().readBinary(mock(RemoteBinaryId.class), new RemoteBinaryFilters());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryWithNullFilters() {
+        createSession().readBinary(mock(ContentRemoteBinaryId.class), null);
+    }
+
+    @Test
+    public void testWriteBinary() throws Exception {
+        Blob blob = mock(Blob.class);
+
+        InputStream stream = mock(InputStream.class);
+
+        Root root = mock(Root.class);
+        doReturn(blob).when(root).createBlob(stream);
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(root).when(session).getLatestRoot();
+
+        ContentRemoteBinaries binaries = mock(ContentRemoteBinaries.class);
+        doReturn("id").when(binaries).put(blob);
+
+        ContentRemoteSession remoteSession = createSession(session, binaries);
+        assertEquals("id", remoteSession.writeBinary(stream).asString());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testWriteBinaryWithNullStream() throws Exception {
+        createSession().writeBinary(null);
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testWriteBinaryFailure() throws Exception {
+        InputStream stream = mock(InputStream.class);
+
+        Root root = mock(Root.class);
+        doThrow(IOException.class).when(root).createBlob(stream);
+
+        ContentSession session = mock(ContentSession.class);
+        doReturn(root).when(session).getLatestRoot();
+
+        ContentRemoteSession remoteSession = createSession(session);
+        remoteSession.writeBinary(stream);
+    }
+
+    @Test
+    public void testReadBinaryLength() throws Exception {
+        Blob blob = mock(Blob.class);
+        doReturn(42L).when(blob).length();
+
+        ContentRemoteBinaryId binaryId = mock(ContentRemoteBinaryId.class);
+        doReturn(blob).when(binaryId).asBlob();
+
+        assertEquals(42L, createSession().readBinaryLength(binaryId));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryLengthWithNullBinaryId() throws Exception {
+        createSession().readBinaryLength(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testReadBinaryLengthWithInvalidBinaryId() throws Exception {
+        createSession().readBinaryLength(mock(RemoteBinaryId.class));
+    }
+
+}