package de.otto.edison.mongo;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.IsNot.not;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import org.bson.Document;
import org.junit.Before;
import org.junit.Test;
import com.github.fakemongo.Fongo;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class AbstractMongoRepositoryTest {
private TestRepository testee;
@Before
public void setUp() throws Exception {
final Fongo fongo = new Fongo("inmemory-mongodb");
final MongoDatabase mongoDatabase = fongo.getDatabase("db");
testee = new TestRepository(mongoDatabase);
}
@Test
public void shouldUpdateExistingDocument() throws Exception {
// given
final TestObject testObject = new TestObject("someId", "someValue");
testee.create(testObject);
final TestObject testObjectToUpdate = new TestObject("someId", "someUpdatedValue");
// when
final boolean updated = testee.update(testObjectToUpdate);
// then
assertThat(updated, is(true));
final TestObject updatedTestObject = testee.findOne("someId").get();
assertThat(updatedTestObject.eTag, notNullValue());
assertThat(updatedTestObject.eTag, is(not(testObject.eTag)));
assertThat(updatedTestObject.id, is("someId"));
assertThat(updatedTestObject.value, is("someUpdatedValue"));
}
@Test
public void shouldNotUpdateMissingDocument() throws Exception {
// given
final TestObject testObjectToUpdate = new TestObject("someId", "someUpdatedValue");
// when
final boolean updated = testee.update(testObjectToUpdate);
// then
assertThat(updated, is(false));
assertThat(testee.findOne("someId").isPresent(), is(false));
}
@Test
public void shouldUpdateIfETagMatch() throws Exception {
// given
final TestObject testObject = new TestObject("someId", "someValue");
testee.create(testObject);
final String etagFromCreated = testee.findOne("someId").get().eTag;
final TestObject testObjectToUpdate = new TestObject("someId", "someUpdatedValue", etagFromCreated);
// when
final UpdateIfMatchResult updateIfMatchResult = testee.updateIfMatch(testObjectToUpdate, etagFromCreated);
final TestObject updatedTestObject = testee.findOne("someId").get();
// then
assertThat(updateIfMatchResult, is(UpdateIfMatchResult.OK));
assertThat(updatedTestObject.eTag, notNullValue());
assertThat(updatedTestObject.eTag, is(not(etagFromCreated)));
assertThat(updatedTestObject.id, is("someId"));
assertThat(updatedTestObject.value, is("someUpdatedValue"));
}
@Test
public void shouldNotUpdateIfEtagNotMatch() throws Exception {
// given
final TestObject testObject = new TestObject("someId", "someValue", "someEtagWhichIsNotInTheDb");
testee.create(testObject);
// when
final UpdateIfMatchResult updated = testee.updateIfMatch(testObject, "someOtherETag");
// then
assertThat(updated, is(UpdateIfMatchResult.CONCURRENTLY_MODIFIED));
}
@Test
public void shouldNotUpdateIfEtagNotExists() throws Exception {
// given
final TestObject testObject = new TestObject("someId", "someValue");
// when
final UpdateIfMatchResult updated = testee.updateIfMatch(testObject, "someETag");
// then
assertThat(updated, is(UpdateIfMatchResult.NOT_FOUND));
}
@Test(expected = NullPointerException.class)
public void shouldNotCreateOrUpdateWithMissingId() throws Exception {
// given
final TestObject testObject = new TestObject(null, "someValue");
// when
final TestObject resultingObject = testee.createOrUpdate(testObject);
// then
// NullPointerException is thrown
}
@Test(expected = NullPointerException.class)
public void shouldNotCreateWithMissingId() throws Exception {
// given
final TestObject testObject = new TestObject(null, "someValue");
// when
final TestObject resultingObject = testee.create(testObject);
// then
// NullPointerException is thrown
}
@Test(expected = NullPointerException.class)
public void shouldFindOneWithMissingId() throws Exception {
// when
testee.findOne(null);
// then
// NullPointerException is thrown
}
private void createTestObjects(final String... values) {
Arrays.stream(values)
.map(value -> new TestObject(value, value))
.forEach(testee::create);
}
@Test
public void shouldFindAllEntries() {
createTestObjects("testObject01", "testObject02", "testObject03", "testObject04", "testObject05", "testObject06");
// when
final List<TestObject> foundObjects = testee.findAll();
// then
assertThat(foundObjects, hasSize(6));
assertThat(foundObjects.get(0).value, is("testObject01"));
assertThat(foundObjects.get(1).value, is("testObject02"));
assertThat(foundObjects.get(2).value, is("testObject03"));
assertThat(foundObjects.get(3).value, is("testObject04"));
assertThat(foundObjects.get(4).value, is("testObject05"));
assertThat(foundObjects.get(5).value, is("testObject06"));
}
@Test
public void shouldStreamAllEntries() {
createTestObjects("testObject01", "testObject02", "testObject03", "testObject04", "testObject05", "testObject06");
// when
final List<TestObject> foundObjects = testee.findAllAsStream().collect(toList());
// then
assertThat(foundObjects, hasSize(6));
assertThat(foundObjects.get(0).value, is("testObject01"));
assertThat(foundObjects.get(1).value, is("testObject02"));
assertThat(foundObjects.get(2).value, is("testObject03"));
assertThat(foundObjects.get(3).value, is("testObject04"));
assertThat(foundObjects.get(4).value, is("testObject05"));
assertThat(foundObjects.get(5).value, is("testObject06"));
}
@Test
public void shouldFindAllEntriesWithSkipAndLimit() {
createTestObjects("testObject01", "testObject02", "testObject03", "testObject04", "testObject05", "testObject06");
// when
final List<TestObject> foundObjects = testee.findAll(2, 3);
// then
assertThat(foundObjects, hasSize(3));
assertThat(foundObjects.get(0).value, is("testObject03"));
assertThat(foundObjects.get(1).value, is("testObject04"));
assertThat(foundObjects.get(2).value, is("testObject05"));
}
@Test
public void shouldStreamAllEntriesWithSkipAndLimit() {
createTestObjects("testObject01", "testObject02", "testObject03", "testObject04", "testObject05", "testObject06");
// when
final List<TestObject> foundObjects = testee.findAllAsStream(2, 3).collect(toList());
// then
assertThat(foundObjects, hasSize(3));
assertThat(foundObjects.get(0).value, is("testObject03"));
assertThat(foundObjects.get(1).value, is("testObject04"));
assertThat(foundObjects.get(2).value, is("testObject05"));
}
// ~~
public class TestRepository extends AbstractMongoRepository<String, TestObject> {
private final MongoCollection<Document> collection;
public TestRepository(final MongoDatabase mongoDatabase) {
this.collection = mongoDatabase.getCollection("test");
}
@Override
protected MongoCollection<Document> collection() {
return collection;
}
@Override
protected String keyOf(final TestObject value) {
return value.id;
}
@Override
protected Document encode(final TestObject value) {
final Document document = new Document();
if (value.id != null) {
document.append("_id", value.id);
}
document.append("etag", UUID.randomUUID().toString());
document.append("value", value.value);
return document;
}
@Override
protected TestObject decode(final Document document) {
return new TestObject(
document.containsKey("_id") ? document.get("_id").toString() : null,
document.getString("value"),
document.getString("etag")
);
}
@Override
protected void ensureIndexes() {
}
}
public class TestObject {
private final String id;
private final String value;
private final String eTag;
protected TestObject(final String id, final String value, final String eTag) {
this.eTag = eTag;
this.id = id;
this.value = value;
}
protected TestObject(final String id, final String value) {
this(id, value, null);
}
}
}