<NME> AbstractBackendTest.java <BEF> package de.bwaldvogel.mongo.backend; import static com.mongodb.client.model.Filters.and; import static com.mongodb.client.model.Filters.eq; import static com.mongodb.client.model.Filters.exists; import static com.mongodb.client.model.Filters.gt; import static com.mongodb.client.model.Filters.in; import static com.mongodb.client.model.Filters.lt; import static com.mongodb.client.model.Filters.ne; import static com.mongodb.client.model.Filters.nor; import static com.mongodb.client.model.Filters.or; import static com.mongodb.client.model.Updates.addEachToSet; import static com.mongodb.client.model.Updates.pull; import static com.mongodb.client.model.Updates.pullByFilter; import static com.mongodb.client.model.Updates.set; import static de.bwaldvogel.mongo.backend.TestUtils.date; import static de.bwaldvogel.mongo.backend.TestUtils.getCollectionStatistics; import static de.bwaldvogel.mongo.backend.TestUtils.instant; import static de.bwaldvogel.mongo.backend.TestUtils.json; import static de.bwaldvogel.mongo.backend.TestUtils.toArray; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.bson.BsonInt32; import org.bson.BsonJavaScript; import org.bson.BsonObjectId; import org.bson.BsonString; import org.bson.BsonTimestamp; import org.bson.Document; import org.bson.UuidRepresentation; import org.bson.codecs.DocumentCodec; import org.bson.codecs.configuration.CodecRegistries; import org.bson.conversions.Bson; import org.bson.types.Binary; import org.bson.types.Code; import org.bson.types.MaxKey; import org.bson.types.MinKey; import org.bson.types.ObjectId; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.AdditionalAnswers; import org.mockito.Mockito; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import com.mongodb.DBRef; import com.mongodb.DuplicateKeyException; import com.mongodb.MongoBulkWriteException; import com.mongodb.MongoClientSettings; import com.mongodb.MongoCommandException; import com.mongodb.MongoCursorNotFoundException; import com.mongodb.MongoNamespace; import com.mongodb.MongoQueryException; import com.mongodb.MongoServerException; import com.mongodb.MongoWriteException; import com.mongodb.ReadPreference; import com.mongodb.ServerCursor; import com.mongodb.WriteConcern; import com.mongodb.bulk.BulkWriteResult; import com.mongodb.bulk.BulkWriteUpsert; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.BulkWriteOptions; import com.mongodb.client.model.CountOptions; import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.DeleteManyModel; import com.mongodb.client.model.EstimatedDocumentCountOptions; import com.mongodb.client.model.Filters; import com.mongodb.client.model.FindOneAndReplaceOptions; import com.mongodb.client.model.FindOneAndUpdateOptions; import com.mongodb.client.model.IndexOptions; import com.mongodb.client.model.InsertOneModel; import com.mongodb.client.model.RenameCollectionOptions; import com.mongodb.client.model.ReplaceOneModel; import com.mongodb.client.model.ReplaceOptions; import com.mongodb.client.model.ReturnDocument; import com.mongodb.client.model.UpdateManyModel; import com.mongodb.client.model.UpdateOneModel; import com.mongodb.client.model.UpdateOptions; import com.mongodb.client.model.Updates; import com.mongodb.client.model.WriteModel; import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.InsertOneResult; import com.mongodb.client.result.UpdateResult; public abstract class AbstractBackendTest extends AbstractTest { private static final Logger log = LoggerFactory.getLogger(AbstractBackendTest.class); protected static final String OTHER_TEST_DATABASE_NAME = "bar"; private static final Duration DEFAULT_TEST_TIMEOUT = Duration.ofSeconds(30); protected MongoCollection<Document> getCollection(String collectionName) { return db.getCollection(collectionName); } private String getCollectionName() { return collection.getNamespace().getCollectionName(); } @Test public void testSimpleInsert() throws Exception { collection.insertOne(json("_id: 1")); } @Test public void testSimpleCursor() { int expectedCount = 20; int batchSize = 10; for (int i = 0; i < expectedCount; i++) { collection.insertOne(new Document("_id", 100 + i)); } MongoCursor<Document> cursor = collection.find().sort(json("_id: 1")).batchSize(batchSize).cursor(); List<Document> retrievedDocuments = new ArrayList<>(); while (cursor.hasNext()) { retrievedDocuments.add(cursor.next()); } assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(cursor::next) .withMessage(null); assertThat(retrievedDocuments).hasSize(expectedCount); assertThat(retrievedDocuments).first().isEqualTo(json("_id: 100")); assertThat(retrievedDocuments).last().isEqualTo(json("_id: 119")); } @Test public void testCursor_skipDocuments() { int totalCount = 20; int numToSkip = 5; int expectedCount = totalCount - numToSkip; int batchSize = 10; for (int i = 0; i < totalCount; i++) { collection.insertOne(new Document("_id", 100 + i)); } MongoCursor<Document> cursor = collection.find() .sort(json("_id: 1")) .skip(numToSkip) .batchSize(batchSize) .cursor(); List<Document> retrievedDocuments = new ArrayList<>(); while (cursor.hasNext()) { retrievedDocuments.add(cursor.next()); } assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(cursor::next) .withMessage(null); assertThat(retrievedDocuments).hasSize(expectedCount); assertThat(retrievedDocuments).first().isEqualTo(json("_id: 105")); assertThat(retrievedDocuments).last().isEqualTo(json("_id: 119")); } @Test public void testCursor_skipAndLimitDocuments() { int totalCount = 50; int numToSkip = 5; int limit = 20; int batchSize = 10; for (int i = 0; i < totalCount; i++) { collection.insertOne(new Document("_id", 100 + i)); } MongoCursor<Document> cursor = collection.find() .sort(json("_id: 1")) .skip(numToSkip) .limit(limit) .batchSize(batchSize) .cursor(); List<Document> retrievedDocuments = new ArrayList<>(); while (cursor.hasNext()) { retrievedDocuments.add(cursor.next()); } assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(cursor::next) .withMessage(null); assertThat(retrievedDocuments).hasSize(limit); assertThat(retrievedDocuments).first().isEqualTo(json("_id: 105")); assertThat(retrievedDocuments).last().isEqualTo(json("_id: 124")); } @Test public void testCursor_withProjection() { int totalCount = 50; int numToSkip = 5; int limit = 20; int batchSize = 10; for (int i = 0; i < totalCount; i++) { collection.insertOne(new Document("_id", 100 + i).append("x", 1000 + i)); } MongoCursor<Document> cursor = collection.find() .sort(json("_id: 1")) .skip(numToSkip) .limit(limit) .batchSize(batchSize) .projection(json("_id: 0, x: 1")) .cursor(); List<Document> retrievedDocuments = new ArrayList<>(); while (cursor.hasNext()) { retrievedDocuments.add(cursor.next()); } assertThatExceptionOfType(NoSuchElementException.class) .isThrownBy(cursor::next) .withMessage(null); assertThat(retrievedDocuments).hasSize(limit); assertThat(retrievedDocuments).first().isEqualTo(json("x: 1005")); assertThat(retrievedDocuments).last().isEqualTo(json("x: 1024")); } @Test public void testCloseCursor() { int expectedCount = 20; int batchSize = 5; for (int i = 0; i < expectedCount; i++) { collection.insertOne(new Document("value", i)); } MongoCursor<Document> cursor = collection.find().batchSize(batchSize).cursor(); int count = 0; while (cursor.hasNext() && count < 10) { cursor.next(); count++; } cursor.close(); assertThat(count).isEqualTo(10); assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(cursor::next) .withMessage("Cursor has been closed"); } @Test public void testCursor_iteratingACursorThatNoLongerExists() { int expectedCount = 20; for (int i = 0; i < expectedCount; i++) { collection.insertOne(new Document("name", "testUser1")); } MongoCursor<Document> cursor = collection.find().batchSize(1).cursor(); cursor.next(); killCursors(Collections.singletonList(cursor.getServerCursor().getId())); assertThatExceptionOfType(MongoCursorNotFoundException.class) .isThrownBy(cursor::next) .withMessageContaining("Query failed with error code -5 and error message"); } @Test void testKillCursor() throws Exception { for (int i = 0; i < 20; i++) { collection.insertOne(json("")); } MongoCursor<Document> cursor = collection.find().batchSize(1).cursor(); ServerCursor serverCursor = cursor.getServerCursor(); String collectionName = collection.getNamespace().getCollectionName(); Document result = runCommand(new Document("killCursors", collectionName).append("cursors", Arrays.asList(serverCursor.getId()))); assertThat(result.getDouble("ok")).isEqualTo(1.0); assertThat(result.get("cursorsKilled")).isEqualTo(Arrays.asList(serverCursor.getId())); assertThat(result.get("cursorsNotFound")).isEqualTo(Collections.emptyList()); } @Test void testKillCursor_unknownCursorId() throws Exception { collection.insertOne(json("")); String collectionName = collection.getNamespace().getCollectionName(); Document result = runCommand(new Document("killCursors", collectionName).append("cursors", Arrays.asList(987654321L))); assertThat(result.getDouble("ok")).isEqualTo(1.0); assertThat(result.get("cursorsKilled")).isEqualTo(Collections.emptyList()); assertThat(result.get("cursorsNotFound")).isEqualTo(Arrays.asList(987654321L)); } @Test public void testSimpleInsertDelete() throws Exception { collection.insertOne(json("_id: 1")); collection.deleteOne(json("_id: 1")); } @Test public void testCreateCollection() throws Exception { String newCollectionName = "some-collection"; assertThat(db.listCollectionNames()).doesNotContain(newCollectionName); db.createCollection(newCollectionName, new CreateCollectionOptions()); assertThat(db.listCollectionNames()).contains(newCollectionName); } @Test public void testCreateCappedCollection_invalidOptions() throws Exception { String newCollectionName = "some-collection"; assertThat(db.listCollectionNames()).doesNotContain(newCollectionName); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> db.createCollection("some-collection", new CreateCollectionOptions().capped(true))) .withMessageContaining("Command failed with error 72 (InvalidOptions): 'the 'size' field is required when 'capped' is true'"); } @Test public void testCreateCollectionAlreadyExists() throws Exception { db.createCollection("some-collection", new CreateCollectionOptions()); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> db.createCollection("some-collection", new CreateCollectionOptions())) .withMessageContaining("Command failed with error 48 (NamespaceExists): 'a collection 'testdb.some-collection' already exists'"); } @Test public void testUnsupportedModifier() throws Exception { collection.insertOne(json("")); assertMongoWriteException(() -> collection.updateOne(json(""), json("$foo: {}")), 9, "FailedToParse", "Unknown modifier: $foo. Expected a valid update modifier or pipeline-style update specified as an array"); } @Test public void testUpsertWithInc() { Document query = json("_id: {f: 'ca', '1': {l: 2}, t: {t: 11}}"); Document update = json("'$inc': {'n.!' : 1 , 'n.a.b:false' : 1}"); collection.updateOne(query, update, new UpdateOptions().upsert(true)); query.putAll(json("n: {'!': 1, a: {'b:false': 1}}")); assertThat(collection.find().first()).isEqualTo(query); } @Test public void testBasicUpdate() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2, b: 5")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); collection.replaceOne(json("_id: 2"), json("_id: 2, a: 5")); assertThat(collection.find(json("_id: 2")).first()).isEqualTo(json("_id: 2, a: 5")); } @Test public void testCollectionStats_newCollection() throws Exception { Document stats = getCollStats(); assertThat(stats.getDouble("ok")).isEqualTo(1.0); assertThat(stats.getInteger("count")).isEqualTo(0); assertThat(stats.getInteger("size")).isEqualTo(0); assertThat(stats).doesNotContainKey("avgObjSize"); assertThat(db.listCollectionNames()).doesNotContain(getCollectionName()); } @Test public void testCollectionStats() throws Exception { collection.insertOne(json("")); collection.insertOne(json("abc: 'foo'")); Document stats = getCollStats(); assertThat(stats.getDouble("ok")).isEqualTo(1.0); assertThat(stats.getInteger("count")).isEqualTo(2); assertThat(stats.getInteger("size")).isEqualTo(57); assertThat(stats.getInteger("avgObjSize")).isEqualTo(28); } private Document getCollStats() { String collectionName = getCollectionName(); return getCollectionStatistics(db, collectionName); } @Test public void testGetLogStartupWarnings() throws Exception { Document startupWarnings = runCommand(json("getLog: 'startupWarnings'")); assertThat(startupWarnings.getDouble("ok")).isEqualTo(1.0); assertThat(startupWarnings.get("totalLinesWritten")).isInstanceOf(Number.class); assertThat(startupWarnings.get("log")).isEqualTo(Collections.emptyList()); } @Test public void testGetLogWhichDoesNotExist() throws Exception { assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> runCommand(json("getLog: 'illegal'"))) .withMessageContaining("Command failed with error -1: 'no RamLog named: illegal'"); } @Test public void testCompoundDateIdUpserts() { Document query = json("_id: {$lt: {n: 'a', t: 10}, $gte: {n: 'a', t: 1}}"); List<Document> toUpsert = Arrays.asList( json("_id: {n: 'a', t: 1}"), json("_id: {n: 'a', t: 2}"), json("_id: {n: 'a', t: 3}"), json("_id: {n: 'a', t: 11}")); for (Document dbo : toUpsert) { collection.replaceOne(dbo, new Document(dbo).append("foo", "bar"), new ReplaceOptions().upsert(true)); } assertThat(collection.find(query)) .containsExactly( json("_id: {n: 'a', t: 1}, foo: 'bar'"), json("_id: {n: 'a', t: 2}, foo: 'bar'"), json("_id: {n: 'a', t: 3}, foo: 'bar'") ); } @Test public void testCompoundSort() { collection.insertOne(json("a:1, _id: 1")); collection.insertOne(json("a:2, _id: 5")); collection.insertOne(json("a:1, _id: 2")); collection.insertOne(json("a:2, _id: 4")); collection.insertOne(json("a:1, _id: 3")); assertThat(collection.find().sort(json("a:1, _id: -1"))) .containsExactly( json("a: 1, _id: 3"), json("a: 1, _id: 2"), json("a: 1, _id: 1"), json("a: 2, _id: 5"), json("a: 2, _id: 4") ); } @Test public void testCountCommand() { assertThat(db.runCommand(new Document("count", getCollectionName()))) .isEqualTo(json("ok: 1.0, n: 0")); collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); assertThat(db.runCommand(new Document("count", getCollectionName()).append("query", json("_id: 2")))) .isEqualTo(json("ok: 1.0, n: 1")); assertThat(db.runCommand(new Document("count", getCollectionName()).append("query", json("")))) .isEqualTo(json("ok: 1.0, n: 3")); assertThat(db.runCommand(new Document("count", getCollectionName()).append("query", json("_id: 4")))) .isEqualTo(json("ok: 1.0, n: 0")); assertThat(db.runCommand(new Document("count", getCollectionName()).append("maxTimeMS", 5000))) .isEqualTo(json("ok: 1.0, n: 3")); } @Test public void testNonPrimaryCountCommand() { assertThat(collection.withReadPreference(ReadPreference.nearest()).countDocuments()).isZero(); } @Test @SuppressWarnings("deprecation") public void testCountCommandWithQuery() { collection.insertOne(json("n:1")); collection.insertOne(json("n:2")); collection.insertOne(json("n:2")); assertThat(collection.countDocuments(json("n:2"))).isEqualTo(2); } @Test @SuppressWarnings("deprecation") public void testCountCommandWithSkipAndLimit() { collection.insertOne(json("x: 1")); collection.insertOne(json("x: 1")); collection.insertOne(json("x: 2")); collection.insertOne(json("x: 1")); collection.insertOne(json("x: 2")); collection.insertOne(json("x: 1")); assertThat(collection.countDocuments(json("x: 1"), new CountOptions().skip(4).limit(2))).isEqualTo(0); assertThat(collection.countDocuments(json("x: 1"), new CountOptions().limit(3))).isEqualTo(3); assertThat(collection.countDocuments(json("x: 1"), new CountOptions().limit(10))).isEqualTo(4); assertThat(collection.countDocuments(json("x: 1"), new CountOptions().skip(1))).isEqualTo(3); } @Test public void testCountDocuments() throws Exception { assertThat(collection.countDocuments()).isZero(); } @Test public void testCountDocumentsWithQuery() { collection.insertOne(json("n:1")); collection.insertOne(json("n:2")); collection.insertOne(json("n:2")); assertThat(collection.countDocuments(json("n:2"))).isEqualTo(2); } @Test public void testEstimatedDocumentCount() throws Exception { assertThat(collection.estimatedDocumentCount()).isEqualTo(0); collection.insertOne(json("n:1")); collection.insertOne(json("n:2")); collection.insertOne(json("n:2")); assertThat(collection.estimatedDocumentCount()).isEqualTo(3); assertThat(collection.estimatedDocumentCount(new EstimatedDocumentCountOptions().maxTime(1, TimeUnit.SECONDS))).isEqualTo(3); } @Test public void testCreateIndexes() { collection.createIndex(new Document("n", 1)); collection.createIndex(new Document("b", 1)); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {n: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "n_1").append("v", 2), json("key: {b: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "b_1").append("v", 2) ); } @Test public void testCreateIndexesWithoutNamespace() { collection.insertOne(json("_id: 1, b: 1")); Document result = db.runCommand(json("createIndexes: 'testcoll', indexes: [{key: {b: 1}, name: 'b_1'}]")); assertThat(result.getDouble("ok")).isEqualTo(1.0); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {b: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "b_1").append("v", 2) ); } @Test public void testCreateSecondPrimaryKeyIndex() { collection.insertOne(json("_id: 1, b: 1")); Document result = db.runCommand(json("createIndexes: 'testcoll', indexes: [{key: {_id: 1}, name: '_id_1'}]")); assertThat(result.getDouble("ok")).isEqualTo(1.0); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2) ); db.drop(); } @Test public void testCreateIndexOnNonExistingDatabase() { db.drop(); Document result = db.runCommand(json("createIndexes: 'sometable', indexes: [{key: {_id: 1}, name: '_id_1'}]")); assertThat(result.getDouble("ok")).isEqualTo(1.0); db.drop(); } @Test public void testDropAndRecreateIndex() throws Exception { collection.createIndex(new Document("n", 1)); collection.createIndex(new Document("b", 1)); collection.createIndex(new Document("c", 1), new IndexOptions().unique(true)); collection.dropIndex(new Document("n", 1)); collection.insertOne(json("_id: 1, c: 10")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.dropIndex(new Document("n", 1))) .withMessageContaining("Command failed with error 27 (IndexNotFound): 'can't find index with key: { n: 1 }'"); assertMongoWriteException(() -> collection.insertOne(json("_id: 2, c: 10")), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: c_1 dup key: { c: 10 }"); collection.dropIndex(new Document("c", 1)); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.dropIndex(new Document("c", 1))) .withMessageContaining("Command failed with error 27 (IndexNotFound): 'can't find index with key: { c: 1 }'"); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {b: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "b_1").append("v", 2) ); collection.insertOne(json("_id: 2, c: 10")); assertThatExceptionOfType(DuplicateKeyException.class) .isThrownBy(() -> collection.createIndex(new Document("c", 1), new IndexOptions().unique(true))) .withMessageContaining("Write failed with error code 11000 and error message " + "'E11000 duplicate key error collection: testdb.testcoll index: c_1 dup key:"); collection.deleteOne(json("_id: 1")); collection.createIndex(new Document("c", 1), new IndexOptions().unique(true)); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {b: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "b_1").append("v", 2), json("key: {c: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "c_1").append("unique", true).append("v", 2) ); } @Test void testDropIndexes() throws Exception { collection.insertOne(json("_id: 1, c: 10")); collection.createIndex(new Document("c", 1), new IndexOptions().unique(true)); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {c: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "c_1").append("unique", true).append("v", 2) ); collection.dropIndexes(); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2) ); collection.dropIndexes(); collection.drop(); collection.dropIndexes(); } // https://github.com/bwaldvogel/mongo-java-server/issues/184 @Test void testDropIndex_string() throws Exception { collection.insertOne(json("_id: 1, c: 10")); String indexName = collection.createIndex(new Document("c", 1), new IndexOptions().unique(true)); assertThat(collection.listIndexes()).hasSize(2); collection.dropIndex(indexName); assertThat(collection.listIndexes()).hasSize(1); collection.insertOne(json("_id: 2, c: 10")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.dropIndex(indexName)) .withMessageContaining("Command failed with error 27 (IndexNotFound): 'index not found with name [c_1]'"); } // https://github.com/bwaldvogel/mongo-java-server/issues/184 @Test void testDropIndex_null() throws Exception { collection.insertOne(json("_id: 1, c: 10")); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> collection.dropIndex((String) null)) .withMessage("indexName can not be null"); } // https://github.com/bwaldvogel/mongo-java-server/issues/171 @Test void testDropIndexes_twoIndexesWithTheSameKey() throws Exception { collection.insertOne(json("_id: 1, c: 10")); MongoCollection<Document> otherCollection = getCollection("other"); otherCollection.insertOne(json("_id: 1, c: 10")); collection.createIndex(new Document("c", 1)); otherCollection.createIndex(new Document("c", 1)); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {c: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "c_1").append("v", 2) ); assertThat(otherCollection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", otherCollection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {c: 1}").append("ns", otherCollection.getNamespace().getFullName()).append("name", "c_1").append("v", 2) ); collection.dropIndex(json("c: 1")); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2) ); assertThat(otherCollection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", otherCollection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {c: 1}").append("ns", otherCollection.getNamespace().getFullName()).append("name", "c_1").append("v", 2) ); } @Test public void testCurrentOperations() throws Exception { Document currentOperations = getAdminDb().getCollection("$cmd.sys.inprog").find().first(); assertThat(currentOperations).isNotNull(); assertThat(currentOperations.get("inprog")).isInstanceOf(List.class); } @Test public void testListCollectionsEmpty() throws Exception { Document result = db.runCommand(json("listCollections: 1")); assertThat(result.getDouble("ok")).isEqualTo(1.0); Document cursor = (Document) result.get("cursor"); assertThat(cursor.keySet()).containsExactly("id", "ns", "firstBatch"); assertThat(cursor.get("id")).isEqualTo(Long.valueOf(0)); assertThat(cursor.get("ns")).isEqualTo(db.getName() + ".$cmd.listCollections"); List<?> firstBatch = (List<?>) cursor.get("firstBatch"); assertThat(firstBatch).isEmpty(); } @Test public void testListCollections() throws Exception { List<String> collections = Arrays.asList("coll1", "coll2", "coll3"); for (String collection : collections) { getCollection(collection).insertOne(json("_id: 1")); } Document result = db.runCommand(json("listCollections: 1")); assertThat(result.getDouble("ok")).isEqualTo(1.0); Document cursor = (Document) result.get("cursor"); assertThat(cursor.keySet()).containsExactly("id", "ns", "firstBatch"); assertThat(cursor.get("id")).isEqualTo(Long.valueOf(0)); assertThat(cursor.get("ns")).isEqualTo(db.getName() + ".$cmd.listCollections"); assertThat(cursor.get("firstBatch")).isInstanceOf(List.class); @SuppressWarnings("unchecked") List<Document> firstBatch = (List<Document>) cursor.get("firstBatch"); assertThat(firstBatch).hasSameSizeAs(collections); Set<String> collectionNames = new HashSet<>(); for (Document collection : firstBatch) { assertThat(collection).containsOnlyKeys("name", "options", "type", "idIndex", "info"); String name = (String) collection.get("name"); assertThat(collection.get("options")).isEqualTo(json("")); assertThat(collection.get("name")).isInstanceOf(String.class); assertThat(collection.get("type")).isEqualTo("collection"); assertThat(collection.get("idIndex")).isEqualTo(json("key: {_id: 1}, name: '_id_', ns: 'testdb." + name + "', v: 2")); assertThat(collection.get("info")).isInstanceOf(Document.class); collectionNames.add(name); } assertThat(collectionNames).containsExactlyInAnyOrderElementsOf(collections); } @Test public void testGetCollectionNames() throws Exception { getCollection("foo").insertOne(json("")); getCollection("bar").insertOne(json("")); assertThat(db.listCollectionNames()) .containsExactlyInAnyOrder("foo", "bar"); } @Test public void testSystemNamespaces() throws Exception { getCollection("foo").insertOne(json("")); getCollection("bar").insertOne(json("")); MongoCollection<Document> namespaces = db.getCollection("system.namespaces"); assertThat(namespaces.find()).containsExactlyInAnyOrder( json("name: 'testdb.foo'"), json("name: 'testdb.bar'") ); } @Test public void testDatabaseStats() throws Exception { Document stats = db.runCommand(new Document("dbStats", 1).append("scale", 1)); assertThat(stats.getDouble("ok")).isEqualTo(1.0); assertThat(stats.getInteger("objects")).isZero(); assertThat(stats.getInteger("collections")).isZero(); assertThat(stats.getInteger("indexes")).isZero(); assertThat(stats.getInteger("dataSize")).isZero(); getCollection("foo").insertOne(json("")); getCollection("foo").insertOne(json("")); getCollection("bar").insertOne(json("")); stats = db.runCommand(new Document("dbStats", 1).append("scale", 1)); assertThat(stats.getDouble("ok")).isEqualTo(1.0); assertThat(stats.getInteger("objects")).isEqualTo(3); assertThat(stats.getInteger("collections")).isEqualTo(2); assertThat(stats.getInteger("indexes")).isEqualTo(2); assertThat(stats.getDouble("dataSize")).isEqualTo(66.0); } @Test public void testDeleteDecrementsCount() { collection.insertOne(json("key: 'value'")); assertThat(collection.countDocuments()).isEqualTo(1); collection.deleteOne(json("")); assertThat(collection.countDocuments()).isZero(); } @Test public void testDeleteInSystemNamespace() throws Exception { assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> getCollection("system.foobar").deleteOne(json(""))) .withMessageContaining("Command failed with error 73 (InvalidNamespace): 'cannot write to 'testdb.system.foobar'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> getCollection("system.namespaces").deleteOne(json(""))) .withMessageContaining("Command failed with error 73 (InvalidNamespace): 'cannot write to 'testdb.system.namespaces'"); } @Test public void testUpdateInSystemNamespace() throws Exception { for (String collectionName : Arrays.asList("system.foobar", "system.namespaces")) { MongoCollection<Document> collection = getCollection(collectionName); assertMongoWriteException(() -> collection.updateMany(eq("some", "value"), set("field", "value")), 10156, "cannot update system collection"); } } @Test public void testDistinctQuery() { collection.insertOne(json("_id: 1, n: null")); collection.insertOne(json("_id: 2, n: 3")); collection.insertOne(json("_id: 3, n: 1")); collection.insertOne(json("_id: 4, n: 2")); collection.insertOne(json("_id: 5, n: 1.0")); collection.insertOne(json("_id: 6, n: 1")); collection.insertOne(json("_id: 7, n: -0.0")); collection.insertOne(json("_id: 8, n: 0")); assertThat(collection.distinct("n", Integer.class)) .containsExactly(null, 0, 1, 2, 3); assertThat(collection.distinct("n", json("n: {$gt: 1}"), Integer.class)) .containsExactly(2, 3); assertThat(collection.distinct("foobar", String.class)).isEmpty(); assertThat(collection.distinct("_id", Integer.class)) .hasSize((int) collection.countDocuments()); } // https://github.com/bwaldvogel/mongo-java-server/issues/44 @Test public void testDistinctUuids_legacy() throws Exception { MongoClientSettings legacyUuidSettings = MongoClientSettings.builder() .applyConnectionString(connectionString) .uuidRepresentation(UuidRepresentation.JAVA_LEGACY) .build(); try (MongoClient clientWithLegacyUuid = MongoClients.create(legacyUuidSettings)) { MongoCollection<Document> collectionWithLegacyUuid = clientWithLegacyUuid.getDatabase(collection.getNamespace().getDatabaseName()).getCollection(collection.getNamespace().getCollectionName()); collectionWithLegacyUuid.insertOne(json("_id: 1, n: null")); collectionWithLegacyUuid.insertOne(json("_id: 2").append("n", new UUID(0, 1))); collectionWithLegacyUuid.insertOne(json("_id: 3").append("n", new UUID(1, 0))); collectionWithLegacyUuid.insertOne(json("_id: 4").append("n", new UUID(0, 2))); collectionWithLegacyUuid.insertOne(json("_id: 5").append("n", new UUID(1, 1))); collectionWithLegacyUuid.insertOne(json("_id: 6").append("n", new UUID(1, 0))); assertThat(collectionWithLegacyUuid.distinct("n", UUID.class)) .containsExactly( null, new UUID(0, 1), new UUID(0, 2), new UUID(1, 0), new UUID(1, 1) ); } } @Test public void testDistinctUuids() throws Exception { collection.insertOne(json("_id: 1, n: null")); collection.insertOne(json("_id: 2").append("n", new UUID(0, 1))); collection.insertOne(json("_id: 3").append("n", new UUID(1, 0))); collection.insertOne(json("_id: 4").append("n", new UUID(0, 2))); collection.insertOne(json("_id: 5").append("n", new UUID(1, 1))); collection.insertOne(json("_id: 6").append("n", new UUID(1, 0))); assertThat(collection.distinct("n", UUID.class)) .containsExactly( null, new UUID(0, 1), new UUID(0, 2), new UUID(1, 0), new UUID(1, 1) ); } // https://github.com/bwaldvogel/mongo-java-server/issues/70 @Test public void testDistinctArrayField() throws Exception { collection.insertOne(json("_id: 1, n: null")); collection.insertOne(json("_id: 2").append("n", Arrays.asList(1, 2, 3))); collection.insertOne(json("_id: 3").append("n", Arrays.asList(3, 4, 5))); collection.insertOne(json("_id: 4").append("n", 6)); assertThat(collection.distinct("n", Integer.class)) .containsExactly(null, 1, 2, 3, 4, 5, 6); } @Test public void testDistinct_documentArray() throws Exception { collection.insertOne(json("_id: 1, n: null")); collection.insertOne(json("_id: 2, n: [{item: 1}, {item: 2}]")); collection.insertOne(json("_id: 3, n: {item: 3}")); collection.insertOne(json("_id: 4, n: {item: [4, 5]}")); collection.insertOne(json("_id: 5, n: {}")); assertThat(collection.distinct("n.item", Integer.class)) .containsExactly(1, 2, 3, 4, 5); } // https://github.com/bwaldvogel/mongo-java-server/issues/178 @Test public void testDistinct_missingCollection() throws Exception { MongoCollection<Document> missingCollection = db.getCollection("does-not-exist"); assertThat(missingCollection.distinct("x", Integer.class)).isEmpty(); } @Test public void testInsertQueryAndSortBinaryTypes() throws Exception { byte[] highBytes = new byte[16]; Arrays.fill(highBytes, (byte) 0xFF); collection.insertOne(json("_id: 1, n: null")); collection.insertOne(json("_id: 2").append("n", new UUID(0, 1))); collection.insertOne(json("_id: 3").append("n", new UUID(1, 0))); collection.insertOne(json("_id: 4, n: 'abc'")); collection.insertOne(json("_id: 5, n: 17")); collection.insertOne(json("_id: 6, n: [1, 2, 3]")); collection.insertOne(json("_id: 7").append("n", new byte[] { 0, 0, 0, 1 })); collection.insertOne(json("_id: 8").append("n", highBytes)); collection.insertOne(json("_id: 9").append("n", new byte[0])); assertThat(collection.find(json("n: {$type: 5}")).sort(json("n: 1"))) .containsExactly( json("_id: 9").append("n", new Binary(new byte[0])), json("_id: 7").append("n", new Binary(new byte[] { 0, 0, 0, 1 })), json("_id: 8").append("n", new Binary(highBytes)), json("_id: 2").append("n", new UUID(0, 1)), json("_id: 3").append("n", new UUID(1, 0)) ); assertThat(collection.find(new Document("n", new UUID(1, 0)))) .containsExactly( json("_id: 3").append("n", new UUID(1, 0)) ); assertThat(collection.find(json("")).sort(json("n: 1"))) .containsExactly( json("_id: 1, n: null"), json("_id: 6, n: [1, 2, 3]"), json("_id: 5, n: 17"), json("_id: 4, n: 'abc'"), json("_id: 9").append("n", new Binary(new byte[0])), json("_id: 7").append("n", new Binary(new byte[] { 0, 0, 0, 1 })), json("_id: 8").append("n", new Binary(highBytes)), json("_id: 2").append("n", new UUID(0, 1)), json("_id: 3").append("n", new UUID(1, 0)) ); } @Test public void testUuidAsId() throws Exception { collection.insertOne(new Document("_id", new UUID(0, 1))); collection.insertOne(new Document("_id", new UUID(0, 2))); collection.insertOne(new Document("_id", new UUID(999999, 128))); assertMongoWriteException(() -> collection.insertOne(new Document("_id", new UUID(0, 1))), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: UUID(\"00000000-0000-0000-0000-000000000001\") }"); assertMongoWriteException(() -> collection.insertOne(new Document("_id", new UUID(999999, 128))), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: UUID(\"00000000-000f-423f-0000-000000000080\") }"); collection.deleteOne(new Document("_id", new UUID(0, 2))); assertThat(collection.find(json(""))) .containsExactlyInAnyOrder( new Document("_id", new UUID(0, 1)), new Document("_id", new UUID(999999, 128)) ); } @Test public void testTypeMatching() throws Exception { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 'abc'")); collection.insertOne(json("a: {b: {c: 123}}")); collection.insertOne(json("_id: {'$numberDecimal': '2'}")); assertThat(collection.find(json("_id: {$type: 2.0}"))) .containsExactly(json("_id: 'abc'")); assertThat(collection.find(json("_id: {$type: [16, 'string']}"))) .containsExactlyInAnyOrder( json("_id: 1"), json("_id: 'abc'") ); assertThat(collection.find(json("_id: {$type: 'number'}"))) .containsExactlyInAnyOrder( json("_id: 1"), json("_id: {'$numberDecimal': '2'}") ); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("n: {$type: []}")).first()) .withMessageContaining("Query failed with error code 9 with name 'FailedToParse' and error message 'n must match at least one type'"); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("'a.b.c': {$type: []}")).first()) .withMessageContaining("Query failed with error code 9 with name 'FailedToParse' and error message 'a.b.c must match at least one type'"); assertThat(collection.find(json("a: {b: {$type: []}}"))).isEmpty(); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("n: {$type: 'abc'}")).first()) .withMessageContaining("Query failed with error code 2 with name 'BadValue' and error message 'Unknown type name alias: abc'"); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("n: {$type: null}")).first()) .withMessageContaining("Query failed with error code 14 with name 'TypeMismatch' and error message 'type must be represented as a number or a string'"); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("_id: {$type: 16.3}")).first()) .withMessageContaining("Query failed with error code 2 with name 'BadValue' and error message 'Invalid numerical type code: 16.3'"); } @Test public void testDistinctQueryWithDot() { collection.insertOne(json("a: {b: 1}")); collection.insertOne(json("a: {b: 1}")); collection.insertOne(json("a: {b: 1}")); collection.insertOne(json("a: {b: 2}")); collection.insertOne(json("a: {b: 3}")); collection.insertOne(json("a: {b: null}")); collection.insertOne(json("a: null")); assertThat(collection.distinct("a.b", Integer.class)) .containsExactly(null, 1, 2, 3); assertThat(collection.distinct("a.c", Integer.class)).isEmpty(); } @Test public void testDropCollection() throws Exception { collection.createIndex(new Document("n", 1)); collection.createIndex(new Document("b", 1)); collection.insertOne(json("")); assertThat(db.listCollectionNames()).contains(getCollectionName()); assertThat(collection.listIndexes()) .containsExactlyInAnyOrder( json("key: {_id: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "_id_").append("v", 2), json("key: {n: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "n_1").append("v", 2), json("key: {b: 1}").append("ns", collection.getNamespace().getFullName()).append("name", "b_1").append("v", 2) ); collection.drop(); assertThat(db.listCollectionNames()).doesNotContain(getCollectionName()); assertThat(collection.listIndexes()).isEmpty(); } @Test public void testDropCollectionAlsoDropsFromDB() throws Exception { collection.insertOne(json("")); collection.drop(); assertThat(collection.countDocuments()).isZero(); assertThat(db.listCollectionNames()).doesNotContain(getCollectionName()); } @Test public void testDropDatabaseAlsoDropsCollectionData() throws Exception { collection.insertOne(json("")); db.drop(); assertThat(collection.countDocuments()).isZero(); } @Test public void testDropDatabaseDropsAllData() throws Exception { collection.insertOne(json("_id: 1")); MongoCollection<Document> collection2 = getCollection("testcoll2"); collection2.insertOne(json("_id: 1")); db.drop(); assertThat(listDatabaseNames()).doesNotContain(db.getName()); assertThat(collection.countDocuments()).isZero(); assertThat(db.listCollectionNames()).doesNotContain(getCollectionName(), collection2.getNamespace().getCollectionName()); collection.insertOne(json("_id: 1")); collection2.insertOne(json("_id: 1")); } // https://github.com/bwaldvogel/mongo-java-server/issues/136 @Test public void testDropEmptyDatabase() throws Exception { String emptyDatabaseName = "empty-db"; MongoDatabase database = syncClient.getDatabase(emptyDatabaseName); database.drop(); assertThat(syncClient.listDatabaseNames()).doesNotContain(emptyDatabaseName); } // https://github.com/bwaldvogel/mongo-java-server/issues/107 @Test public void testDropDatabaseAfterAddingIndexMultipleTimes() throws Exception { collection.insertOne(json("_id: 1, a: 10")); for (int i = 0; i < 3; i++) { collection.createIndex(json("a: 1"), new IndexOptions().unique(true)); } db.drop(); } // https://github.com/bwaldvogel/mongo-java-server/issues/107 @Test public void testAddIndexAgainWithDifferentOptions() throws Exception { collection.insertOne(json("_id: 1, a: 10")); collection.createIndex(json("a: 1"), new IndexOptions().unique(true)); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.createIndex(json("a: 1"), new IndexOptions().unique(true).sparse(true))) .withMessageContaining("Command failed with error 85 (IndexOptionsConflict): 'Index with name: a_1 already exists with different options'"); } @Test public void testEmbeddedSort() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4, counts: {done: 1}")); collection.insertOne(json("_id: 5, counts: {done: 2}")); assertThat(collection.find(ne("c", true)).sort(json("'counts.done': -1, _id: 1"))) .containsExactly( json("_id: 5, counts: {done: 2}"), json("_id: 4, counts: {done: 1}"), json("_id: 1"), json("_id: 2"), json("_id: 3") ); } @Test public void testEmbeddedSort_arrayOfDocuments() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2, counts: {done: 1}")); collection.insertOne(json("_id: 3, counts: {done: 2}")); collection.insertOne(json("_id: 4, counts: [{done: 2}, {done: 1}]")); collection.insertOne(json("_id: 5, counts: [{done: 4}, {done: 2}]")); collection.insertOne(json("_id: 6, counts: {done: [3]}")); collection.insertOne(json("_id: 7, counts: {done: [1, 2]}")); collection.insertOne(json("_id: 8, counts: [1, 2]")); assertThat(collection.find(json("")).sort(json("\"counts.done\": -1, _id: 1"))) .containsExactly( json("_id: 5, counts: [{done: 4}, {done: 2}]"), json("_id: 6, counts: {done: [3]}"), json("_id: 3, counts: {done: 2}"), json("_id: 4, counts: [{done: 2}, {done: 1}]"), json("_id: 7, counts: {done: [1, 2]}"), json("_id: 2, counts: {done: 1}"), json("_id: 1"), json("_id: 8, counts: [1, 2]") ); } @Test public void testFindAndModifyCommandEmpty() throws Exception { Document cmd = new Document("findandmodify", getCollectionName()); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> db.runCommand(cmd)) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Either an update or remove=true must be specified'"); } @Test public void testFindAndModifyCommandIllegalOp() throws Exception { collection.insertOne(json("_id: 1")); Document cmd = new Document("findAndModify", getCollectionName()); cmd.put("query", json("_id: 1")); cmd.put("update", new Document("$inc", json("_id: 1"))); assertThat(collection.find().first()).isEqualTo(json("_id: 1")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> db.runCommand(cmd)) .withMessageContaining("Command failed with error 66 (ImmutableField): 'Performing an update on the path '_id' would modify the immutable field '_id'"); } @Test public void testFindAndModifyCommandUpdate() throws Exception { collection.insertOne(json("_id: 1")); Document cmd = new Document("findAndModify", getCollectionName()); cmd.put("query", json("_id: 1")); cmd.put("update", json("$inc: {a: 1}")); Document result = db.runCommand(cmd); assertThat(result.get("lastErrorObject")).isEqualTo(json("updatedExisting: true, n: 1")); assertThat(result.getDouble("ok")).isEqualTo(1.0); assertThat(collection.find().first()).isEqualTo(json("_id: 1, a: 1")); } // https://github.com/bwaldvogel/mongo-java-server/issues/75 @Test public void testFindAndModifyCommand_UpdateSameFields() throws Exception { collection.insertOne(json("_id: 1")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate(json("_id: 1"), json("$inc: {x: 0, a: 1}, $set: {a: 2}"))) .withMessageContaining("Command failed with error 40 (ConflictingUpdateOperators): 'Updating the path 'a' would create a conflict at 'a'"); } // https://github.com/bwaldvogel/mongo-java-server/issues/75 @Test public void testFindAndModifyCommand_UpdateFieldAndItsSubfield() throws Exception { collection.insertOne(json("_id: 1, a: {b: {c: 1}}")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate(json("_id: 1"), json("$set: {'x': 1, 'a.b': {c: 1}}, $inc: {'a.b.c': 1}"))) .withMessageContaining("Command failed with error 40 (ConflictingUpdateOperators): 'Updating the path 'a.b.c' would create a conflict at 'a.b'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate(json("_id: 1"), json("$set: {'x': 1, 'a.b.c': 1}, $unset: {'a.b': 1}"))) .withMessageContaining("Command failed with error 40 (ConflictingUpdateOperators): 'Updating the path 'a.b' would create a conflict at 'a.b'"); } @Test public void testFindOneAndUpdateError() throws Exception { collection.insertOne(json("_id: 1, a: 1")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate(json("_id: 1"), json("$inc: {_id: 1}"))) .withMessageContaining("Command failed with error 66 (ImmutableField): 'Performing an update on the path '_id' would modify the immutable field '_id'"); } @Test public void testFindOneAndUpdateFields() throws Exception { collection.insertOne(json("_id: 1, a: 1")); Document result = collection.findOneAndUpdate(json("_id: 1"), json("$inc: {a: 1}"), new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 1, a: 2")); } @Test public void testFineOneAndUpdateNotFound() throws Exception { collection.insertOne(json("_id: 1, a: 1")); Document result = collection.findOneAndUpdate(json("_id: 2"), new Document("$inc", json("a: 1"))); assertThat(result).isNull(); assertThat(collection.countDocuments()).isEqualTo(1); } @Test public void testFineOneAndUpdateRemove() { collection.insertOne(json("_id: 1, a: 1")); Document result = collection.findOneAndDelete(json("_id: 1")); assertThat(result).isEqualTo(json("_id: 1, a: 1")); assertThat(collection.countDocuments()).isZero(); } // https://github.com/foursquare/fongo/issues/32 @Test public void testFineOneAndUpdateReturnNew() { collection.insertOne(json("_id: 1, a: 1, b: {c: 1}")); Document query = json("_id: 1"); Document update = json("$inc: {a: 1, 'b.c': 1}"); Document result = collection.findOneAndUpdate(query, update, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 1, a: 2, b: {c: 2}")); } @Test public void testFineOneAndUpdateMax() { collection.insertOne(json("_id: 1, a: 1, b: {c: 1}")); Document query = json("_id: 1"); Document update = json("$max: {a: 2, 'b.c': 2, d : 'd'}"); Document result = collection.findOneAndUpdate(query, update, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 1, a: 2, b: {c: 2}, d : 'd'")); } @Test public void testFineOneAndUpdateMin() { collection.insertOne(json("_id: 1, a: 2, b: {c: 1}")); Document query = json("_id: 1"); Document update = json("$min: {a: 1, 'b.c': 2, d : 'd'}"); Document result = collection.findOneAndUpdate(query, update, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 1, a: 1, b: {c: 1}, d : 'd'")); } // https://github.com/foursquare/fongo/issues/32 @Test public void testFindOneAndUpdateReturnOld() { collection.insertOne(json("_id: 1, a: 1, b: {c: 1}")); Document query = json("_id: 1"); Document update = json("$inc: {a: 1, 'b.c': 1}"); Document result = collection.findOneAndUpdate(query, update, new FindOneAndUpdateOptions().returnDocument(ReturnDocument.BEFORE)); assertThat(result).isEqualTo(json("_id: 1, a: 1, b: {c: 1}")); assertThat(collection.find(query).first()).isEqualTo(json("_id: 1, a: 2, b: {c: 2}")); } @Test public void testFindOneAndUpdateSorted() throws Exception { collection.insertOne(json("_id: 1, a: 15")); collection.insertOne(json("_id: 2, a: 10")); collection.insertOne(json("_id: 3, a: 20")); Document order = json("a: 1"); Document result = collection.findOneAndUpdate(json(""), json("$inc: {a: 1}"), new FindOneAndUpdateOptions().sort(order).returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 2, a: 11")); order = json("a: -1"); result = collection.findOneAndUpdate(json(""), json("$inc: {a: 1}"), new FindOneAndUpdateOptions().sort(order).returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 3, a: 21")); } @Test public void testFindOneAndUpdateUpsert() { Document result = collection.findOneAndUpdate(json("_id: 1"), json("$inc: {a: 1}"), new FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER)); assertThat(result).isEqualTo(json("_id: 1, a: 1")); assertThat(collection.find().first()).isEqualTo(json("_id: 1, a: 1")); } @Test public void testFindOneAndUpdateUpsertReturnBefore() { Document result = collection.findOneAndUpdate(json("_id: 1"), json("$inc: {a: 1}"), new FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.BEFORE)); assertThat(result).isNull(); assertThat(collection.find().first()).isEqualTo(json("_id: 1, a: 1")); } // https://github.com/bwaldvogel/mongo-java-server/issues/60 @Test public void testFindOneAndUpdateWithArrayFilters() { collection.insertOne(json("_id: 1, grades: [95, 102, 90, 150]")); collection.insertOne(json("_id: 2, values: [[1, 2, 3], 'other']")); collection.insertOne(json("_id: 3, a: {b: [1, 2, 3]}")); collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.$[element]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: {$gte: 100}")))); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, grades: [95, 'abc', 90, 'abc']")); collection.findOneAndUpdate( json("_id: 1"), json("$unset: {'grades.$[element]': 1}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: 'abc'")))); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, grades: [95, null, 90, null]")); collection.findOneAndUpdate( json("_id: 1"), json("$inc: {'grades.$[element]': 1}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: 90")))); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, grades: [95, null, 91, null]")); collection.findOneAndUpdate( json("_id: 2"), json("$pull: {'values.$[element]': 2}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: {$type: 'array'}")))); assertThat(collection.find(json("_id: 2")).first()) .isEqualTo(json("_id: 2, values: [[1, 3], 'other']")); collection.findOneAndUpdate( json("_id: 3"), json("$mul: {'a.b.$[element]': 10}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: 2")))); assertThat(collection.find(json("_id: 3")).first()) .isEqualTo(json("_id: 3, a: {b: [1, 20, 3]}")); } // https://github.com/bwaldvogel/mongo-java-server/issues/60 @Test public void testUpdateManyWithArrayFilters() { collection.insertOne(json("_id: 1, values: [9, 102, 90, 150]")); collection.insertOne(json("_id: 2, values: [1, 2, 3, 50]")); collection.updateMany( json(""), json("$set: {'values.$[x]': 20}"), new UpdateOptions().arrayFilters(Arrays.asList(json("x: {$gt: 20}"))) ); assertThat(collection.find(json(""))) .containsExactly( json("_id: 1, values: [9, 20, 20, 20]"), json("_id: 2, values: [1, 2, 3, 20]") ); } // https://github.com/bwaldvogel/mongo-java-server/issues/60 @Test public void testUpdateOneWithArrayFilter() throws Exception { collection.insertOne(json("_id: 1, values: [{name: 'A', active: false}, {name: 'B', active: false}]")); collection.updateOne(json("_id: 1"), json("$set: {'values.$[elem].active': true}"), new UpdateOptions().arrayFilters(Arrays.asList(json("'elem.name': {$in: ['A']}"))) ); assertThat(collection.find(json(""))) .containsExactly( json("_id: 1, values: [{name: 'A', active: true}, {name: 'B', active: false}]") ); } // https://github.com/bwaldvogel/mongo-java-server/issues/60 @Test public void testUpsertWithArrayFilters() { collection.updateOne( json("_id: 1, values: [0, 1]"), json("$set: {'values.$[x]': 20}"), new UpdateOptions() .upsert(true) .arrayFilters(Arrays.asList(json("x: 0"))) ); assertThat(collection.find(json(""))) .containsExactly( json("_id: 1, values: [20, 1]") ); } // https://github.com/bwaldvogel/mongo-java-server/issues/60 @Test public void testUpdateWithMultipleArrayFilters() throws Exception { collection.insertOne(json("_id: 1, values: [9, 102, 90, 150]")); collection.insertOne(json("_id: 2, values: [1, 2, 30, 50]")); collection.updateMany( json(""), json("$set: {'values.$[tooLow]': 10, 'values.$[tooHigh]': 40}"), new UpdateOptions().arrayFilters(Arrays.asList( json("tooLow: {$lte: 10}"), json("tooHigh: {$gt: 40}") )) ); assertThat(collection.find(json(""))) .containsExactly( json("_id: 1, values: [10, 40, 40, 40]"), json("_id: 2, values: [10, 10, 30, 40]") ); } // https://github.com/bwaldvogel/mongo-java-server/issues/86 @Test public void testUpdateWithMultipleComplexArrayFilters() throws Exception { collection.insertOne(json("_id: 1, products: [" + "{id: 1, charges: [" + "{type: 'A', min: 0, max: 1}, " + "{type: 'A', min: 0, max: 2}, " + "{type: 'B', min: 0, max: 1}, " + "]}, " + "{id: 2, charges: [{type: 'A', min: 0, max: 1}, ]}, " + "]")); collection.updateMany( json(""), json("$set: {'products.$[product].charges.$[charge].amount': 10}"), new UpdateOptions().arrayFilters(Arrays.asList( json("'product.id': 1"), json("'charge.type': 'A', 'charge.min': 0, 'charge.max': 2") )) ); assertThat(collection.find(json(""))) .containsExactly( json("_id: 1, products: [" + "{id: 1, charges: [" + "{type: 'A', min: 0, max: 1}, " + "{type: 'A', min: 0, max: 2, amount: 10}, " + "{type: 'B', min: 0, max: 1}, " + "]}, " + "{id: 2, charges: [{type: 'A', min: 0, max: 1}, ]}, " + "]") ); } @Test public void testFindOneAndUpdate_IllegalArrayFilters() { collection.insertOne(json("_id: 1, grades: 'abc', a: {b: 123}")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: {$gte: 100}"))))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'The array filter for identifier 'element' was not used in the update { $set: { grades: \"abc\" } }'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList( json("element: {$gte: 100}"), json("element: {$lt: 100}") )))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Found multiple array filters with the same top-level field name element'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.$[element]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("a: {$gte: 100}, b: {$gte: 100}, c: {$gte: 10}"))))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Error parsing array filter :: caused by :: Expected a single top-level field name, found 'a' and 'b'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.$[element]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json(""))))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Cannot use an expression without a top-level field name in arrayFilters'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.$[element]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: {$gte: 100}"))))) .withMessageContaining("Command failed with error 2 (BadValue): 'Cannot apply array updates to non-array element grades: \"abc\"'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'$[element]': 10}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: 2"))))) .withMessageContaining("Command failed with error 2 (BadValue): 'Cannot have array filter identifier (i.e. '$[<id>]') element in the first position in path '$[element]'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.subGrades.$[element]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("element: {$gte: 100}"))))) .withMessageContaining("Command failed with error 2 (BadValue): 'The path 'grades.subGrades' must exist in the document in order to apply array updates.'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades.$[some value]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("'some value': {$gte: 100}"))))) .withMessageContaining("Command failed with error 2 (BadValue): 'Error parsing array filter :: caused by :: The top-level field name must be an alphanumeric string beginning with a lowercase letter, found 'some value''"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'a.b.$[x]': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("x: {$gte: 100}"))))) .withMessageContaining("Command failed with error 2 (BadValue): 'Cannot apply array updates to non-array element b: 123'"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("'a.b': 10, b: 12"))))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Error parsing array filter :: caused by :: Expected a single top-level field name, found 'a' and 'b''"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'grades': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList( json("'a.b': 10"), json("'a.c': 10") )))) .withMessageContaining("Command failed with error 9 (FailedToParse): 'Found multiple array filters with the same top-level field name a'"); } @Test public void testFindOneAndUpdate_IllegalArrayFiltersPaths() { collection.insertOne(json("_id: 1, grades: 'abc', a: {b: [1, 2, 3]}")); collection.insertOne(json("_id: 2, grades: 'abc', a: {b: [{c: 1}, {c: 2}, {c: 3}]}")); collection.insertOne(json("_id: 3, grades: 'abc', a: {b: [[[1, 2], [3, 4]]]}")); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'a.b.$[x].c': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("x: {$gt: 1}"))))) .withMessageContaining("Command failed with error 28 (PathNotViable): 'Cannot create field 'c' in element {1: 2}"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 1"), json("$set: {'a.b.$[x].c.d': 'abc'}"), new FindOneAndUpdateOptions().arrayFilters(Arrays.asList(json("x: {$gt: 1}"))))) .withMessageContaining("Command failed with error 28 (PathNotViable): 'Cannot create field 'c' in element {1: 2}"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 2"), json("$set: {'a.b.$[].c.$[]': 'abc'}"))) .withMessageContaining("Command failed with error 2 (BadValue): 'Cannot apply array updates to non-array element c: 1"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 3"), json("$set: {'a.b.$[].0.c': 'abc'}"))) .withMessageContaining("Command failed with error 28 (PathNotViable): 'Cannot create field 'c' in element {0: [ 1, 2 ]}"); assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> collection.findOneAndUpdate( json("_id: 3"), json("$set: {'a.b.$[].0.$[].c': 'abc'}"))) .withMessageContaining("Command failed with error 28 (PathNotViable): 'Cannot create field 'c' in element {0: 1}"); } @Test public void testFindAndRemoveFromEmbeddedList() { collection.insertOne(json("_id: 1, a: [1]")); Document result = collection.findOneAndDelete(json("_id: 1")); assertThat(result).isEqualTo(json("_id: 1, a: [1]")); assertThat(collection.countDocuments()).isZero(); } @Test public void testFindOne() { collection.insertOne(json("key: 'value'")); collection.insertOne(json("key: 'value'")); Document result = collection.find().first(); assertThat(result).isNotNull(); assertThat(result.get("_id")).isNotNull(); } @Test public void testFindOneById() { collection.insertOne(json("_id: 1")); Document result = collection.find(json("_id: 1")).first(); assertThat(result).isEqualTo(json("_id: 1")); assertThat(collection.find(json("_id: 2")).first()).isNull(); } @Test public void testFindOneIn() { collection.insertOne(json("_id: 1")); Document result = collection.find(json("_id: {$in: [1, 2]}")).first(); assertThat(result).isEqualTo(json("_id: 1")); } @Test public void testFindWithLimit() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); assertThat(collection.find().sort(json("_id: 1")).limit(2)) .containsExactly( json("_id: 1"), json("_id: 2") ); assertThat(collection.find().sort(json("_id: 1")).limit(-2)) .containsExactly( json("_id: 1"), json("_id: 2") ); } @Test public void testFindInReverseNaturalOrder() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); assertThat(collection.find().sort(json("$natural: -1"))) .containsExactly( json("_id: 2"), json("_id: 1") ); } @Test public void testFindWithPattern() { collection.insertOne(json("_id: 'marta'")); collection.insertOne(json("_id: 'john', foo: 'bar'")); collection.insertOne(json("_id: 'jon', foo: 'ba'")); collection.insertOne(json("_id: 'jo'")); assertThat(collection.find(new Document("_id", Pattern.compile("mart")))) .containsExactly(json("_id: 'marta'")); assertThat(collection.find(new Document("foo", Pattern.compile("ba")))) .containsExactly(json("_id: 'john', foo: 'bar'"), json("_id: 'jon', foo: 'ba'")); assertThat(collection.find(new Document("foo", Pattern.compile("ba$")))) .containsExactly(json("_id: 'jon', foo: 'ba'")); } @Test public void testFindWithQuery() { collection.insertOne(json("name: 'jon'")); collection.insertOne(json("name: 'leo'")); collection.insertOne(json("name: 'neil'")); collection.insertOne(json("name: 'neil'")); assertThat(collection.find(json("name: 'neil'"))).hasSize(2); } @Test public void testFindWithSkipLimit() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); assertThat(collection.find().sort(json("_id: 1")).limit(2).skip(2)) .containsExactly(json("_id: 3"), json("_id: 4")); } @Test public void testFindWithSkipLimitInReverseOrder() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); assertThat(collection.find().sort(json("_id: -1")).limit(2).skip(2)) .containsExactly(json("_id: 2"), json("_id: 1")); } @Test public void testFindWithSkipLimitAfterDelete() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); collection.insertOne(json("_id: 5")); collection.deleteOne(json("_id: 1")); collection.deleteOne(json("_id: 3")); assertThat(collection.find().sort(json("_id: 1")).limit(2).skip(2)) .containsExactly(json("_id: 5")); } @Test public void testFullUpdateWithSameId() throws Exception { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2, b: 5")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); collection.replaceOne(json("_id: 2, b: 5"), json("_id: 2, a: 5")); assertThat(collection.find(json("_id: 2")).first()).isEqualTo(json("_id: 2, a: 5")); } @Test public void testGetCollection() { MongoCollection<Document> collection = getCollection("coll"); getCollection("coll").insertOne(json("")); assertThat(collection).isNotNull(); assertThat(db.listCollectionNames()).contains("coll"); } @Test public void testNullId() throws Exception { collection.insertOne(json("_id: null, name: 'test'")); Document result = collection.find(json("name: 'test'")).first(); assertThat(result).isNotNull(); assertThat(result.getObjectId(Constants.ID_FIELD)).isNull(); assertMongoWriteException(() -> collection.insertOne(json("_id: null")), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: null }"); assertThat(collection.countDocuments()).isEqualTo(1); assertThat(collection.find(json("_id: null")).first()).isEqualTo(json("_id: null, name: 'test'")); collection.deleteOne(json("_id: null")); assertThat(collection.countDocuments()).isZero(); } @Test public void testIdInQueryResultsInIndexOrder() { collection.insertOne(json("_id: 4")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); assertThat(collection.find(json("_id: {$in: [3, 2, 1]}"))) .containsExactlyInAnyOrder( json("_id: 1"), json("_id: 2"), json("_id: 3") ); // https://github.com/bwaldvogel/mongo-java-server/issues/57 assertThat(collection.find(json("_id: {$in: [[1, 2, 3]]}"))) .isEmpty(); } @Test public void testInQuery_Arrays() throws Exception { collection.insertOne(json("_id: 1, v: [1, 2, 3]")); collection.insertOne(json("_id: 2, v: [1, 2]")); collection.insertOne(json("_id: 3, v: 50")); collection.insertOne(json("_id: 4, v: null")); collection.insertOne(json("_id: 5")); assertThat(collection.find(json("v: {$in: [[1, 2, 3], 50]}"))) .containsExactly( json("_id: 1, v: [1, 2, 3]"), json("_id: 3, v: 50") ); assertThat(collection.find(json("v: {$not: {$in: [[1, 2, 3], 50]}}"))) .containsExactlyInAnyOrder( json("_id: 2, v: [1, 2]"), json("_id: 4, v: null"), json("_id: 5") ); assertThat(collection.find(json("v: {$not: {$in: [2, 50]}}"))) .containsExactlyInAnyOrder( json("_id: 4, v: null"), json("_id: 5") ); assertThat(collection.find(json("v: {$not: {$in: [[1, 2], 50, null]}}"))) .containsExactly( json("_id: 1, v: [1, 2, 3]") ); } @Test public void testIdNotAllowedToBeUpdated() { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.replaceOne(json("_id: 1"), json("_id: 2, a: 4")), 66, "ImmutableField", "After applying the update, the (immutable) field '_id' was found to have been altered to _id: 2"); // test with $set assertMongoWriteException(() -> collection.updateOne(json("_id: 1"), new Document("$set", json("_id: 2"))), 66, "ImmutableField", "Performing an update on the path '_id' would modify the immutable field '_id'"); } @Test public void testIllegalCommand() throws Exception { assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> db.runCommand(json("foo: 1"))) .withMessageContaining("Command failed with error 59 (CommandNotFound): 'no such command: 'foo'"); } @Test public void testInsert() throws Exception { assertThat(collection.countDocuments()).isEqualTo(0); for (int i = 0; i < 3; i++) { collection.insertOne(new Document("_id", Integer.valueOf(i))); } assertThat(collection.countDocuments()).isEqualTo(3); collection.insertOne(json("foo: [1, 2, 3]")); collection.insertOne(new Document("foo", new byte[10])); Document insertedObject = new Document("foo", UUID.randomUUID()); collection.insertOne(insertedObject); Document document = collection.find(insertedObject).first(); assertThat(document).isEqualTo(insertedObject); } @Test public void testInsertDuplicate() throws Exception { assertThat(collection.countDocuments()).isEqualTo(0); collection.insertOne(json("_id: 1")); assertThat(collection.countDocuments()).isEqualTo(1); assertMongoWriteException(() -> collection.insertOne(json("_id: 1.0")), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: 1.0 }"); assertThat(collection.countDocuments()).isEqualTo(1); } @Test public void testInsertDuplicateThrows() { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.insertOne(json("_id: 1")), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: 1 }"); } @Test public void testInsertDuplicateWithConcernThrows() { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.withWriteConcern(WriteConcern.ACKNOWLEDGED).insertOne(json("_id: 1")), 11000, "DuplicateKey", "E11000 duplicate key error collection: testdb.testcoll index: _id_ dup key: { _id: 1 }"); } @Test public void testInsertIncrementsCount() { assertThat(collection.countDocuments()).isZero(); collection.insertOne(json("key: 'value'")); assertThat(collection.countDocuments()).isEqualTo(1); } @Test public void testInsertQuery() throws Exception { assertThat(collection.countDocuments()).isEqualTo(0); Document insertedObject = json("_id: 1"); insertedObject.put("foo", "bar"); collection.insertOne(insertedObject); assertThat(collection.find(insertedObject).first()).isEqualTo(insertedObject); assertThat(collection.find(new Document("_id", Long.valueOf(1))).first()).isEqualTo(insertedObject); assertThat(collection.find(new Document("_id", Double.valueOf(1.0))).first()).isEqualTo(insertedObject); assertThat(collection.find(new Document("_id", Float.valueOf(1.0001f))).first()).isNull(); assertThat(collection.find(json("foo: 'bar'")).first()).isEqualTo(insertedObject); } @Test public void testInsertRemove() throws Exception { for (int i = 0; i < 10; i++) { collection.insertOne(json("_id: 1")); assertThat(collection.countDocuments()).isEqualTo(1); collection.deleteOne(json("_id: 1")); assertThat(collection.countDocuments()).isZero(); collection.insertOne(new Document("_id", i)); collection.deleteOne(new Document("_id", i)); } assertThat(collection.countDocuments()).isZero(); collection.deleteOne(json("'doesnt exist': 1")); assertThat(collection.countDocuments()).isZero(); } @Test public void testInsertInSystemNamespace() throws Exception { assertMongoWriteException(() -> getCollection("system.foobar").insertOne(json("")), 16459, "attempt to insert in system namespace"); assertMongoWriteException(() -> getCollection("system.namespaces").insertOne(json("")), 16459, "attempt to insert in system namespace"); } @Test public void testListDatabaseNames() throws Exception { assertThat(listDatabaseNames()).isEmpty(); collection.insertOne(json("")); assertThat(listDatabaseNames()).containsExactly(db.getName()); getDatabase().getCollection("some-collection").insertOne(json("")); assertThat(listDatabaseNames()).containsExactly("bar", db.getName()); } private MongoDatabase getDatabase() { return syncClient.getDatabase(OTHER_TEST_DATABASE_NAME); } @Test public void testQuery() throws Exception { Document obj = collection.find(json("_id: 1")).first(); assertThat(obj).isNull(); assertThat(collection.countDocuments()).isEqualTo(0); } @Test public void testQueryAll() throws Exception { List<Document> inserted = new ArrayList<>(); for (int i = 0; i < 10; i++) { Document obj = new Document("_id", i); collection.insertOne(obj); inserted.add(obj); } assertThat(collection.countDocuments()).isEqualTo(10); assertThat(collection.find().sort(json("_id: 1"))) .containsExactlyElementsOf(inserted); } @Test public void testQueryCount() throws Exception { for (int i = 0; i < 100; i++) { collection.insertOne(json("")); } assertThat(collection.countDocuments()).isEqualTo(100); Document obj = json("_id: 1"); assertThat(collection.countDocuments(obj)).isEqualTo(0); collection.insertOne(obj); assertThat(collection.countDocuments(obj)).isEqualTo(1); } @Test public void testQueryLimitEmptyQuery() throws Exception { for (int i = 0; i < 5; i++) { collection.insertOne(json("")); } assertThat(collection.countDocuments(json(""), new CountOptions().limit(1))).isEqualTo(1); assertThat(collection.countDocuments(json(""), new CountOptions().limit(-1))).isEqualTo(5); assertThat(collection.countDocuments(json(""))).isEqualTo(5); } @Test public void testQueryLimitSimpleQuery() throws Exception { for (int i = 0; i < 5; i++) { collection.insertOne(json("a: 1")); } assertThat(collection.countDocuments(json("a: 1"), new CountOptions().limit(1))).isEqualTo(1); assertThat(collection.countDocuments(json("a: 1"), new CountOptions().limit(-1))).isEqualTo(5); assertThat(collection.countDocuments(json("a: 1"))).isEqualTo(5); } @Test public void testQueryNull() throws Exception { Document object = json("_id: 1"); collection.insertOne(object); assertThat(collection.find(json("foo: null")).first()).isEqualTo(object); } @Test public void testQuerySkipLimitEmptyQuery() throws Exception { assertThat(collection.countDocuments(json(""), new CountOptions().skip(3))).isEqualTo(0); for (int i = 0; i < 10; i++) { collection.insertOne(json("")); } assertThat(collection.countDocuments(json(""), new CountOptions().skip(3))).isEqualTo(7); assertThat(collection.countDocuments(json(""), new CountOptions().skip(15))).isEqualTo(0); assertThat(collection.countDocuments(json(""), new CountOptions().skip(3).limit(5))).isEqualTo(5); } @Test public void testQuerySkipLimitSimpleQuery() throws Exception { assertThat(collection.countDocuments(json("a: 1"), new CountOptions().skip(3))).isEqualTo(0); for (int i = 0; i < 10; i++) { collection.insertOne(json("a: 1")); } assertThat(collection.countDocuments(json("a: 1"), new CountOptions().skip(3))).isEqualTo(7); assertThat(collection.countDocuments(json("a: 1"), new CountOptions().skip(3).limit(5))).isEqualTo(5); assertThat(collection.countDocuments(json("a: 1"), new CountOptions().skip(15).limit(5))).isEqualTo(0); } @Test public void testQuerySort() throws Exception { Random random = new Random(4711); for (int i = 0; i < 10; i++) { collection.insertOne(new Document("_id", Double.valueOf(random.nextDouble()))); } assertThat(collection.find().sort(json("_id: 1"))) .extracting(d -> d.getDouble("_id")) .isSorted(); assertThat(collection.find().sort(json("_id: -1"))) .extracting(d -> d.getDouble("_id")) .isSortedAccordingTo(Comparator.comparingDouble(Double::doubleValue).reversed()); } @Test public void testQueryWithFieldSelector() throws Exception { collection.insertOne(json("foo: 'bar'")); collection.insertOne(json("foo: null")); Document obj = collection.find(json("")).projection(json("foo: 1")).first(); assertThat(obj).containsOnlyKeys("_id", "foo"); obj = collection.find(json("foo: 'bar'")).projection(json("_id: 1")).first(); assertThat(obj).containsOnlyKeys("_id"); obj = collection.find(json("foo: null")).projection(json("_id: 0, foo: 1")).first(); assertThat(obj).isEqualTo(json("foo: null")); obj = collection.find(json("foo: 'bar'")).projection(json("_id: 0, foo: 1")).first(); assertThat(obj).containsOnlyKeys("foo"); } @Test public void testQueryWithDotNotationFieldSelector() throws Exception { collection.insertOne(json("_id: 1, index: false, foo: {a: 'a1', b: 0}")); collection.insertOne(json("_id: 2, foo: {a: null, b: null}")); Document obj = collection.find(json("_id: 1")).projection(json("'foo.a': 1, 'foo.b': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, foo: {a: 'a1', b: 0}")); obj = collection.find(json("_id: 1")).projection(json("'foo.a': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, foo: {a: 'a1'}")); obj = collection.find(json("_id: 1")).projection(json("'foo.a': 1, index: 1, _id: 0")).first(); assertThat(obj).isEqualTo(json("foo: {a: 'a1'}, index: false")); obj = collection.find(json("_id: 1")).projection(json("foo: 1, _id: 0")).first(); assertThat(obj).isEqualTo(json("foo: {a: 'a1', b: 0}")); obj = collection.find(json("_id: 1")).projection(json("'foo.a.b.c.d': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, foo: {}")); obj = collection.find(json("_id: 1")).projection(json("'foo..': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, foo: {}")); obj = collection.find(json("_id: 2")).projection(json("'foo.a.b': 1, 'foo.b': 1, 'foo.c': 1, 'foo.c.d': 1")).first(); assertThat(obj).isEqualTo(json("_id: 2, foo: {b: null}")); obj = collection.find(json("_id: 2")).projection(json("'foo.a': 1")).first(); assertThat(obj).isEqualTo(json("_id: 2, foo: {a: null}")); obj = collection.find(json("_id: 2")).projection(json("'foo.c': 1")).first(); assertThat(obj).isEqualTo(json("_id: 2, foo: {}")); } @Test public void testQueryWithDotNotationFieldSelector_Array() throws Exception { collection.insertOne(json("_id: 1, values: [1, 2, {x: 100, y: 10}, {x: 200}]")); Document obj = collection.find(json("_id: 1")).projection(json("'values.0': 1, 'values.x': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, values: [{x: 100}, {x: 200}]")); obj = collection.find(json("_id: 1")).projection(json("'values.y': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, values: [{y: 10}, {}]")); obj = collection.find(json("_id: 1")).projection(json("'values.x': 1, 'values.y': 1")).first(); assertThat(obj).isEqualTo(json("_id: 1, values: [{x: 100, y: 10}, {x: 200}]")); } @Test public void testQueryWithIllegalFieldSelection() throws Exception { collection.insertOne(json("_id: 1")); assertThatExceptionOfType(MongoQueryException.class) .isThrownBy(() -> collection.find(json("_id: 1")).projection(json("values: {x: 1, y: 1}")).first()) .withMessageContaining("Query failed with error code 2 with name 'BadValue' and error message '>1 field in obj: { x: 1, y: 1 }'"); } @Test public void testQuerySystemNamespace() throws Exception { assertThat(getCollection("system.foobar").find().first()).isNull(); assertThat(db.listCollectionNames()).isEmpty(); collection.insertOne(json("")); Document expectedObj = new Document("name", collection.getNamespace().getFullName()); Document coll = getCollection("system.namespaces").find(expectedObj).first(); assertThat(coll).isEqualTo(expectedObj); } @Test public void testQueryAllExpression() throws Exception { collection.insertOne(json("a: [{x: 1}, {x: 2}]")); collection.insertOne(json("a: [{x: 2}, {x: 3}]")); assertThat(collection.countDocuments(json("'a.x': {$all: [1, 2]}"))).isEqualTo(1); assertThat(collection.countDocuments(json("'a.x': {$all: [2, 3]}"))).isEqualTo(1); } // https://github.com/bwaldvogel/mongo-java-server/issues/36 @Test public void testAndQueryWithAllAndNin() throws Exception { collection.insertOne(json("_id: 1, tags: ['A', 'B']")); collection.insertOne(json("_id: 2, tags: ['A', 'D']")); collection.insertOne(json("_id: 3, tags: ['A', 'C']")); collection.insertOne(json("_id: 4, tags: ['C', 'D']")); assertThat(collection.find(json("$and: [{'tags': {$all: ['A']}}, {'tags': {$nin: ['B', 'C']}}]"))) .containsExactly(json("_id: 2, tags: ['A', 'D']")); } // https://github.com/bwaldvogel/mongo-java-server/issues/96 @Test public void testAndQueryWithAllAndSize() throws Exception { collection.insertOne(json("_id: 1, list: ['A', 'B']")); collection.insertOne(json("_id: 2, list: ['A', 'B', 'C']")); assertThat(collection.find(json("$and: [{list: {$size: 2}}, {list: {$all: ['A', 'B']}}]}"))) .containsExactly(json("_id: 1, list: ['A', 'B']")); assertThat(collection.find(json("list: {$all: ['A', 'B'], $size: 2}"))) .containsExactly(json("_id: 1, list: ['A', 'B']")); } // https://github.com/bwaldvogel/mongo-java-server/issues/36 @Test public void testMatchesAllWithEmptyCollection() throws Exception { collection.insertOne(json("_id: 1, text: 'TextA', tags: []")); collection.insertOne(json("_id: 2, text: 'TextB', tags: []")); collection.insertOne(json("_id: 3, text: 'TextA', tags: ['A']")); assertThat(collection.find(json("$and: [{'text': 'TextA'}, {'tags': {$all: []}}]"))).isEmpty(); } @Test public void testQueryWithSubdocumentIndex() throws Exception { collection.createIndex(json("action: {actionId: 1}"), new IndexOptions().unique(true)); collection.insertOne(json("action: {actionId: 1}, value: 'a'")); collection.insertOne(json("action: {actionId: 2}, value: 'b'")); collection.insertOne(json("action: {actionId: 3}, value: 'c'")); Document foundWithNestedDocument = collection.find(json("action: {actionId: 2}")).first(); assertThat(foundWithNestedDocument.get("value")).isEqualTo("b"); Document foundWithDotNotation = collection.find(json("'action.actionId': 2")).first(); assertThat(foundWithDotNotation.get("value")).isEqualTo("b"); } // see https://github.com/bwaldvogel/mongo-java-server/issues/12 @Test public void testQueryBinaryData() throws Exception { byte[] firstBytes = new byte[] { 0x01, 0x02, 0x03 }; byte[] secondBytes = new byte[] { 0x03, 0x02, 0x01 }; collection.insertOne(new Document("_id", 1).append("test", firstBytes)); collection.insertOne(new Document("_id", 2).append("test", secondBytes)); Document first = collection.find(new Document("test", firstBytes)).first(); assertThat(first).isNotNull(); assertThat(first.get("_id")).isEqualTo(1); Document second = collection.find(new Document("test", secondBytes)).first(); assertThat(second).isNotNull(); assertThat(second.get("_id")).isEqualTo(2); } @Test public void testRemove() { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2")); collection.insertOne(json("_id: 3")); collection.insertOne(json("_id: 4")); collection.deleteOne(json("_id: 2")); assertThat(collection.find(json("_id: 2")).first()).isNull(); assertThat(collection.countDocuments()).isEqualTo(3); collection.deleteMany(json("_id: {$gte: 3}")); assertThat(collection.countDocuments()).isEqualTo(1); assertThat(collection.find().first()).isEqualTo(json("_id: 1")); } @Test public void testRemoveSingle() throws Exception { Document obj = new Document("_id", ObjectId.get()); collection.insertOne(obj); collection.deleteOne(obj); } @Test public void testRemoveReturnsModifiedDocumentCount() { collection.insertOne(json("")); collection.insertOne(json("")); DeleteResult result = collection.deleteMany(json("")); assertThat(result.getDeletedCount()).isEqualTo(2); result = collection.deleteMany(json("")); assertThat(result.getDeletedCount()).isEqualTo(0); } @Test public void testReservedCollectionNames() throws Exception { assertMongoWriteException(() -> getCollection("foo$bar").insertOne(json("")), 10093, "cannot insert into reserved $ collection"); String veryLongString = repeat("verylongstring", 5); assertMongoWriteException(() -> getCollection(veryLongString).insertOne(json("")), 10080, "ns name too long, max size is 128"); } private static String repeat(String str, int num) { String repeated = str; for (int i = 0; i < num; i++) { repeated += repeated; } return repeated; } @Test public void testServerStatus() throws Exception { Document serverStatus = runCommand("serverStatus"); assertThat(serverStatus.getDouble("ok")).isEqualTo(1); assertThat(serverStatus.get("uptime")).isInstanceOf(Number.class); assertThat(serverStatus.get("uptimeMillis")).isInstanceOf(Long.class); Instant serverTime = ((Date) serverStatus.get("localTime")).toInstant(); assertThat(serverTime).isEqualTo(clock.instant()); Document connections = (Document) serverStatus.get("connections"); assertThat(connections.get("current")).isNotNull(); } @Test void testServerStatusWithOpenCursors() throws Exception { for (int i = 0; i < 20; i++) { collection.insertOne(new Document("_id", i + 1)); } try (MongoCursor<Document> cursor1 = collection.find().batchSize(10).cursor(); MongoCursor<Document> cursor2 = collection.find().batchSize(10).cursor()) { log.debug("Opened {} and {}", cursor1.getServerCursor(), cursor2.getServerCursor()); Document serverStatus = runCommand("serverStatus"); assertThat(serverStatus.getDouble("ok")).isEqualTo(1); Document metrics = serverStatus.get("metrics", Document.class); Document cursorMetrics = metrics.get("cursor", Document.class); assertThat(cursorMetrics.getLong("timedOut")).isZero(); Document openCursors = cursorMetrics.get("open", Document.class); assertThat(openCursors.getLong("noTimeout")).isZero(); assertThat(openCursors.getLong("pinned")).isZero(); assertThat(openCursors.getLong("total")).isEqualTo(2); } } @Test public void testPing() throws Exception { assertThat(runCommand("ping").getDouble("ok")).isEqualTo(1.0); assertThat(runCommand(json("ping: true")).getDouble("ok")).isEqualTo(1.0); assertThat(runCommand(json("ping: 2.0")).getDouble("ok")).isEqualTo(1.0); } @Test public void testReplSetGetStatus() throws Exception { assertThatExceptionOfType(MongoCommandException.class) .isThrownBy(() -> runCommand("replSetGetStatus")) .withMessageContaining("Command failed with error 76 (NoReplicationEnabled): 'not running with --replSet'"); } @Test public void testWhatsMyUri() throws Exception { for (String dbName : new String[] { ADMIN_DB_NAME, "local", "test" }) { Document result = syncClient.getDatabase(dbName).runCommand(new Document("whatsmyuri", 1)); assertThat(result.get("you")).isNotNull(); assertThat(result.get("you").toString()).matches("\\d+\\.\\d+\\.\\d+\\.\\d+:\\d*"); } } @Test public void testSortDocuments() throws Exception { collection.insertOne(json("_id: 1, a: {b: 1}")); collection.insertOne(json("_id: 2, a: {b: 2}")); collection.insertOne(json("_id: 3, a: 3")); collection.insertOne(json("_id: 4, a: {c: 1}")); assertThat(collection.find().sort(json("a: 1"))) .containsExactly( json("_id: 3, a: 3"), json("_id: 1, a: {b: 1}"), json("_id: 2, a: {b: 2}"), json("_id: 4, a: {c: 1}") ); assertThat(collection.find().sort(json("a: -1"))) .containsExactly( json("_id: 4, a: {c: 1}"), json("_id: 2, a: {b: 2}"), json("_id: 1, a: {b: 1}"), json("_id: 3, a: 3") ); } @Test public void testSort() { collection.insertOne(json("_id: 1, a: null")); collection.insertOne(json("_id: 2, a: 1")); collection.insertOne(json("_id: 3, a: 2")); collection.insertOne(json("_id: 4")); collection.insertOne(json("_id: 5, a: 3")); collection.insertOne(json("_id: 6, a: 4")); collection.insertOne(json("_id: 7, a: 'abc'")); collection.insertOne(json("_id: 8, a: 'zzz'")); collection.insertOne(json("_id: 9, a: 1.0")); assertThat(collection.find().sort(json("a: 1, _id: 1"))) .containsExactly( json("_id: 1, a: null"), json("_id: 4"), json("_id: 2, a: 1"), json("_id: 9, a: 1.0"), json("_id: 3, a: 2"), json("_id: 5, a: 3"), json("_id: 6, a: 4"), json("_id: 7, a: 'abc'"), json("_id: 8, a: 'zzz'") ); assertThat(collection.find().sort(json("a: -1, _id: 1"))) .containsExactly( json("_id: 8, a: 'zzz'"), json("_id: 7, a: 'abc'"), json("_id: 6, a: 4"), json("_id: 5, a: 3"), json("_id: 3, a: 2"), json("_id: 2, a: 1"), json("_id: 9, a: 1.0"), json("_id: 1, a: null"), json("_id: 4") ); } @Test public void testSortByEmbeddedKey() { collection.insertOne(json("_id: 1, a: {b: 1}")); collection.insertOne(json("_id: 2, a: {b: 2}")); collection.insertOne(json("_id: 3, a: {b: 3}")); assertThat(collection.find().sort(json("'a.b': -1"))) .containsExactly( json("_id: 3, a: {b: 3}"), json("_id: 2, a: {b: 2}"), json("_id: 1, a: {b: 1}") ); } @Test public void testUpdate() throws Exception { Document object = json("_id: 1"); Document newObject = json("_id: 1, foo: 'bar'"); collection.insertOne(object); UpdateResult result = collection.replaceOne(object, newObject); assertThat(result.getModifiedCount()).isEqualTo(1); assertThat(result.getUpsertedId()).isNull(); assertThat(collection.find(object).first()).isEqualTo(newObject); } @Test public void testUpdateNothing() throws Exception { Document object = json("_id: 1"); UpdateResult result = collection.replaceOne(object, object); assertThat(result.getModifiedCount()).isEqualTo(0); assertThat(result.getMatchedCount()).isEqualTo(0); assertThat(result.getUpsertedId()).isNull(); } @Test public void testUpdateBlank() throws Exception { Document document = json("'': 1, _id: 2, a: 3, b: 4"); collection.insertOne(document); collection.updateOne(json(""), json("$set: {c: 5}")); assertThat(collection.find().first()).isEqualTo(json("'': 1, _id: 2, a: 3, b: 4, c: 5")); } @Test public void testUpdateEmptyPositional() throws Exception { collection.insertOne(json("")); assertMongoWriteException(() -> collection.updateOne(json(""), json("$set: {'a.$.b': 1}")), 2, "BadValue", "The positional operator did not find the match needed from the query."); } @Test public void testUpdateMultiplePositional() throws Exception { collection.insertOne(json("a: {b: {c: 1}}")); assertMongoWriteException(() -> collection.updateOne(json("'a.b.c': 1"), json("$set: {'a.$.b.$.c': 1}")), 2, "BadValue", "Too many positional (i.e. '$') elements found in path 'a.$.b.$.c'"); } @Test public void testUpdateIllegalFieldName() throws Exception { // Disallow $ in field names - SERVER-3730 collection.insertOne(json("x: 1")); collection.updateOne(json("x: 1"), json("$set: {y: 1}")); // ok assertMongoWriteException(() -> collection.updateOne(json("x: 1"), json("$set: {$z: 1}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$z' in '$z' is not valid for storage."); // unset ok to remove bad fields collection.updateOne(json("x: 1"), json("$unset: {$z: 1}")); assertMongoWriteException(() -> collection.updateOne(json("x: 1"), json("$inc: {$z: 1}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$z' in '$z' is not valid for storage."); assertMongoWriteException(() -> collection.updateOne(json("x: 1"), json("$push: {$z: [1, 2, 3]}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$z' in '$z' is not valid for storage."); } @Test public void testUpdateSubdocument() throws Exception { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> collection.updateOne(json(""), json("'a.b.c': 123"))) .withMessage("Invalid BSON field name a.b.c"); } @ParameterizedTest @ValueSource(strings = { "a.", "a..", "a.b", "a.b.", "a.....111" }) void testInsertWithSpecialFieldNames(String specialFieldName) throws Exception { Document document = json("_id: 1").append(specialFieldName, 1); collection.insertOne(document); assertThat(collection.find().first()).isEqualTo(document); } @Test public void testUpdateIdNoChange() { collection.insertOne(json("_id: 1")); collection.replaceOne(json("_id: 1"), json("_id: 1, a: 5")); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, a: 5")); collection.updateOne(json("_id: 1"), json("$set: {_id: 1, b: 3}")); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, a: 5, b: 3")); // test with $set collection.updateOne(json("_id: 1"), json("$set: {_id: 1, a: 7}")); assertThat(collection.find(json("_id: 1")).first()).isEqualTo(json("_id: 1, a: 7, b: 3")); } @Test public void testUpdatePush() throws Exception { Document idObj = json("_id: 1"); collection.insertOne(idObj); collection.updateOne(idObj, json("$push: {'field.subfield.subsubfield': 'value'}")); Document expected = json("_id: 1, field: {subfield: {subsubfield: ['value']}}"); assertThat(collection.find(idObj).first()).isEqualTo(expected); // push to non-array collection.updateOne(idObj, json("$set: {field: 'value'}")); assertMongoWriteException(() -> collection.updateOne(idObj, json("$push: {field: 'value'}")), 2, "BadValue", "The field 'field' must be an array but is of type string in document {_id: 1}"); // push with multiple fields Document pushObj = json("$push: {field1: 'value', field2: 'value2'}"); collection.updateOne(idObj, pushObj); expected = json("_id: 1, field: 'value', field1: ['value'], field2: ['value2']"); assertThat(collection.find(idObj).first()).isEqualTo(expected); // push duplicate pushObj = json("$push: {field1: 'value'}"); collection.updateOne(idObj, pushObj); expected.put("field1", Arrays.asList("value", "value")); assertThat(collection.find(idObj).first()).isEqualTo(expected); } @Test public void testUpdatePushEach() throws Exception { collection.insertOne(json("_id: 1")); collection.insertOne(json("_id: 2, value: [0]")); collection.updateMany(json(""), json("$push: {value: {$each: [1, 2, 3, {key: 'value'}]}}")); assertThat(collection.find()) .containsExactlyInAnyOrder( json("_id: 1, value: [1, 2, 3, {key: 'value'}]"), json("_id: 2, value: [0, 1, 2, 3, {key: 'value'}]") ); } @Test public void testUpdatePushSlice() throws Exception { collection.insertOne(json("_id: 1")); collection.updateOne(json(""), json("$push: {value: {$each: ['a', 'b', 'c'], $slice: 4}}")); collection.updateOne(json(""), json("$push: {value: {$each: [1, 2, 3], $slice: 4}}")); assertThat(collection.find()) .containsExactly(json("_id: 1, value: ['a', 'b', 'c', 1]")); } // https://github.com/bwaldvogel/mongo-java-server/issues/166 @Test public void testUpdatePushWithNegativeAndZeroSlice() throws Exception { collection.insertOne(json("_id: 1")); collection.updateOne(json(""), json("$push: {value: {$each: ['a', 'b', 'c'], $slice: -2}}")); assertThat(collection.find()) .containsExactly(json("_id: 1, value: ['b', 'c']")); collection.updateOne(json(""), json("$push: {value: {$each: [1, 2], $slice: -5}}")); assertThat(collection.find()) .containsExactly(json("_id: 1, value: ['b', 'c', 1, 2]")); collection.updateOne(json(""), json("$push: {value: {$each: ['x', 'y', 'z'], $slice: 0}}")); assertThat(collection.find()) .containsExactly(json("_id: 1, value: []")); } @Test public void testUpdatePushSort() throws Exception { collection.insertOne(json("_id: 1")); collection.updateOne(json(""), json("$push: {value: {$each: [1, 5, 6, 3], $sort: -1}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [6, 5, 3, 1]")); collection.updateOne(json(""), json("$push: {value: {$each: [{value: 1}, {value: 3}, {value: 2}], $sort: {value: 1}}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [6, 5, 3, 1, {value: 1}, {value: 2}, {value: 3}]")); } @Test public void testUpdatePushSortAndSlice() throws Exception { collection.insertOne(json("_id: 1, quizzes: [{wk: 2, score: 9}, {wk: 1, score: 10}]")); collection.updateOne(json(""), json("$push: {quizzes: {" + "$each: [{wk: 5, score: 8}, {wk: 6, score: 7}, {wk: 7, score: 6}]," + "$sort: { score: -1 }," + "$slice: 3}}")); assertThat(collection.find()) .containsExactly( json("_id: 1, quizzes: [" + "{wk: 1, score: 10}," + "{wk: 2, score: 9}," + "{wk: 5, score: 8}" + "]") ); } @Test public void testUpdatePushPosition() throws Exception { collection.insertOne(json("_id: 1, value: [1, 2]")); collection.updateOne(json(""), json("$push: {value: {$each: [3, 4], $position: 10}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [1, 2, 3, 4]")); collection.updateOne(json(""), json("$push: {value: {$each: ['x'], $position: 2}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [1, 2, 'x', 3, 4]")); collection.updateOne(json(""), json("$push: {value: {$each: ['y'], $position: -2}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [1, 2, 'x', 'y', 3, 4]")); } @Test public void testUpdatePushEach_unknownModifier() throws Exception { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$each: [1, 2, 3], $illegal: 1}}")), 2, "BadValue", "Unrecognized clause in $push: $illegal"); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$position: 1}}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$position' in 'value..$position' is not valid for storage."); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$illegal: 1}}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$illegal' in 'value..$illegal' is not valid for storage."); } @Test public void testUpdatePushEach_illegalOptions() throws Exception { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$each: [1, 2, 3], $slice: 'abc'}}")), 2, "BadValue", "The value for $slice must be an integer value but was given type: string"); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$each: [1, 2, 3], $sort: 'abc'}}")), 2, "BadValue", "The $sort is invalid: use 1/-1 to sort the whole element, or {field:1/-1} to sort embedded fields"); assertMongoWriteException(() -> collection.updateOne(json(""), json("$push: {value: {$each: [1, 2, 3], $position: 'abc'}}")), 2, "BadValue", "The value for $position must be an integer value, not of type: string"); } @Test public void testUpdatePushAll() throws Exception { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.updateOne(json(""), json("$pushAll: {field: 'value'}")), 9, "FailedToParse", "Unknown modifier: $pushAll. Expected a valid update modifier or pipeline-style update specified as an array"); } @Test public void testUpdateAddToSet() throws Exception { Document idObj = json("_id: 1"); collection.insertOne(idObj); collection.updateOne(idObj, json("$addToSet: {'field.subfield.subsubfield': 'value'}")); assertThat(collection.find(idObj).first()).isEqualTo(json("_id: 1, field: {subfield: {subsubfield: ['value']}}")); // addToSet to non-array collection.updateOne(idObj, json("$set: {field: 'value'}")); assertThatExceptionOfType(MongoWriteException.class) .isThrownBy(() -> collection.updateOne(idObj, json("$addToSet: {field: 'value'}"))) .withMessageContaining("Cannot apply $addToSet to non-array field. Field named 'field' has non-array type string"); // addToSet with multiple fields collection.updateOne(idObj, json("$addToSet: {field1: 'value', field2: 'value2'}")); assertThat(collection.find(idObj).first()) .isEqualTo(json("_id: 1, field: 'value', field1: ['value'], field2: ['value2']")); // addToSet duplicate collection.updateOne(idObj, json("$addToSet: {field1: 'value'}")); assertThat(collection.find(idObj).first()) .isEqualTo(json("_id: 1, field: 'value', field1: ['value'], field2: ['value2']")); } @Test public void testUpdateAddToSetEach() throws Exception { collection.insertOne(json("_id: 1")); collection.updateOne(json("_id: 1"), addEachToSet("a", Arrays.asList(6, 5, 4))); assertThat(collection.find()).containsExactly(json("_id: 1, a: [6, 5, 4]")); collection.updateOne(json("_id: 1"), addEachToSet("a", Arrays.asList(3, 2, 1))); assertThat(collection.find()).containsExactly(json("_id: 1, a: [6, 5, 4, 3, 2, 1]")); collection.updateOne(json("_id: 1"), addEachToSet("a", Arrays.asList(7, 7, 9, 2))); assertThat(collection.find()).containsExactly(json("_id: 1, a: [6, 5, 4, 3, 2, 1, 7, 9]")); collection.updateOne(json("_id: 1"), addEachToSet("a", Arrays.asList(12, 13, 12))); assertThat(collection.find()).containsExactly(json("_id: 1, a: [6, 5, 4, 3, 2, 1, 7, 9, 12, 13]")); collection.replaceOne(json("_id: 1"), json("_id: 1")); collection.updateOne(json("_id: 1"), json("$addToSet: {value: {key: 'x'}}")); assertThat(collection.find()).containsExactly(json("_id: 1, value: [{key: 'x'}]")); } @Test public void testUpdateAddToSetEach_unknownModifier() throws Exception { collection.insertOne(json("_id: 1")); assertMongoWriteException(() -> collection.updateOne(json("_id: 1"), json("$addToSet: {value: {$each: [1, 2, 3], $slice: 2}}")), 2, "BadValue", "Found unexpected fields after $each in $addToSet: { $each: [ 1, 2, 3 ], $slice: 2 }"); assertMongoWriteException(() -> collection.updateOne(json("_id: 1"), json("$addToSet: {value: {$each: [1, 2, 3], value: 2}}")), 2, "BadValue", "Found unexpected fields after $each in $addToSet: { $each: [ 1, 2, 3 ], value: 2 }"); String expectedPathPrefix = getExpectedPathPrefix_testUpdateAddToSetEach_unknownModifier(); assertMongoWriteException(() -> collection.updateOne(json("_id: 1"), json("$addToSet: {value: {key: 2, $each: [1, 2, 3]}}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$each' in '" + expectedPathPrefix + ".$each' is not valid for storage."); assertMongoWriteException(() -> collection.updateOne(json("_id: 1"), json("$addToSet: {value: {$slice: 2, $each: [1, 2, 3]}}")), 52, "DollarPrefixedFieldName", "The dollar ($) prefixed field '$slice' in '" + expectedPathPrefix + ".$slice' is not valid for storage."); } protected String getExpectedPathPrefix_testUpdateAddToSetEach_unknownModifier() { return "value."; } @Test public void testUpdateDatasize() throws Exception { Document obj = json("_id: 1, a: {x: [1, 2, 3]}"); collection.insertOne(obj); Number oldSize = getCollStats().getInteger("size"); collection.updateOne(json("_id: 1"), set("a.x.0", 3)); assertThat(collection.find().first().get("a")).isEqualTo(json("x: [3, 2, 3]")); Number newSize = getCollStats().getInteger("size"); assertThat(newSize).isEqualTo(oldSize); // now increase the db collection.updateOne(json("_id: 1"), set("a.x.0", "abc")); Number yetNewSize = getCollStats().getInteger("size"); assertThat(yetNewSize.intValue() - oldSize.intValue()).isEqualTo(4); } @Test public void testUpdatePull() throws Exception { Document obj = json("_id: 1"); collection.insertOne(obj); // pull from non-existing field collection.updateOne(obj, json("$pull: {field1: 'value2', field2: 'value3'}")); assertThat(collection.find(obj).first()).isEqualTo(obj); // pull from non-array collection.updateOne(obj, set("field", "value")); assertMongoWriteException(() -> collection.updateOne(obj, pull("field", "value")), 2, "BadValue", "Cannot apply
Fix EmptyIndex operations #154