Newer
Older
package org.briarproject.db;
import org.briarproject.BriarTestCase;
import org.briarproject.TestMessage;
import org.briarproject.TestUtils;
import org.briarproject.api.TransportConfig;
import org.briarproject.api.TransportId;
import org.briarproject.api.TransportProperties;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.NoSuchContactException;
import org.briarproject.api.db.NoSuchLocalAuthorException;
import org.briarproject.api.db.NoSuchSubscriptionException;
import org.briarproject.api.db.NoSuchTransportException;
import org.briarproject.api.event.ContactAddedEvent;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.LocalAuthorAddedEvent;
import org.briarproject.api.event.LocalAuthorRemovedEvent;
import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
import org.briarproject.api.event.LocalTransportsUpdatedEvent;
import org.briarproject.api.event.MessageAddedEvent;
import org.briarproject.api.event.MessageRequestedEvent;
import org.briarproject.api.event.MessageToAckEvent;
import org.briarproject.api.event.MessageToRequestEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.SubscriptionAddedEvent;
import org.briarproject.api.event.SubscriptionRemovedEvent;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.ShutdownManager;
import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.Offer;
import org.briarproject.api.sync.Request;
import org.briarproject.api.sync.SubscriptionAck;
import org.briarproject.api.sync.SubscriptionUpdate;
import org.briarproject.api.sync.TransportAck;
import org.briarproject.api.sync.TransportUpdate;
import org.briarproject.api.transport.IncomingKeys;
import org.briarproject.api.transport.OutgoingKeys;
import org.briarproject.api.transport.TransportKeys;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sync.MessagingConstants.GROUP_SALT_LENGTH;
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class DatabaseComponentImplTest extends BriarTestCase {
protected final Object txn = new Object();
protected final GroupId groupId;
protected final Group group;
protected final AuthorId authorId;
protected final Author author;
protected final AuthorId localAuthorId;
protected final LocalAuthor localAuthor;
protected final MessageId messageId, messageId1;
protected final long timestamp;
protected final int size;
protected final byte[] raw;
protected final Message message, message1;
protected final TransportId transportId;
protected final TransportProperties transportProperties;
protected final int maxLatency;
protected final ContactId contactId;
protected final Contact contact;
groupId = new GroupId(TestUtils.getRandomId());
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
authorId = new AuthorId(TestUtils.getRandomId());
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
localAuthorId = new AuthorId(TestUtils.getRandomId());
localAuthor = new LocalAuthor(localAuthorId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
messageId = new MessageId(TestUtils.getRandomId());
messageId1 = new MessageId(TestUtils.getRandomId());
contentType = "text/plain";
timestamp = System.currentTimeMillis();
size = 1234;
raw = new byte[size];
message = new TestMessage(messageId, null, group, author, contentType,
message1 = new TestMessage(messageId1, messageId, group, null,
transportId = new TransportId("id");
transportProperties = new TransportProperties(Collections.singletonMap(
"bar", "baz"));
maxLatency = Integer.MAX_VALUE;
contactId = new ContactId(234);
contact = new Contact(contactId, author, localAuthorId);
}
private <T> DatabaseComponent createDatabaseComponent(Database<T> database,
EventBus eventBus, ShutdownManager shutdown) {
return new DatabaseComponentImpl<T>(database, eventBus, shutdown);
}
@Test
@SuppressWarnings("unchecked")
public void testSimpleCalls() throws Exception {
final int shutdownHandle = 12345;
Mockery context = new Mockery();
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
exactly(11).of(database).startTransaction();
will(returnValue(txn));
exactly(11).of(database).commitTransaction(txn);
// open()
oneOf(database).open();
will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle));
oneOf(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(false));
oneOf(database).addLocalAuthor(txn, localAuthor);
oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class)));
oneOf(database).containsContact(txn, authorId);
will(returnValue(false));
oneOf(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(true));
oneOf(database).addContact(txn, author, localAuthorId);
will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// getContacts()
oneOf(database).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
oneOf(database).getRemoteProperties(txn, transportId);
will(returnValue(Collections.emptyMap()));
// addGroup()
oneOf(database).containsGroup(txn, groupId);
will(returnValue(false));
oneOf(database).addGroup(txn, group);
will(returnValue(true));
oneOf(eventBus).broadcast(with(any(SubscriptionAddedEvent.class)));
// addGroup() again
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
// getMessageHeaders()
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).getMessageHeaders(txn, groupId);
will(returnValue(Collections.emptyList()));
// getGroups()
oneOf(database).getGroups(txn);
will(returnValue(Collections.singletonList(group)));
// removeGroup()
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.emptyList()));
oneOf(database).removeGroup(txn, groupId);
SubscriptionRemovedEvent.class)));
LocalSubscriptionsUpdatedEvent.class)));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getInboxGroupId(txn, contactId);
will(returnValue(null));
oneOf(database).removeContact(txn, contactId);
oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class)));
oneOf(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(true));
oneOf(database).getContacts(txn, localAuthorId);
will(returnValue(Collections.emptyList()));
oneOf(database).removeLocalAuthor(txn, localAuthorId);
oneOf(eventBus).broadcast(with(any(LocalAuthorRemovedEvent.class)));
// close()
oneOf(shutdown).removeShutdownHook(shutdownHandle);
oneOf(database).close();
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertFalse(db.open());
db.addLocalAuthor(localAuthor);
assertEquals(contactId, db.addContact(author, localAuthorId));
assertEquals(Collections.singletonList(contact), db.getContacts());
assertEquals(Collections.emptyMap(),
db.getRemoteProperties(transportId));
db.addGroup(group); // First time - listeners called
db.addGroup(group); // Second time - not called
assertEquals(Collections.emptyList(), db.getMessageHeaders(groupId));
assertEquals(Collections.singletonList(group), db.getGroups());
db.removeLocalAuthor(localAuthorId);
db.close();
context.assertIsSatisfied();
}
@Test
public void testDuplicateLocalMessagesAreNotStored() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(true));
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
context.assertIsSatisfied();
}
@Test
public void testLocalMessagesAreNotStoredUnlessSubscribed()
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).containsGroup(txn, groupId);
will(returnValue(false));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
context.assertIsSatisfied();
}
@Test
public void testAddLocalMessage() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
oneOf(database).addMessage(txn, message, true);
oneOf(database).setReadFlag(txn, messageId, true);
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).getContactIds(txn);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
oneOf(database).commitTransaction(txn);
// The message was added, so the listener should be called
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
context.assertIsSatisfied();
}
@Test
public void testVariousMethodsThrowExceptionIfContactIsMissing()
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not)
exactly(21).of(database).startTransaction();
will(returnValue(txn));
exactly(21).of(database).containsContact(txn, contactId);
will(returnValue(false));
exactly(21).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
try {
db.addTransportKeys(contactId, createTransportKeys());
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateAck(contactId, 123);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateBatch(contactId, 123, 456);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateOffer(contactId, 123, 456);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateSubscriptionAck(contactId);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateSubscriptionUpdate(contactId, 123);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateTransportAcks(contactId);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.generateTransportUpdates(contactId, 123);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.getContact(contactId);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
fail();
} catch (NoSuchContactException expected) {
// Expected
}
db.incrementStreamCounter(contactId, transportId, 0);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
Ack a = new Ack(Collections.singletonList(messageId));
db.receiveAck(contactId, a);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.receiveMessage(contactId, message);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
Offer o = new Offer(Collections.singletonList(messageId));
db.receiveOffer(contactId, o);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
SubscriptionAck a = new SubscriptionAck(0);
db.receiveSubscriptionAck(contactId, a);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
SubscriptionUpdate u = new SubscriptionUpdate(
Collections.<Group>emptyList(), 1);
db.receiveSubscriptionUpdate(contactId, u);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
TransportAck a = new TransportAck(transportId, 0);
db.receiveTransportAck(contactId, a);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
TransportUpdate u = new TransportUpdate(transportId,
transportProperties, 1);
db.receiveTransportUpdate(contactId, u);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.removeContact(contactId);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try {
db.setReorderingWindow(contactId, transportId, 0, 0, new byte[4]);
fail();
} catch (NoSuchContactException expected) {
// Expected
}
db.setInboxGroup(contactId, group);
} catch (NoSuchContactException expected) {
// Expected
}
context.assertIsSatisfied();
}
@Test
public void testVariousMethodsThrowExceptionIfLocalAuthorIsMissing()
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
// Check whether the pseudonym is in the DB (which it's not)
exactly(3).of(database).startTransaction();
will(returnValue(txn));
exactly(3).of(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(false));
exactly(3).of(database).abortTransaction(txn);
// This is needed for addContact() to proceed
exactly(1).of(database).containsContact(txn, authorId);
will(returnValue(false));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
try {
db.addContact(author, localAuthorId);
fail();
} catch (NoSuchLocalAuthorException expected) {
// Expected
}
try {
db.getLocalAuthor(localAuthorId);
fail();
} catch (NoSuchLocalAuthorException expected) {
// Expected
}
try {
db.removeLocalAuthor(localAuthorId);
fail();
} catch (NoSuchLocalAuthorException expected) {
// Expected
}
context.assertIsSatisfied();
}
@Test
public void testVariousMethodsThrowExceptionIfSubscriptionIsMissing()
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
// Check whether the subscription is in the DB (which it's not)
exactly(5).of(database).startTransaction();
will(returnValue(txn));
exactly(5).of(database).containsGroup(txn, groupId);
will(returnValue(false));
exactly(5).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
try {
db.getGroup(groupId);
fail();
} catch (NoSuchSubscriptionException expected) {
// Expected
}
try {
db.getMessageHeaders(groupId);
} catch (NoSuchSubscriptionException expected) {
// Expected
}
try {
db.getVisibility(groupId);
fail();
} catch (NoSuchSubscriptionException expected) {
// Expected
}
try {
} catch (NoSuchSubscriptionException expected) {
// Expected
}
try {
db.setVisibility(groupId, Collections.<ContactId>emptyList());
} catch (NoSuchSubscriptionException expected) {
// Expected
}
context.assertIsSatisfied();
}
@Test
public void testVariousMethodsThrowExceptionIfTransportIsMissing()
throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(false));
oneOf(database).addLocalAuthor(txn, localAuthor);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class)));
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, authorId);
will(returnValue(false));
oneOf(database).containsLocalAuthor(txn, localAuthorId);
will(returnValue(true));
oneOf(database).addContact(txn, author, localAuthorId);
will(returnValue(contactId));
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// Check whether the transport is in the DB (which it's not)
exactly(8).of(database).startTransaction();
will(returnValue(txn));
exactly(2).of(database).containsContact(txn, contactId);
will(returnValue(true));
exactly(8).of(database).containsTransport(txn, transportId);
will(returnValue(false));
exactly(8).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
db.addLocalAuthor(localAuthor);
assertEquals(contactId, db.addContact(author, localAuthorId));
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.getLocalProperties(transportId);
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.getTransportKeys(transportId);
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.mergeConfig(transportId, new TransportConfig());
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.mergeLocalProperties(transportId, new TransportProperties());
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.incrementStreamCounter(contactId, transportId, 0);
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
try {
db.removeTransport(transportId);
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
db.setReorderingWindow(contactId, transportId, 0, 0, new byte[4]);
fail();
} catch (NoSuchTransportException expected) {
// Expected
}
context.assertIsSatisfied();
}
@Test
public void testGenerateAck() throws Exception {
final Collection<MessageId> messagesToAck = Arrays.asList(messageId,
messageId1);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getMessagesToAck(txn, contactId, 123);
will(returnValue(messagesToAck));
oneOf(database).lowerAckFlag(txn, contactId, messagesToAck);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Ack a = db.generateAck(contactId, 123);
assertEquals(messagesToAck, a.getMessageIds());
context.assertIsSatisfied();
}
@Test
public void testGenerateBatch() throws Exception {
final byte[] raw1 = new byte[size];
final Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
final Collection<byte[]> messages = Arrays.asList(raw, raw1);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getMessagesToSend(txn, contactId, size * 2);
will(returnValue(ids));
oneOf(database).getRawMessage(txn, messageId);
will(returnValue(raw));
oneOf(database).updateExpiryTime(txn, contactId, messageId,
oneOf(database).getRawMessage(txn, messageId1);
will(returnValue(raw1));
oneOf(database).updateExpiryTime(txn, contactId, messageId1,
oneOf(database).lowerRequestedFlag(txn, contactId, ids);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertEquals(messages, db.generateBatch(contactId, size * 2,
context.assertIsSatisfied();
}
@Test
public void testGenerateOffer() throws Exception {
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
final Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getMessagesToOffer(txn, contactId, 123);
will(returnValue(ids));
oneOf(database).updateExpiryTime(txn, contactId, messageId,
oneOf(database).updateExpiryTime(txn, contactId, messageId1,
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Offer o = db.generateOffer(contactId, 123, maxLatency);
assertEquals(ids, o.getMessageIds());
context.assertIsSatisfied();
}
@Test
public void testGenerateRequest() throws Exception {
final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
final Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getMessagesToRequest(txn, contactId, 123);
will(returnValue(ids));
oneOf(database).removeOfferedMessages(txn, contactId, ids);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Request r = db.generateRequest(contactId, 123);
assertEquals(ids, r.getMessageIds());
context.assertIsSatisfied();
}
@Test
public void testGenerateRequestedBatch() throws Exception {
final byte[] raw1 = new byte[size];
final Collection<MessageId> ids = Arrays.asList(messageId, messageId1);
final Collection<byte[]> messages = Arrays.asList(raw, raw1);
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getRequestedMessagesToSend(txn, contactId,
size * 2);
will(returnValue(ids));
oneOf(database).getRawMessage(txn, messageId);
will(returnValue(raw));
oneOf(database).updateExpiryTime(txn, contactId, messageId,
oneOf(database).getRawMessage(txn, messageId1);
will(returnValue(raw1));
oneOf(database).updateExpiryTime(txn, contactId, messageId1,
oneOf(database).lowerRequestedFlag(txn, contactId, ids);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertEquals(messages, db.generateRequestedBatch(contactId, size * 2,
context.assertIsSatisfied();
}
@Test
public void testGenerateSubscriptionUpdateNoUpdateDue() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency);
will(returnValue(null));
oneOf(database).commitTransaction(txn);
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertNull(db.generateSubscriptionUpdate(contactId, maxLatency));
context.assertIsSatisfied();
}
@Test
public void testGenerateSubscriptionUpdate() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency);
will(returnValue(new SubscriptionUpdate(
Collections.singletonList(group), 1)));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
SubscriptionUpdate u = db.generateSubscriptionUpdate(contactId,
assertEquals(Collections.singletonList(group), u.getGroups());
assertEquals(1, u.getVersion());
context.assertIsSatisfied();
}
@Test
public void testGenerateTransportUpdatesNoUpdatesDue() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getTransportUpdates(txn, contactId, maxLatency);
will(returnValue(null));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
assertNull(db.generateTransportUpdates(contactId, maxLatency));
context.assertIsSatisfied();
}
@Test
public void testGenerateTransportUpdates() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).getTransportUpdates(txn, contactId, maxLatency);
will(returnValue(Collections.singletonList(new TransportUpdate(
transportId, transportProperties, 1))));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
Collection<TransportUpdate> updates =
db.generateTransportUpdates(contactId, maxLatency);
assertNotNull(updates);
assertEquals(1, updates.size());
TransportUpdate u = updates.iterator().next();
assertEquals(transportId, u.getId());
assertEquals(transportProperties, u.getProperties());
assertEquals(1, u.getVersion());
context.assertIsSatisfied();
}
@Test
public void testReceiveAck() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
will(returnValue(true));
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
db.receiveAck(contactId, new Ack(Collections.singletonList(messageId)));
context.assertIsSatisfied();
}
@Test
public void testReceiveMessage() throws Exception {
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
oneOf(database).containsMessage(txn, messageId);
will(returnValue(false));
oneOf(database).containsVisibleGroup(txn, contactId, groupId);
will(returnValue(true));