package org.jooby.mongodb;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.bson.codecs.configuration.CodecRegistry;
import org.jooby.Env;
import org.jooby.Route;
import org.jooby.Router;
import org.jooby.rx.Rx;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.name.Names;
import com.mongodb.ConnectionString;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ClusterType;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerSettings;
import com.mongodb.connection.SocketSettings;
import com.mongodb.connection.SslSettings;
import com.mongodb.rx.client.AggregateObservable;
import com.mongodb.rx.client.DistinctObservable;
import com.mongodb.rx.client.FindObservable;
import com.mongodb.rx.client.ListCollectionsObservable;
import com.mongodb.rx.client.ListDatabasesObservable;
import com.mongodb.rx.client.MapReduceObservable;
import com.mongodb.rx.client.MongoClient;
import com.mongodb.rx.client.MongoClients;
import com.mongodb.rx.client.MongoCollection;
import com.mongodb.rx.client.MongoDatabase;
import com.mongodb.rx.client.MongoObservable;
import com.mongodb.rx.client.ObservableAdapter;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import javaslang.control.Try;
import javaslang.control.Try.CheckedRunnable;
import rx.Observable;
import rx.Scheduler;
@RunWith(PowerMockRunner.class)
@PrepareForTest({MongoRx.class, MongoClients.class, MongoClientSettings.class,
ClusterSettings.class, ConnectionPoolSettings.class, SocketSettings.class, ServerSettings.class,
SslSettings.class, Rx.class, Observable.class })
public class MongodbRxTest {
private Block settings = unit -> {
MongoClientSettings.Builder builder = unit.powerMock(MongoClientSettings.Builder.class);
expect(builder.clusterSettings(unit.get(ClusterSettings.class))).andReturn(builder);
expect(builder.connectionPoolSettings(unit.get(ConnectionPoolSettings.class)))
.andReturn(builder);
expect(builder.socketSettings(unit.get(SocketSettings.class))).andReturn(builder);
expect(builder.heartbeatSocketSettings(unit.get(SocketSettings.class))).andReturn(builder);
expect(builder.serverSettings(unit.get(ServerSettings.class))).andReturn(builder);
expect(builder.sslSettings(unit.get(SslSettings.class))).andReturn(builder);
unit.registerMock(MongoClientSettings.Builder.class, builder);
MongoClientSettings settings = unit.mock(MongoClientSettings.class);
expect(builder.build()).andReturn(settings);
unit.registerMock(MongoClientSettings.class, settings);
unit.mockStatic(MongoClientSettings.class);
expect(MongoClientSettings.builder()).andReturn(builder);
};
private Block server = unit -> {
ServerSettings settings = unit.mock(ServerSettings.class);
unit.registerMock(ServerSettings.class, settings);
ServerSettings.Builder builder = unit.mock(ServerSettings.Builder.class);
expect(builder.build()).andReturn(settings);
unit.mockStatic(ServerSettings.class);
expect(ServerSettings.builder()).andReturn(builder);
};
private Block mongo = unit -> {
unit.mockStatic(MongoClients.class);
expect(MongoClients.create(unit.get(MongoClientSettings.class)))
.andReturn(unit.get(MongoClient.class));
};
private Block env = unit -> {
Router routes = unit.mock(Router.class);
expect(routes.map(unit.capture(Route.Mapper.class))).andReturn(routes);
Env env = unit.get(Env.class);
expect(env.router()).andReturn(routes);
expect(env.onStop(unit.capture(CheckedRunnable.class))).andReturn(env);
};
private Block socket = unit -> {
unit.mockStatic(SocketSettings.class);
};
private Block database = unit -> {
MongoClient client = unit.get(MongoClient.class);
expect(client.getDatabase("pets")).andReturn(unit.get(MongoDatabase.class));
};
@SuppressWarnings("unchecked")
private Block collection = unit -> {
MongoDatabase db = unit.get(MongoDatabase.class);
expect(db.getCollection("Pets")).andReturn(unit.get(MongoCollection.class));
};
@Test
public void configure() throws Exception {
String db = "mongodb://localhost";
new MockUnit(Env.class, Binder.class, MongoClient.class)
.expect(instances(0))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class)))
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(env)
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@Test
public void configure1() throws Exception {
String db = "mongodb://localhost";
new MockUnit(Env.class, Binder.class, MongoClient.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(env)
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@Test
public void conf() throws Exception {
assertEquals("mongodb://localhost", new MongoRx().config().getString("db"));
}
@Test
public void onStop() throws Exception {
String db = "mongodb://localhost";
new MockUnit(Env.class, Binder.class, MongoClient.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(env)
.expect(unit -> {
MongoClient client = unit.get(MongoClient.class);
client.close();
})
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
}, unit -> {
unit.captured(CheckedRunnable.class).iterator().next().run();
});
}
@SuppressWarnings({"unchecked", "rawtypes" })
@Test
public void withObservable() throws Exception {
String db = "mongodb://localhost/pets";
new MockUnit(Env.class, Binder.class, MongoClient.class, MongoDatabase.class, Scheduler.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(database)
.expect(bind(Key.get(MongoDatabase.class, Names.named("pets"))))
.expect(unit -> {
MongoDatabase database = unit.get(MongoDatabase.class);
expect(database.withObservableAdapter(unit.capture(ObservableAdapter.class)))
.andReturn(database);
Observable observable = unit.powerMock(Observable.class);
unit.registerMock(Observable.class, observable);
expect(observable.observeOn(unit.get(Scheduler.class))).andReturn(observable);
})
.expect(env)
.run(unit -> {
new MongoRx()
.observableAdapter(o -> o.observeOn(unit.get(Scheduler.class)))
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
}, unit -> {
unit.captured(ObservableAdapter.class).iterator().next()
.adapt(unit.get(Observable.class));
});
}
@Test
public void withCodecRegistry() throws Exception {
String db = "mongodb://localhost/pets";
new MockUnit(Env.class, Binder.class, MongoClient.class, MongoDatabase.class, CodecRegistry.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(database)
.expect(bind(Key.get(MongoDatabase.class, Names.named("pets"))))
.expect(unit -> {
unit.get(CodecRegistry.class);
MongoDatabase database = unit.get(MongoDatabase.class);
expect(database.withCodecRegistry(unit.get(CodecRegistry.class))).andReturn(database);
})
.expect(env)
.run(unit -> {
new MongoRx()
.codecRegistry(unit.get(CodecRegistry.class))
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@Test
public void withCallback() throws Exception {
String db = "mongodb://localhost";
new MockUnit(Env.class, Binder.class, MongoClient.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(env)
.expect(unit -> {
MongoClientSettings.Builder settings = unit.get(MongoClientSettings.Builder.class);
expect(settings.readConcern(ReadConcern.LOCAL)).andReturn(settings);
})
.run(unit -> {
new MongoRx()
.doWith(settings -> {
settings.readConcern(ReadConcern.LOCAL);
})
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@SuppressWarnings({"unchecked", "rawtypes" })
@Test
public void mongoRxMapper() throws Exception {
String db = "mongodb://localhost";
new MockUnit(Env.class, Binder.class, MongoClient.class, FindObservable.class,
ListCollectionsObservable.class, ListDatabasesObservable.class, AggregateObservable.class,
DistinctObservable.class, MapReduceObservable.class, MongoObservable.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(env)
.expect(unit -> {
Observable observable = unit.powerMock(Observable.class);
expect(observable.toList()).andReturn(unit.powerMock(Observable.class)).times(6);
Observable mobservable = unit.powerMock(Observable.class);
FindObservable o1 = unit.get(FindObservable.class);
expect(o1.toObservable()).andReturn(observable);
ListCollectionsObservable o2 = unit.get(ListCollectionsObservable.class);
expect(o2.toObservable()).andReturn(observable);
ListDatabasesObservable o3 = unit.get(ListDatabasesObservable.class);
expect(o3.toObservable()).andReturn(observable);
AggregateObservable o4 = unit.get(AggregateObservable.class);
expect(o4.toObservable()).andReturn(observable);
DistinctObservable o5 = unit.get(DistinctObservable.class);
expect(o5.toObservable()).andReturn(observable);
MapReduceObservable o6 = unit.get(MapReduceObservable.class);
expect(o6.toObservable()).andReturn(observable);
MongoObservable o7 = unit.get(MongoObservable.class);
expect(o7.toObservable()).andReturn(mobservable);
})
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
}, unit -> {
Route.Mapper mongorx = unit.captured(Route.Mapper.class).iterator().next();
assertTrue(mongorx.map(unit.get(FindObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(ListCollectionsObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(ListDatabasesObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(AggregateObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(DistinctObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(MapReduceObservable.class)) instanceof Observable);
assertTrue(
mongorx.map(unit.get(MongoObservable.class)) instanceof Observable);
assertEquals("x", mongorx.map("x"));
});
}
@Test
public void withDatabase() throws Exception {
String db = "mongodb://localhost/pets";
new MockUnit(Env.class, Binder.class, MongoClient.class, MongoDatabase.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(database)
.expect(bind(Key.get(MongoDatabase.class, Names.named("pets"))))
.expect(env)
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@Test
public void withCollection() throws Exception {
String db = "mongodb://localhost/pets.Pets";
new MockUnit(Env.class, Binder.class, MongoClient.class, MongoDatabase.class,
MongoCollection.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named("db"))))
.expect(database)
.expect(bind(Key.get(MongoDatabase.class, Names.named("pets"))))
.expect(collection)
.expect(bind(Key.get(MongoCollection.class, Names.named("Pets"))))
.expect(env)
.run(unit -> {
new MongoRx()
.configure(unit.get(Env.class), conf(null, "db", db), unit.get(Binder.class));
});
}
@Test
public void withDirectDb() throws Exception {
String db = "mongodb://localhost/pets.Pets";
new MockUnit(Env.class, Binder.class, MongoClient.class, MongoDatabase.class,
MongoCollection.class)
.expect(instances(1))
.expect(cluster(db))
.expect(pool(db))
.expect(socket)
.expect(socket(db))
.expect(server)
.expect(ssl(db))
.expect(settings)
.expect(mongo)
.expect(bind(Key.get(MongoClient.class, Names.named(db))))
.expect(database)
.expect(bind(Key.get(MongoDatabase.class, Names.named("pets"))))
.expect(collection)
.expect(bind(Key.get(MongoCollection.class, Names.named("Pets"))))
.expect(env)
.run(unit -> {
new MongoRx(db)
.configure(unit.get(Env.class), conf(null), unit.get(Binder.class));
});
}
private Block instances(final int n) {
return unit -> {
Field field = MongoRx.class.getDeclaredField("instances");
field.setAccessible(true);
AtomicInteger instances = (AtomicInteger) field.get(null);
instances.set(n);
};
}
@SuppressWarnings({"unchecked", "rawtypes" })
private Block bind(final Key key) {
return unit -> {
Binder binder = unit.get(Binder.class);
LinkedBindingBuilder bb = unit.mock(LinkedBindingBuilder.class);
bb.toInstance(unit.get(key.getTypeLiteral().getRawType()));
expect(binder.bind(key)).andReturn(bb);
};
}
private Block cluster(final String db) {
return unit -> {
ClusterSettings settings = unit.mock(ClusterSettings.class);
unit.registerMock(ClusterSettings.class, settings);
ClusterSettings.Builder builder = unit.mock(ClusterSettings.Builder.class);
expect(builder.applyConnectionString(new ConnectionString(db))).andReturn(builder);
expect(builder.build()).andReturn(settings);
unit.mockStatic(ClusterSettings.class);
expect(ClusterSettings.builder()).andReturn(builder);
};
}
private Block pool(final String db) {
return unit -> {
ConnectionPoolSettings settings = unit.mock(ConnectionPoolSettings.class);
unit.registerMock(ConnectionPoolSettings.class, settings);
ConnectionPoolSettings.Builder builder = unit.mock(ConnectionPoolSettings.Builder.class);
expect(builder.applyConnectionString(new ConnectionString(db))).andReturn(builder);
expect(builder.build()).andReturn(settings);
unit.mockStatic(ConnectionPoolSettings.class);
expect(ConnectionPoolSettings.builder()).andReturn(builder);
};
}
private Block socket(final String db) {
return unit -> {
SocketSettings settings = Try.of(() -> unit.get(SocketSettings.class))
.getOrElse(() -> unit.mock(SocketSettings.class));
unit.registerMock(SocketSettings.class, settings);
SocketSettings.Builder builder = Try.of(() -> unit.get(SocketSettings.Builder.class))
.getOrElse(() -> unit.mock(SocketSettings.Builder.class));
expect(builder.applyConnectionString(new ConnectionString(db))).andReturn(builder).times(2);
expect(builder.build()).andReturn(settings).times(2);
expect(SocketSettings.builder()).andReturn(builder).times(2);
};
}
private Block ssl(final String db) {
return unit -> {
SslSettings settings = unit.mock(SslSettings.class);
unit.registerMock(SslSettings.class, settings);
SslSettings.Builder builder = unit.mock(SslSettings.Builder.class);
expect(builder.applyConnectionString(new ConnectionString(db))).andReturn(builder);
expect(builder.build()).andReturn(settings);
unit.mockStatic(SslSettings.class);
expect(SslSettings.builder()).andReturn(builder);
};
}
@Test
public void cluster() {
ClusterSettings cluster = MongoRx.cluster(new ConnectionString("mongodb://localhost"),
conf("cluster",
"maxWaitQueueSize", 5,
"replicaSetName", "r",
"requiredClusterType", "replica_set",
"serverSelectionTimeout", "3s"));
assertEquals(5, cluster.getMaxWaitQueueSize());
assertEquals("r", cluster.getRequiredReplicaSetName());
assertEquals(ClusterType.REPLICA_SET, cluster.getRequiredClusterType());
assertEquals(3, cluster.getServerSelectionTimeout(TimeUnit.SECONDS));
}
@Test
public void pool() {
ConnectionPoolSettings pool = MongoRx.pool(new ConnectionString("mongodb://localhost"),
conf("pool",
"maintenanceFrequency", "1s",
"maintenanceInitialDelay", "2s",
"maxConnectionIdleTime", "3s",
"maxConnectionLifeTime", 4500,
"maxSize", 3,
"maxWaitQueueSize", 9,
"maxWaitTime", "1m",
"minSize", 1));
assertEquals(1, pool.getMaintenanceFrequency(TimeUnit.SECONDS));
assertEquals(2, pool.getMaintenanceInitialDelay(TimeUnit.SECONDS));
assertEquals(3, pool.getMaxConnectionIdleTime(TimeUnit.SECONDS));
assertEquals(4500, pool.getMaxConnectionLifeTime(TimeUnit.MILLISECONDS));
assertEquals(60, pool.getMaxWaitTime(TimeUnit.SECONDS));
assertEquals(3, pool.getMaxSize());
assertEquals(9, pool.getMaxWaitQueueSize());
assertEquals(1, pool.getMinSize());
}
@Test
public void server() {
ServerSettings server = MongoRx.server(
conf("server",
"heartbeatFrequency", "1s",
"minHeartbeatFrequency", "2s"));
assertEquals(1, server.getHeartbeatFrequency(TimeUnit.SECONDS));
assertEquals(2, server.getMinHeartbeatFrequency(TimeUnit.SECONDS));
}
@Test
public void socket() {
SocketSettings socket = MongoRx.socket("socket", new ConnectionString("mongodb://localhost"),
conf("socket",
"connectTimeout", "1s",
"readTimeout", "2s",
"receiveBufferSize", 15,
"sendBufferSize", 20));
assertEquals(1, socket.getConnectTimeout(TimeUnit.SECONDS));
assertEquals(2, socket.getReadTimeout(TimeUnit.SECONDS));
assertEquals(15, socket.getReceiveBufferSize());
assertEquals(20, socket.getSendBufferSize());
}
@Test
public void ssl() {
SslSettings ssl = MongoRx.ssl(new ConnectionString("mongodb://localhost"),
conf("ssl",
"enabled", true,
"invalidHostNameAllowed", true));
assertEquals(true, ssl.isEnabled());
assertEquals(true, ssl.isInvalidHostNameAllowed());
}
@Test
public void settings() {
MongoClientSettings settings = MongoRx.settings(new ConnectionString("mongodb://localhost"),
conf(null,
"readConcern", "local",
"readPreference", "primary",
"writeConcern", "w3"))
.build();
assertEquals(ReadConcern.LOCAL, settings.getReadConcern());
assertEquals(ReadPreference.primary(), settings.getReadPreference());
assertEquals(WriteConcern.W3, settings.getWriteConcern());
}
@Test(expected = IllegalArgumentException.class)
public void wrongReadConcern() {
MongoRx.settings(new ConnectionString("mongodb://localhost"),
conf(null,
"readConcern", "Xxx",
"readPreference", "primary",
"writeConcern", "w3"))
.build();
}
@Test(expected = IllegalArgumentException.class)
public void wrongWriteConcern() {
MongoRx.settings(new ConnectionString("mongodb://localhost"),
conf(null,
"readPreference", "primary",
"writeConcern", "Xqw"))
.build();
}
@Test
public void dbconf() {
assertEquals("default",
MongoRx.dbconf("db", conf("mongo", "readConcern", "default")).getString("readConcern"));
assertEquals("default",
MongoRx.dbconf("mongodb://localhost", conf("mongo", "readConcern", "default"))
.getString("readConcern"));
assertEquals("local",
MongoRx.dbconf("db",
conf(null,
"mongo.readConcern", "default",
"mongo.db.readConcern", "local"))
.getString("readConcern"));
}
private Config conf(final String scope, final Object... values) {
ImmutableMap.Builder<String, Object> hash = ImmutableMap.builder();
String prefix = scope == null ? "" : scope + ".";
for (int i = 0; i < values.length; i += 2) {
hash.put(prefix + values[i].toString(), values[i + 1]);
}
return ConfigFactory.parseMap(hash.build());
}
}