Java tutorial
/* * Copyright (c) 2016 Couchbase, Inc. * * 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 com.couchbase.client.java; import static com.googlecode.catchexception.CatchException.caughtException; import static com.googlecode.catchexception.CatchException.verifyException; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; 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.internal.matchers.ThrowableCauseMatcher.hasCause; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import com.couchbase.client.core.logging.CouchbaseLogger; import com.couchbase.client.core.logging.CouchbaseLoggerFactory; import com.couchbase.client.core.message.ResponseStatus; import com.couchbase.client.core.message.kv.subdoc.multi.Lookup; import com.couchbase.client.core.message.kv.subdoc.multi.Mutation; import com.couchbase.client.deps.com.fasterxml.jackson.annotation.JsonIgnore; import com.couchbase.client.deps.com.fasterxml.jackson.databind.ObjectMapper; import com.couchbase.client.java.bucket.BucketType; import com.couchbase.client.java.document.JsonArrayDocument; import com.couchbase.client.java.document.JsonDocument; import com.couchbase.client.java.document.json.JsonArray; import com.couchbase.client.java.document.json.JsonObject; import com.couchbase.client.java.error.CASMismatchException; import com.couchbase.client.java.error.DocumentDoesNotExistException; import com.couchbase.client.java.error.DurabilityException; import com.couchbase.client.java.error.subdoc.BadDeltaException; import com.couchbase.client.java.error.subdoc.CannotInsertValueException; import com.couchbase.client.java.error.subdoc.MultiMutationException; import com.couchbase.client.java.error.subdoc.PathExistsException; import com.couchbase.client.java.error.subdoc.PathInvalidException; import com.couchbase.client.java.error.subdoc.PathMismatchException; import com.couchbase.client.java.error.subdoc.PathNotFoundException; import com.couchbase.client.java.error.subdoc.SubDocumentException; import com.couchbase.client.java.subdoc.DocumentFragment; import com.couchbase.client.java.subdoc.MutateInBuilder; import com.couchbase.client.java.subdoc.SubdocOptionsBuilder; import com.couchbase.client.java.util.CouchbaseTestContext; import com.couchbase.client.java.util.features.CouchbaseFeature; import org.assertj.core.api.Assertions; import org.junit.AfterClass; import org.junit.Assume; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; /** * Integration tests for the sub-document API in {@link Bucket}. */ public class SubDocumentTest { private static CouchbaseTestContext ctx; private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(SubDocumentTest.class); private String key = "SubdocAPI"; private JsonObject testJson; @BeforeClass public static void connect() throws Exception { ctx = CouchbaseTestContext.builder().bucketQuota(100).bucketReplicas(1).bucketType(BucketType.COUCHBASE) .build(); ctx.ignoreIfMissing(CouchbaseFeature.SUBDOC); } @Before public void initData() { testJson = JsonObject.create().put("sub", JsonObject.create().put("value", "original")).put("boolean", true) .put("string", "someString").put("int", 123).put("array", JsonArray.from("1", 2, true)); ctx.bucket().upsert(JsonDocument.create(key, testJson)); } @AfterClass public static void disconnect() throws InterruptedException { ctx.destroyBucketAndDisconnect(); ctx.disconnect(); } @Test public void testFragmentCanBeAnEntity() { ctx.bucket().mutateIn(key) .upsert("user", new KeyValueTest.User("frank"), new SubdocOptionsBuilder().createParents(false)) .remove("sub").remove("array").remove("string").remove("boolean").execute(); JsonObject expected = JsonObject.create().put("user", JsonObject.create().put("firstname", "frank")) .put("int", 123); assertEquals(expected, ctx.bucket().get(key).content()); } private static class JacksonEntity { private String name; private int value; @JsonIgnore private String ignore; private JacksonEntity() { this.ignore = "zzz"; } public JacksonEntity(String name, int value) { this.name = name; this.value = value; this.ignore = "zzz"; } public String getName() { return name; } public int getValue() { return value; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } JacksonEntity that = (JacksonEntity) o; if (value != that.value) { return false; } return name != null ? name.equals(that.name) : that.name == null; } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + value; return result; } } @Test public void testRawGetWithMulti() throws IOException { JacksonEntity entity = new JacksonEntity("testRaw", 123); JsonObject expectedStore = JsonObject.create().put("name", "testRaw").put("value", 123); ObjectMapper mapper = new ObjectMapper(); ctx.bucket().mutateIn(key).upsert("user", entity).upsert("user2", entity).remove("sub").remove("array") .remove("string").remove("boolean").execute(); final DocumentFragment<Lookup> fragment = ctx.bucket().async().lookupIn(key).includeRaw(true).get("user") .get("user2").execute().toBlocking().single(); Object c1 = fragment.rawContent(0); Object c2 = fragment.rawContent(1); assertTrue(c1 instanceof byte[]); assertTrue(c2 instanceof byte[]); Assertions.assertThat(mapper.readValue((byte[]) c1, JacksonEntity.class)).isEqualTo(entity); Assertions.assertThat(mapper.readValue((byte[]) c2, JacksonEntity.class)).isEqualTo(entity); Assertions.assertThat(fragment.content(0)).isEqualTo(expectedStore); Assertions.assertThat(fragment.content(1)).isEqualTo(expectedStore); } @Test public void testRawGetSingle() throws IOException { JacksonEntity entity = new JacksonEntity("testRaw", 123); JsonObject expectedStore = JsonObject.create().put("name", "testRaw").put("value", 123); ObjectMapper mapper = new ObjectMapper(); ctx.bucket().mutateIn(key).upsert("user", entity).upsert("user2", entity).remove("sub").remove("array") .remove("string").remove("boolean").execute(); final DocumentFragment<Lookup> fragment = ctx.bucket().async().lookupIn(key).includeRaw(true).get("user") .execute().toBlocking().single(); Object c1 = fragment.rawContent(0); assertTrue(c1 instanceof byte[]); Assertions.assertThat(mapper.readValue((byte[]) c1, JacksonEntity.class)).isEqualTo(entity); Assertions.assertThat(fragment.content(0)).isEqualTo(expectedStore); } //=== GET and EXIST === @Test public void testGetInPathTranscodesToCorrectClasses() { Object objectFragment = ctx.bucket().lookupIn(key).get("sub").execute().content(0); Object intFragment = ctx.bucket().lookupIn(key).get("int").execute().content(0); Object stringFragment = ctx.bucket().lookupIn(key).get("string").execute().content(0); Object arrayFragment = ctx.bucket().lookupIn(key).get("array").execute().content(0); Object booleanFragment = ctx.bucket().lookupIn(key).get("boolean").execute().content(0); JsonObject jsonObjectFragment = ctx.bucket().lookupIn(key).get("sub").execute().content(0, JsonObject.class); assertNotNull(objectFragment); assertTrue(objectFragment.getClass().getName(), objectFragment instanceof JsonObject); assertNotNull(intFragment); assertTrue(intFragment instanceof Integer); assertNotNull(stringFragment); assertTrue(stringFragment instanceof String); assertNotNull(arrayFragment); assertTrue(arrayFragment instanceof JsonArray); assertNotNull(booleanFragment); assertTrue(booleanFragment instanceof Boolean); assertNotNull(jsonObjectFragment); assertEquals(JsonObject.create().put("value", "original"), jsonObjectFragment); } @Test(expected = DocumentDoesNotExistException.class) public void testGetInOnUnknownDocumentThrowsException() { ctx.bucket().lookupIn("blabla").get("array").execute(); } @Test public void testGetInUnknownPathReturnsContentNull() { DocumentFragment<Lookup> result = ctx.bucket().lookupIn(key).get("badPath").execute(); assertNotNull(result); assertEquals(null, result.content(0)); assertEquals(null, result.content("badPath")); } @Test(expected = PathMismatchException.class) public void testGetInPathMismatchThrowsException() { ctx.bucket().lookupIn(key).get("sub[1]").execute(); } @Test public void testExistsIn() { DocumentFragment<Lookup> resultSub = ctx.bucket().lookupIn(key).exists("sub").execute(); DocumentFragment<Lookup> resultInt = ctx.bucket().lookupIn(key).exists("int").execute(); DocumentFragment<Lookup> resultString = ctx.bucket().lookupIn(key).exists("string").execute(); DocumentFragment<Lookup> resultArray = ctx.bucket().lookupIn(key).exists("array").execute(); DocumentFragment<Lookup> resultBoolean = ctx.bucket().lookupIn(key).exists("boolean").execute(); assertTrue(resultSub.exists("sub")); assertTrue(resultInt.exists("int")); assertTrue(resultString.exists("string")); assertTrue(resultArray.exists("array")); assertTrue(resultBoolean.exists("boolean")); assertEquals(Boolean.TRUE, resultSub.content("sub")); assertEquals(Boolean.TRUE, resultInt.content("int")); assertEquals(Boolean.TRUE, resultString.content("string")); assertEquals(Boolean.TRUE, resultArray.content("array")); assertEquals(Boolean.TRUE, resultBoolean.content("boolean")); } @Test(expected = DocumentDoesNotExistException.class) public void testExistsInOnUnknownDocumentThrowsException() { ctx.bucket().lookupIn("blabla").exists("array").execute(); } @Test public void testExistsInUnknownPathReturnContentFalse() { DocumentFragment<Lookup> result = ctx.bucket().lookupIn(key).exists("badPath").execute(); assertNotNull(result); assertEquals(false, result.content(0)); assertEquals(false, result.content("badPath")); } @Test(expected = PathMismatchException.class) public void testExistOnMismatchPathThrowsException() { ctx.bucket().lookupIn(key).exists("sub[1]").execute(); } //=== Mutations with EMPTY path === @Test public void testInsertEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).insert("", "foo"); //exception thrown as soon as the builder method is invoked } @Test public void testUpsertEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).upsert("", "foo"); //exception thrown as soon as the builder method is invoked } @Test public void testReplaceEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).replace("", "foo"); //exception thrown as soon as the builder method is invoked } @Test public void testRemoveEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).remove(""); //exception thrown as soon as the builder method is invoked } @Test public void testArrayInsertEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).arrayInsert("", "foo"); //exception thrown as soon as the builder method is invoked } @Test public void testCounterEmptyPath() { verifyException(ctx.bucket().mutateIn(key), IllegalArgumentException.class).counter("", 1000L); //exception thrown as soon as the builder method is invoked } //=== Mutation with BAD CAS === @Test(expected = CASMismatchException.class) public void testUpsertInWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).upsert("int", null).execute(); } @Test(expected = CASMismatchException.class) public void testInsertInWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).insert("int", null).execute(); } @Test(expected = CASMismatchException.class) public void testReplaceInWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).replace("int", null).execute(); } @Test(expected = CASMismatchException.class) public void testExtendInFrontWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).arrayPrepend("int", "something").execute(); } @Test(expected = CASMismatchException.class) public void testExtendInBackWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).arrayAppend("int", "something").execute(); } @Test(expected = CASMismatchException.class) public void testArrayInsertWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).arrayInsert("int", null).execute(); } @Test(expected = CASMismatchException.class) public void testArrayAddUniqueWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).arrayAddUnique("int", null).execute(); } @Test(expected = CASMismatchException.class) public void testRemoveWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).remove("int").execute(); } @Test(expected = CASMismatchException.class) public void testCounterInWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).counter("int", 1000L, false).execute(); } //==== Durability and Expiry Litmus Tests ==== @Test public void testSingleMutationWithDurability() { int numReplicas = ctx.bucketManager().info().replicaCount(); int numNodes = ctx.bucketManager().info().nodeCount(); Assume.assumeTrue("At least one available replica is necessary for this test", numReplicas >= 1 && numNodes >= (numReplicas + 1)); int timeout = 10; PersistTo persistTo = PersistTo.MASTER; ReplicateTo replicateTo = ReplicateTo.ONE; //single mutations assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).arrayAddUnique("array", "foo")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).arrayInsert("array[1]", "bar")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).counter("int", 1000L)); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).arrayPrepend("array", "extendFront")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).arrayAppend("array", "extendBack")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).insert("sub.insert", "inserted")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).remove("boolean")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).replace("sub.value", "replaced")); assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).upsert("newDict", JsonObject.create().put("value", 1))); //assert final state of JSON JsonObject expected = JsonObject.create() .put("sub", JsonObject.create().put("value", "replaced").put("insert", "inserted")) .put("newDict", JsonObject.create().put("value", 1)).put("string", "someString").put("int", 1123) .put("array", JsonArray.from("extendFront", "1", "bar", 2, true, "foo", "extendBack")); assertEquals(expected, ctx.bucket().get(key).content()); } @Test public void testMultiMutationWithDurability() { int numReplicas = ctx.bucketManager().info().replicaCount(); int numNodes = ctx.bucketManager().info().nodeCount(); Assume.assumeTrue("At least one available replica is necessary for this test", numReplicas >= 1 && numNodes >= (numReplicas + 1)); int timeout = 10; PersistTo persistTo = PersistTo.MASTER; ReplicateTo replicateTo = ReplicateTo.ONE; //multi mutations assertMutationReplicated(key, timeout, persistTo, replicateTo, ctx.bucket().mutateIn(key).arrayAddUnique("array", "foo").remove("boolean")); //assert final state of JSON JsonObject expected = JsonObject.create().put("sub", JsonObject.create().put("value", "original")) // removed // .put("boolean", true) .put("string", "someString").put("int", 123).put("array", JsonArray.from("1", 2, true, "foo")); assertEquals(expected, ctx.bucket().get(key).content()); } private void assertMutationReplicated(String key, int timeout, PersistTo persistTo, ReplicateTo replicateTo, MutateInBuilder mutateInBuilder) { //ensure durability factors are set up mutateInBuilder.withDurability(persistTo, replicateTo); LOGGER.info("Asserting replication of {}", mutateInBuilder); DocumentFragment<Mutation> result = mutateInBuilder.execute(timeout, TimeUnit.SECONDS); JsonDocument masterDoc = ctx.bucket().get(key); JsonDocument replicaDoc = ctx.bucket().getFromReplica(key, ReplicaMode.FIRST).get(0); assertNotNull("result is null", result); assertEquals("master doc and fragment cas differ", masterDoc.cas(), result.cas()); assertEquals("master doc and fragment mutation token differ", masterDoc.mutationToken(), result.mutationToken()); assertEquals("replicated doc and fragment cas differ", replicaDoc.cas(), result.cas()); assertEquals("replicated doc and fragment token differ", replicaDoc.mutationToken(), result.mutationToken()); assertEquals("master doc and replicated doc contents differ", masterDoc.content(), replicaDoc.content()); } private void assertMutationWithExpiry(String expiredKey, MutateInBuilder builder, int expirySeconds) throws InterruptedException { ctx.bucket().upsert(JsonDocument.create(expiredKey, testJson), PersistTo.MASTER); builder = builder.withExpiry(expirySeconds); LOGGER.info("Resetting expiry via {}", builder); DocumentFragment<Mutation> result = builder.execute(); assertNotNull("mutation failed", result); assertNotNull("document has expired too soon", ctx.bucket().get(expiredKey)); //then wait for at least 1s total to have passed since last operation, see that the document is gone Thread.sleep(expirySeconds * 1000 * 2); assertNull("document should have expired after last operation", ctx.bucket().get(expiredKey)); } @Test public void testMutationWithExpirySingleCommonPath() throws InterruptedException { final String expiredKey = "SubdocMutationWithExpirySingleCommonPath"; MutateInBuilder builder = ctx.bucket().mutateIn(expiredKey).arrayAddUnique("array", "foo"); assertMutationWithExpiry(expiredKey, builder, 3); } @Test public void testMutationWithExpirySingleRemovePath() throws InterruptedException { final String expiredKey = "SubdocMutationWithExpirySingleRemovePath"; MutateInBuilder builder = ctx.bucket().mutateIn(expiredKey).remove("boolean"); assertMutationWithExpiry(expiredKey, builder, 1); } @Test public void testMutationWithExpirySingleCounterPath() throws InterruptedException { final String expiredKey = "SubdocMutationWithExpirySingleCounterPath"; MutateInBuilder builder = ctx.bucket().mutateIn(expiredKey).counter("int", 1000L); assertMutationWithExpiry(expiredKey, builder, 1); } @Test public void testMutationWithExpiryMultiPath() throws InterruptedException { final String expiredKey = "SubdocMutationWithExpiryMultiPath"; MutateInBuilder builder = ctx.bucket().mutateIn(expiredKey).upsert("newDict", "notADict").remove("sub"); assertMutationWithExpiry(expiredKey, builder, 1); } //=== UPSERT === @Test public void testUpsertInDictionaryCreates() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).upsert("sub.newValue", "sValue").execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); assertEquals("sValue", ctx.bucket().get(key).content().getObject("sub").getString("newValue")); } @Test public void testUpsertInDictionaryUpdates() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).upsert("sub.value", true).execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); assertEquals(Boolean.TRUE, ctx.bucket().get(key).content().getObject("sub").getBoolean("value")); } @Test public void testUpsertInDictionaryExtraLevelFails() { verifyException(ctx.bucket().mutateIn(key).upsert("sub.some.path", 1024)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testUpsertInDictionaryExtraLevelSucceedsWithCreatesParents() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .upsert("sub.some.path", 1024, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); int content = ctx.bucket().get(key).content().getObject("sub").getObject("some").getInt("path"); assertEquals(1024, content); } @Test public void testUpsertInScalarFails() { verifyException(ctx.bucket().mutateIn(key).upsert("boolean.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testUpsertInArrayFails() { verifyException(ctx.bucket().mutateIn(key).upsert("array.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testUpsertInArrayIndexFails() { verifyException(ctx.bucket().mutateIn(key).upsert("array[1]", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathInvalidException.class)))); } //=== INSERT === @Test public void testInsertInDictionaryCreates() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).insert("sub.newValue", "sValue", false) .execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); assertEquals("sValue", ctx.bucket().get(key).content().getObject("sub").getString("newValue")); } @Test public void testInsertInDictionaryDoesntUpdate() { verifyException(ctx.bucket().mutateIn(key).insert("sub.value", true)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathExistsException.class)))); } @Test public void testInsertInDictionaryExtraLevelFails() { verifyException(ctx.bucket().mutateIn(key).insert("sub.some.path", 1024)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testInsertInDictionaryExtraLevelSucceedsWithCreatesParents() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .insert("sub.some.path", 1024, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); int content = ctx.bucket().get(key).content().getObject("sub").getObject("some").getInt("path"); assertEquals(1024, content); } @Test public void testInsertInScalarFails() { verifyException(ctx.bucket().mutateIn(key).insert("boolean.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testInsertInArrayFails() { verifyException(ctx.bucket().mutateIn(key).insert("array.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testInsertInArrayIndexFails() { verifyException(ctx.bucket().mutateIn(key).insert("array[1]", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathInvalidException.class)))); } //=== REPLACE === @Test public void testReplaceInDictionaryDoesntCreate() { verifyException(ctx.bucket().mutateIn(key).replace("sub.newValue", "sValue")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testReplaceInDictionaryUpdates() { DocumentFragment<Mutation> singleResult = ctx.bucket().mutateIn(key).replace("sub.value", true).execute(); assertNotNull(singleResult); assertEquals(ResponseStatus.SUCCESS, singleResult.status(0)); assertNotEquals(0, singleResult.cas()); assertEquals(Boolean.TRUE, ctx.bucket().get(key).content().getObject("sub").getBoolean("value")); } @Test public void testReplaceInScalarFails() { verifyException(ctx.bucket().mutateIn(key).replace("boolean.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testReplaceInArrayFails() { verifyException(ctx.bucket().mutateIn(key).replace("array.some", "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testReplaceInArrayIndexUpdates() { DocumentFragment<Mutation> singleResult = ctx.bucket().mutateIn(key).replace("array[1]", "string") .execute(); singleResult.content(0); assertNotNull(singleResult); assertEquals(ResponseStatus.SUCCESS, singleResult.status(0)); assertNotEquals(0L, singleResult.cas()); assertEquals("string", ctx.bucket().get(key).content().getArray("array").getString(1)); } @Test public void testReplaceInArrayIndexOutOfBoundsFails() { verifyException(ctx.bucket().mutateIn(key).replace("array[3]", "badIndex")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } //=== EXTEND === @Test public void testExtendOnNonArrayFails() { final String path = "sub"; verifyException(ctx.bucket().mutateIn(key).arrayAppend(path, "string")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testExtendAtBackOfArray() { final String path = "array"; final String value = "newElement"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayAppend(path, value).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); assertNull(result.content(0)); JsonArray array = ctx.bucket().get(key).content().getArray(path); assertEquals(4, array.size()); assertEquals(value, array.getString(3)); } @Test public void testExtendAtFrontOfArray() { final String path = "array"; final String value = "newElement"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayPrepend(path, value).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); assertNull(result.content(0)); JsonArray array = ctx.bucket().get(key).content().getArray(path); assertEquals(4, array.size()); assertEquals(value, array.getString(0)); } @Test public void testExtendInDictionaryWithCreateParentsCreatesArray() { final String path = "sub.array"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .arrayPrepend(path, "newElement", new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertNull(result.content(0)); assertNotEquals(0L, result.cas()); JsonArray array = ctx.bucket().get(key).content().getObject("sub").getArray("array"); assertEquals(1, array.size()); assertEquals("newElement", array.getString(0)); } @Test public void testExtendInDictionnaryWithoutCreateParentsFails() { final String path = "sub.array"; verifyException(ctx.bucket().mutateIn(key).arrayPrepend(path, "newElement")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testExtendAtBackOfRootArrayWorks() { String arrayKey = "subdocArray"; String path = ""; final String value1 = "unique"; final String value2 = "back"; ctx.bucket().upsert(JsonArrayDocument.create(arrayKey, JsonArray.empty())); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(arrayKey).arrayAppend(path, value1).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); assertNull(result.content(0)); JsonArray array = ctx.bucket().get(arrayKey, JsonArrayDocument.class).content(); assertEquals(1, array.size()); assertEquals(value1, array.getString(0)); DocumentFragment<Mutation> result2 = ctx.bucket().mutateIn(arrayKey).arrayAppend(path, value2).execute(); assertNotNull(result2); assertNotEquals(0L, result2.cas()); assertNull(result.content(0)); array = ctx.bucket().get(arrayKey, JsonArrayDocument.class).content(); assertEquals(2, array.size()); assertEquals(value1, array.getString(0)); assertEquals(value2, array.getString(1)); } @Test public void testArrayPrependMultiValuesVararg() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayPrependAll("array", "a", "b", 123, "d") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(storedArray.toString(), 7, storedArray.size()); assertEquals("a", storedArray.getString(0)); assertEquals("b", storedArray.getString(1)); assertEquals(123, storedArray.getInt(2).intValue()); assertEquals("d", storedArray.getString(3)); assertEquals("1", storedArray.getString(4)); assertEquals(2, storedArray.getInt(5).intValue()); assertEquals(true, storedArray.getBoolean(6)); } @Test public void testArrayPrependMultiValuesCollection() { List<?> values = Arrays.asList("a", "b", 123, "d"); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .arrayPrependAll("array", values, new SubdocOptionsBuilder().createParents(false)) .arrayPrependAll("array2", values, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(7, storedArray.size()); assertEquals("a", storedArray.getString(0)); assertEquals("b", storedArray.getString(1)); assertEquals(123, storedArray.getInt(2).intValue()); assertEquals("d", storedArray.getString(3)); assertEquals("1", storedArray.getString(4)); assertEquals(2, storedArray.getInt(5).intValue()); assertEquals(true, storedArray.getBoolean(6)); storedArray = ctx.bucket().get(key).content().getArray("array2"); assertEquals(values, storedArray.toList()); } @Test public void testArrayAppendMultiValuesVararg() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayAppendAll("array", "a", "b", 123, "d") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); System.out.println(storedArray); assertEquals(7, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals(2, storedArray.getInt(1).intValue()); assertEquals(true, storedArray.getBoolean(2)); assertEquals("a", storedArray.getString(3)); assertEquals("b", storedArray.getString(4)); assertEquals(123, storedArray.getInt(5).intValue()); assertEquals("d", storedArray.getString(6)); } @Test public void testArrayAppendMultiValuesCollection() { List<?> values = Arrays.asList("a", "b", 123, "d"); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .arrayAppendAll("array", values, new SubdocOptionsBuilder().createParents(false)) .arrayAppendAll("array2", values, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); System.out.println(storedArray); assertEquals(7, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals(2, storedArray.getInt(1).intValue()); assertEquals(true, storedArray.getBoolean(2)); assertEquals("a", storedArray.getString(3)); assertEquals("b", storedArray.getString(4)); assertEquals(123, storedArray.getInt(5).intValue()); assertEquals("d", storedArray.getString(6)); storedArray = ctx.bucket().get(key).content().getArray("array2"); assertEquals(values, storedArray.toList()); } @Test public void testExtendAtFrontOfRootArrayWorks() { String arrayKey = "subdocArray"; String path = ""; final String value1 = "unique"; final String value2 = "front"; ctx.bucket().upsert(JsonArrayDocument.create(arrayKey, JsonArray.empty())); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(arrayKey) .arrayPrepend(path, value1, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); assertNull(result.content(0)); JsonArray array = ctx.bucket().get(arrayKey, JsonArrayDocument.class).content(); assertEquals(1, array.size()); assertEquals(value1, array.getString(0)); DocumentFragment<Mutation> result2 = ctx.bucket().mutateIn(arrayKey) .arrayPrepend(path, value2, new SubdocOptionsBuilder().createParents(true)).execute(); assertNotNull(result2); assertNotEquals(result.cas(), result2.cas()); array = ctx.bucket().get(arrayKey, JsonArrayDocument.class).content(); assertEquals(2, array.size()); assertEquals(value2, array.getString(0)); assertEquals(value1, array.getString(1)); } //=== ARRAY INSERT === @Test public void testArrayInsertAtIndexZero() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayInsert("array[0]", "arrayInsert") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(4, storedArray.size()); assertEquals("arrayInsert", storedArray.getString(0)); } @Test public void testArrayInsertAtSize() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayInsert("array[3]", "arrayInsert") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(4, storedArray.size()); assertEquals("arrayInsert", storedArray.getString(3)); } @Test public void testArrayInsertAtIndexZeroOnEmptyArray() { //prepare doc with empty array JsonObject withEmptyArray = JsonObject.create().put("array", JsonArray.empty()); ctx.bucket().upsert(JsonDocument.create(key, withEmptyArray)); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayInsert("array[0]", "arrayInsert") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(1, storedArray.size()); assertEquals("arrayInsert", storedArray.getString(0)); } @Test public void testArrayInsertAtExistingIndex() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayInsert("array[1]", "arrayInsert") .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(4, storedArray.size()); assertEquals("arrayInsert", storedArray.getString(1)); assertEquals(2, storedArray.getInt(2).intValue()); assertEquals(true, storedArray.getBoolean(3)); } @Test public void testArrayInsertMultiValuesVararg() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .arrayInsertAll("array[1]", "a", "b", 123, "d").execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(7, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals("a", storedArray.getString(1)); assertEquals("b", storedArray.getString(2)); assertEquals(123, storedArray.getInt(3).intValue()); assertEquals("d", storedArray.getString(4)); assertEquals(2, storedArray.getInt(5).intValue()); assertEquals(true, storedArray.getBoolean(6)); } @Test public void testArrayInsertMultiValuesCollection() { List<?> values = Arrays.asList("a", "b", 123, "d"); DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).arrayInsertAll("array[1]", values).execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(7, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals("a", storedArray.getString(1)); assertEquals("b", storedArray.getString(2)); assertEquals(123, storedArray.getInt(3).intValue()); assertEquals("d", storedArray.getString(4)); assertEquals(2, storedArray.getInt(5).intValue()); assertEquals(true, storedArray.getBoolean(6)); } @Test public void testArrayInsertAtIndexOutOfBounds() { final String path = "array[5]"; verifyException(ctx.bucket().mutateIn(key).arrayInsert(path, "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testArrayInsertAtNegativeIndex() { final String path = "array[-1]"; verifyException(ctx.bucket().mutateIn(key).arrayInsert(path, "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathInvalidException.class)))); } @Test public void testArrayInsertOnArrayThatDoesntExist() { final String path = "secondArray[0]"; verifyException(ctx.bucket().mutateIn(key).arrayInsert(path, "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testArrayInsertOnPathNotEndingWithArrayElement() { final String path = "array"; verifyException(ctx.bucket().mutateIn(key).arrayInsert(path, "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathInvalidException.class)))); } //=== ARRAY ADD UNIQUE === @Test public void testArrayAddUniqueInNonArray() { final String path = "sub"; verifyException(ctx.bucket().mutateIn(key).arrayAddUnique(path, "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testArrayAddUniqueInArrayWithNonPrimitives() { //create document with array containing array JsonObject root = JsonObject.create().put("array", JsonArray.create().add(JsonArray.empty())); ctx.bucket().upsert(JsonDocument.create(key, root)); //not a primitive only array => MISMATCH verifyException(ctx.bucket().mutateIn(key).arrayAddUnique("array", "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testArrayAddUniqueWithNonPrimitiveFragment() { verifyException(ctx.bucket().mutateIn(key).arrayAddUnique("array", JsonObject.create().put("object", true))) .execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(CannotInsertValueException.class)))); } @Test public void testArrayAddUniqueWithValueAlreadyPresent() { verifyException(ctx.bucket().mutateIn(key).arrayAddUnique("array", true)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathExistsException.class)))); } @Test public void testArrayAddUniqueOnNonExistingArray() { verifyException(ctx.bucket().mutateIn(key).arrayAddUnique("anotherArray", "arrayInsert")).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testArrayAddUniqueOnNonExistingArraySucceedsWithCreateParents() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .arrayAddUnique("anotherArray", "arrayInsert", new SubdocOptionsBuilder().createParents(true)) .execute(); assertNotNull(result); assertNotEquals(0L, result.cas()); assertNull(result.content(0)); JsonArray storedArray = ctx.bucket().get(key).content().getArray("anotherArray"); assertEquals(1, storedArray.size()); assertEquals("arrayInsert", storedArray.getString(0)); } //=== REMOVE === @Test public void testRemoveScalar() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).remove("int").execute(); assertNotNull(result); assertNull(result.content("int")); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); assertFalse(ctx.bucket().get(key).content().containsKey("int")); } @Test public void testRemoveDictEntry() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).remove("sub.value").execute(); assertNotNull(result); assertNull(result.content("sub.value")); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); assertEquals(0, ctx.bucket().get(key).content().getObject("sub").size()); } @Test public void testRemoveArrayElement() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).remove("array[1]").execute(); assertNotNull(result); assertNull(result.content("array[1]")); assertEquals(ResponseStatus.SUCCESS, result.status(0)); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(2, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals(true, storedArray.getBoolean(1)); } @Test public void testRemoveLastItem() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).remove("array[-1]").execute(); assertNotNull(result); assertNull(result.content("array[-1]")); assertEquals(ResponseStatus.SUCCESS, result.status("array[-1]")); assertNotEquals(0L, result.cas()); JsonArray storedArray = ctx.bucket().get(key).content().getArray("array"); assertEquals(2, storedArray.size()); assertEquals("1", storedArray.getString(0)); assertEquals(2, storedArray.getInt(1).intValue()); } @Test public void testRemoveScalarWithBadPath() { String path = "integer"; verifyException(ctx.bucket().mutateIn(key).remove(path)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testRemoveDictEntryWithBadKey() { String path = "sub.valuezz"; verifyException(ctx.bucket().mutateIn(key).remove(path)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testRemoveArrayElementWithIndexOutOfBounds() { final String path = "array[4]"; verifyException(ctx.bucket().mutateIn(key).remove(path)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } //=== COUNTER === @Test public void testCounterWithPositiveDeltaIncrements() { String path = "int"; long delta = 1000L; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, delta).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(1123L, result.content(path, Long.class).longValue()); assertEquals(1123L, ctx.bucket().get(key).content().getLong(path).longValue()); } @Test public void testCounterWithNegativeDeltaDecrements() { String path = "int"; long delta = -123L; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, delta).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(0L, result.content(path, Long.class).longValue()); assertEquals(0L, ctx.bucket().get(key).content().getLong("int").longValue()); } @Test(expected = BadDeltaException.class) public void testCounterWithZeroDeltaFails() { ctx.bucket().mutateIn(key).counter("int", 0L); //fails fast } //TODO is there a way of testing for NumberTooBigException (the stored number would have to be greater than Long.MAX.VALUE) @Test public void testCounterProducingTooLargeValueFails() { System.out.println(ctx.bucket().get(key)); String path = "int"; long delta = Long.MAX_VALUE - 123L; //first increment should work DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, delta).execute(); assertNotNull(result); assertEquals(ResponseStatus.SUCCESS, result.status(path)); assertEquals(Long.MAX_VALUE, result.content(0, Long.class).longValue()); assertEquals(Long.MAX_VALUE, result.content(path, Long.class).longValue()); //second increment should fail, as a subdoc level error verifyException(ctx.bucket().mutateIn(key).counter(path, delta)).execute(); assertThat("second counter increment should have made the counter value too big", caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(CannotInsertValueException.class)))); } @Test public void testCounterInPartialPathMissingLastPathElementCreatesNewCounter() { final String path = "sub.counter"; final long delta = 1000L; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, delta).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(1000L, result.content(path, Long.class).longValue()); assertEquals(1000L, result.content(0, Long.class).longValue()); assertEquals(1000L, ctx.bucket().get(key).content().getObject("sub").getLong("counter").longValue()); } @Test public void testCounterDeltaUpperBoundIsLongMaxValue() { long expected = Long.MAX_VALUE; final String path = "newCounter"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, expected).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(expected, result.content(path, Long.class).longValue()); assertEquals(expected, result.content(0, Long.class).longValue()); assertEquals(expected, ctx.bucket().get(key).content().getLong(path).longValue()); } @Test public void testCounterWithLongMinValueDeltaSucceedsOnNewCounter() { long expected = Long.MIN_VALUE + 1L; final String path = "newCounter"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).counter(path, expected).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(expected, result.content(path, Long.class).longValue()); assertEquals(expected, result.content(0, Long.class).longValue()); assertEquals(expected, ctx.bucket().get(key).content().getLong(path).longValue()); } @Test public void testCounterOnNonNumericPathFails() { verifyException(ctx.bucket().mutateIn(key).counter("sub.value", 1000L)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathMismatchException.class)))); } @Test public void testCounterInPartialPathMissingIntermediaryElementFails() { verifyException(ctx.bucket().mutateIn(key).counter("counters.a", 1000L)).execute(); assertThat(caughtException(), allOf(instanceOf(MultiMutationException.class), hasCause(isA(PathNotFoundException.class)))); } @Test public void testCounterInPartialPathMissingIntermediaryElementWithCreateParentsSucceeds() { long delta = 1000L; final String path = "counters.a"; DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key) .counter(path, delta, new SubdocOptionsBuilder().createParents(true)).execute(); assertThat(result.content(path), instanceOf(Long.class)); assertEquals(delta, result.content(path, Long.class).longValue()); assertEquals(delta, result.content(0, Long.class).longValue()); assertEquals(delta, ctx.bucket().get(key).content().getObject("counters").getLong("a").longValue()); } //=== MULTI LOOKUP === @Test(expected = IllegalArgumentException.class) public void testMultiLookupEmptySpecFails() { ctx.bucket().lookupIn(key).execute(); } @Test public void testMultiLookup() { DocumentFragment<Lookup> results = ctx.bucket().lookupIn(key).get("boolean").get("sub").exists("string") //path not found => content null/false .get("no").exists("no") //other error => content throws .get("sub[1]").exists("sub[1]").execute(); assertNotNull(results); assertEquals(7, results.size()); //assert the paths exist and didn't fail (ie we can call content() on them without an exception) assertEquals(true, results.exists(0)); assertEquals(true, results.exists(1)); assertEquals(true, results.exists(2)); assertEquals(true, results.exists(3)); assertEquals(true, results.exists(4)); assertEquals(false, results.exists(5)); assertEquals(false, results.exists(6)); assertTrue(results.cas() != 0); //assert the type of value that content() returns assertTrue(results.content(0) instanceof Boolean); assertTrue(results.content(1) instanceof JsonObject); assertTrue(results.content(2) instanceof Boolean); assertEquals(null, results.content(3)); assertEquals(false, results.content(4)); verifyException(results).content(5); assertThat("expected subdocument exception when getting content for #5", caughtException(), instanceOf(SubDocumentException.class)); verifyException(results).content(6); assertThat("expected subdocument exception when getting content for #6", caughtException(), instanceOf(SubDocumentException.class)); } @Test public void testMultiLookupExistDoesNotFailOnBadPath() { String path1 = "sub[1]"; String path2 = "badPath"; String path3 = "sub"; DocumentFragment<Lookup> results = ctx.bucket().lookupIn(key).exists(path1, path2, path3).execute(); assertNotNull(results); assertTrue(results.cas() != 0); assertEquals(3, results.size()); //errors throw an exception when calling content assertFalse(results.exists(0)); assertEquals(ResponseStatus.SUBDOC_PATH_MISMATCH, results.status(0)); verifyException(results, PathMismatchException.class).content(0); assertFalse(results.exists(path1)); assertEquals(ResponseStatus.SUBDOC_PATH_MISMATCH, results.status(path1)); verifyException(results, PathMismatchException.class).content(path1); //except path not found gives a content, false assertTrue(results.exists(1)); //means that content() won't throw assertEquals(ResponseStatus.SUBDOC_PATH_NOT_FOUND, results.status(1)); assertEquals(false, results.content(1)); assertTrue(results.exists(path2)); //means that content() won't throw assertEquals(ResponseStatus.SUBDOC_PATH_NOT_FOUND, results.status(path2)); assertEquals(false, results.content(path2)); //success gives a content, true assertTrue(results.exists(2)); assertEquals(true, results.content(2)); assertEquals(ResponseStatus.SUCCESS, results.status(2)); assertTrue(results.exists(path3)); assertEquals(true, results.content(path3)); assertEquals(ResponseStatus.SUCCESS, results.status(path3)); } @Test public void testMultiLookupGetDoesNotFailOnBadPath() { DocumentFragment<Lookup> results = ctx.bucket().lookupIn(key).get("sub", "sub[1]", "badPath").execute(); assertNotNull(results); assertTrue(results.cas() != 0); assertEquals(3, results.size()); assertNotNull(results.content(0)); assertTrue(results.exists(0)); assertEquals(ResponseStatus.SUCCESS, results.status(0)); assertEquals(testJson.getObject("sub"), results.content(0)); assertFalse(results.exists(1)); assertEquals(ResponseStatus.SUBDOC_PATH_MISMATCH, results.status(1)); verifyException(results, PathMismatchException.class).content(1); assertTrue(results.exists(2)); assertEquals(ResponseStatus.SUBDOC_PATH_NOT_FOUND, results.status(2)); assertEquals(null, results.content(2)); } //=== MULTI MUTATION === @Test public void testMultiMutation() { DocumentFragment<Mutation> mmr = ctx.bucket().mutateIn(key).replace("sub.value", "replaced") .replace("string", "otherString").upsert("sub.otherValue", "newValue").arrayInsert("array[1]", "v") .arrayAddUnique("array", "v2").arrayAppend("array", "v3").counter("int", 1000) .insert("sub.insert", "inserted").remove("boolean").execute(); JsonDocument stored = ctx.bucket().get(key); assertNotNull(mmr); assertNotEquals(0L, mmr.cas()); assertEquals(stored.cas(), mmr.cas()); assertEquals(stored.mutationToken(), mmr.mutationToken()); assertEquals("replaced", stored.content().getObject("sub").getString("value")); assertEquals("otherString", stored.content().getString("string")); assertEquals("newValue", stored.content().getObject("sub").getString("otherValue")); assertEquals(JsonArray.from("1", "v", 2, true, "v2", "v3"), stored.content().getArray("array")); assertEquals(1123, stored.content().getInt("int").intValue()); assertEquals("inserted", stored.content().getObject("sub").getString("insert")); assertFalse(stored.content().containsKey("boolean")); } @Test public void testMultiMutationWithCreateParents() { DocumentFragment<Mutation> mmr = ctx.bucket().mutateIn(key) .arrayAddUnique("addUnique.array", "v", new SubdocOptionsBuilder().createParents(true)) .counter("counter.newCounter", 100, new SubdocOptionsBuilder().createParents(true)) .arrayPrepend("extend.array", "v", new SubdocOptionsBuilder().createParents(true)) .insert("insert.sub.entry", "v", new SubdocOptionsBuilder().createParents(true)) .upsert("upsert.sub.entry", "v", new SubdocOptionsBuilder().createParents(true)).execute(); JsonDocument stored = ctx.bucket().get(key); assertNotNull(mmr); assertNotEquals(0L, mmr.cas()); assertEquals(stored.cas(), mmr.cas()); assertEquals(stored.mutationToken(), mmr.mutationToken()); assertEquals("v", stored.content().getObject("addUnique").getArray("array").getString(0)); assertEquals(100L, stored.content().getObject("counter").getLong("newCounter").longValue()); assertEquals("v", stored.content().getObject("extend").getArray("array").getString(0)); assertEquals("v", stored.content().getObject("insert").getObject("sub").getString("entry")); assertEquals("v", stored.content().getObject("upsert").getObject("sub").getString("entry")); } @Test public void testMultiMutationWithFailure() { verifyException(ctx.bucket().mutateIn(key).replace("sub.value", "replaced").replace("int", 1024) .upsert("sub.otherValue.deeper", "newValue").replace("secondError", "unreachable")).execute(); assertThat(caughtException(), instanceOf(MultiMutationException.class)); MultiMutationException e = caughtException(); assertEquals(2, e.firstFailureIndex()); assertEquals(ResponseStatus.SUBDOC_PATH_NOT_FOUND, e.firstFailureStatus()); assertNotNull(e.getCause()); assertTrue(e.getCause().toString(), e.getCause() instanceof PathNotFoundException); assertTrue(e.getCause().toString(), e.getCause().toString().contains("sub.otherValue.deeper")); assertEquals(4, e.originalSpec().size()); assertEquals(testJson, ctx.bucket().get(key).content()); } @Test(expected = IllegalArgumentException.class) public void testMultiMutationWithEmptySpecFails() { ctx.bucket().mutateIn(key).execute(); } @Test(expected = CASMismatchException.class) public void testMultiMutationWithBadCas() { ctx.bucket().mutateIn(key).withCas(1234L).replace("sub", 123).remove("int").execute(); } @Test(expected = DurabilityException.class) public void shouldFailMutationWithDurabilityException() { int replicaCount = ctx.bucketManager().info().replicaCount(); Assume.assumeTrue(replicaCount < 3); ctx.bucket().mutateIn(key).upsert("sub", 1234).execute(PersistTo.FOUR); } @Test public void shouldPersistMutation() { DocumentFragment<Mutation> result = ctx.bucket().mutateIn(key).upsert("string", "iPersist") .execute(PersistTo.ONE); assertTrue(result.cas() != 0); assertEquals(ResponseStatus.SUCCESS, result.status("string")); } }