Newer
Older
// The message should be unread by default
assertFalse(db.getReadFlag(txn, messageId));
// Mark the message read
db.setReadFlag(txn, messageId, true);
// The message should be read
assertTrue(db.getReadFlag(txn, messageId));
// Mark the message unread
db.setReadFlag(txn, messageId, false);
// The message should be unread
assertFalse(db.getReadFlag(txn, messageId));
db.commitTransaction(txn);
db.close();
}
@Test
public void testGetUnreadMessageCounts() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Subscribe to a couple of groups
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
Group group1 = new Group(groupId1, "Another group",
new byte[GROUP_SALT_LENGTH]);
// Store two messages in the first group
db.addMessage(txn, message, false);
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
Message message1 = new TestMessage(messageId1, null, group, author,
contentType, subject, timestamp, raw);
db.addMessage(txn, message1, false);
// Store one message in the second group
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
Message message2 = new TestMessage(messageId2, null, group1, author,
contentType, subject, timestamp, raw);
db.addMessage(txn, message2, false);
// Mark one of the messages in the first group read
db.setReadFlag(txn, messageId, true);
// There should be one unread message in each group
Map<GroupId, Integer> counts = db.getUnreadMessageCounts(txn);
assertEquals(2, counts.size());
Integer count = counts.get(groupId);
assertNotNull(count);
assertEquals(1, count.intValue());
count = counts.get(groupId1);
assertNotNull(count);
assertEquals(1, count.intValue());
// Mark the read message unread
db.setReadFlag(txn, messageId, false);
// Mark the message in the second group read
db.setReadFlag(txn, messageId2, true);
// There should be two unread messages in the first group, none in
// the second group
counts = db.getUnreadMessageCounts(txn);
assertEquals(1, counts.size());
count = counts.get(groupId);
assertNotNull(count);
assertEquals(2, count.intValue());
db.commitTransaction(txn);
db.close();
}
@Test
public void testMultipleSubscriptionsAndUnsubscriptions() throws Exception {
// Create some groups
List<Group> groups = new ArrayList<Group>();
for(int i = 0; i < 100; i++) {
GroupId id = new GroupId(TestUtils.getRandomId());
String name = "Group " + i;
groups.add(new Group(id, name, new byte[GROUP_SALT_LENGTH]));
}
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact and subscribe to the groups
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
for(Group g : groups) db.addGroup(txn, g);
// Make the groups visible to the contact
Collections.shuffle(groups);
for(Group g : groups) db.addVisibility(txn, contactId, g.getId());
// Make some of the groups invisible to the contact and remove them all
Collections.shuffle(groups);
for(Group g : groups) {
if(Math.random() < 0.5)
db.removeVisibility(txn, contactId, g.getId());
db.removeGroup(txn, g.getId());
}
db.commitTransaction(txn);
db.close();
}
@Test
public void testTemporarySecrets() throws Exception {
// Create an endpoint and four consecutive temporary secrets
long epoch = 123, latency = 234;
boolean alice = false;
long outgoing1 = 345, centre1 = 456;
long outgoing2 = 567, centre2 = 678;
long outgoing3 = 789, centre3 = 890;
long outgoing4 = 901, centre4 = 123;
Endpoint ep = new Endpoint(contactId, transportId, epoch, alice);
Random random = new Random();
byte[] secret1 = new byte[32], bitmap1 = new byte[4];
random.nextBytes(secret1);
random.nextBytes(bitmap1);
TemporarySecret s1 = new TemporarySecret(contactId, transportId, epoch,
alice, 0, secret1, outgoing1, centre1, bitmap1);
byte[] secret2 = new byte[32], bitmap2 = new byte[4];
random.nextBytes(secret2);
random.nextBytes(bitmap2);
TemporarySecret s2 = new TemporarySecret(contactId, transportId, epoch,
alice, 1, secret2, outgoing2, centre2, bitmap2);
byte[] secret3 = new byte[32], bitmap3 = new byte[4];
random.nextBytes(secret3);
random.nextBytes(bitmap3);
TemporarySecret s3 = new TemporarySecret(contactId, transportId, epoch,
alice, 2, secret3, outgoing3, centre3, bitmap3);
byte[] secret4 = new byte[32], bitmap4 = new byte[4];
random.nextBytes(secret4);
random.nextBytes(bitmap4);
TemporarySecret s4 = new TemporarySecret(contactId, transportId, epoch,
alice, 3, secret4, outgoing4, centre4, bitmap4);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no secrets in the database
assertEquals(Collections.emptyList(), db.getSecrets(txn));
// Add the contact, the transport, the endpoint and the first three
// secrets (periods 0, 1 and 2)
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addTransport(txn, transportId, latency);
db.addEndpoint(txn, ep);
db.addSecrets(txn, Arrays.asList(s1, s2, s3));
// Retrieve the first three secrets
Collection<TemporarySecret> secrets = db.getSecrets(txn);
assertEquals(3, secrets.size());
boolean foundFirst = false, foundSecond = false, foundThird = false;
for(TemporarySecret s : secrets) {
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(epoch, s.getEpoch());
assertEquals(alice, s.getAlice());
if(s.getPeriod() == 0) {
assertArrayEquals(secret1, s.getSecret());
assertEquals(outgoing1, s.getOutgoingConnectionCounter());
assertEquals(centre1, s.getWindowCentre());
assertArrayEquals(bitmap1, s.getWindowBitmap());
foundFirst = true;
} else if(s.getPeriod() == 1) {
assertArrayEquals(secret2, s.getSecret());
assertEquals(outgoing2, s.getOutgoingConnectionCounter());
assertEquals(centre2, s.getWindowCentre());
assertArrayEquals(bitmap2, s.getWindowBitmap());
foundSecond = true;
} else if(s.getPeriod() == 2) {
assertArrayEquals(secret3, s.getSecret());
assertEquals(outgoing3, s.getOutgoingConnectionCounter());
assertEquals(centre3, s.getWindowCentre());
assertArrayEquals(bitmap3, s.getWindowBitmap());
foundThird = true;
} else {
fail();
}
}
assertTrue(foundFirst);
assertTrue(foundSecond);
assertTrue(foundThird);
// Adding the fourth secret (period 3) should delete the first
db.addSecrets(txn, Arrays.asList(s4));
secrets = db.getSecrets(txn);
assertEquals(3, secrets.size());
foundSecond = foundThird = false;
boolean foundFourth = false;
for(TemporarySecret s : secrets) {
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(epoch, s.getEpoch());
assertEquals(alice, s.getAlice());
if(s.getPeriod() == 1) {
assertArrayEquals(secret2, s.getSecret());
assertEquals(outgoing2, s.getOutgoingConnectionCounter());
assertEquals(centre2, s.getWindowCentre());
assertArrayEquals(bitmap2, s.getWindowBitmap());
foundSecond = true;
} else if(s.getPeriod() == 2) {
assertArrayEquals(secret3, s.getSecret());
assertEquals(outgoing3, s.getOutgoingConnectionCounter());
assertEquals(centre3, s.getWindowCentre());
assertArrayEquals(bitmap3, s.getWindowBitmap());
foundThird = true;
} else if(s.getPeriod() == 3) {
assertArrayEquals(secret4, s.getSecret());
assertEquals(outgoing4, s.getOutgoingConnectionCounter());
assertEquals(centre4, s.getWindowCentre());
assertArrayEquals(bitmap4, s.getWindowBitmap());
foundFourth = true;
} else {
fail();
}
}
assertTrue(foundSecond);
assertTrue(foundThird);
assertTrue(foundFourth);
// Removing the contact should remove the secrets
db.removeContact(txn, contactId);
assertEquals(Collections.emptyList(), db.getSecrets(txn));
db.commitTransaction(txn);
db.close();
}
@Test
public void testIncrementConnectionCounter() throws Exception {
// Create an endpoint and a temporary secret
long epoch = 123, latency = 234;
boolean alice = false;
long period = 345, outgoing = 456, centre = 567;
Endpoint ep = new Endpoint(contactId, transportId, epoch, alice);
Random random = new Random();
byte[] secret = new byte[32], bitmap = new byte[4];
random.nextBytes(secret);
TemporarySecret s = new TemporarySecret(contactId, transportId, epoch,
alice, period, secret, outgoing, centre, bitmap);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the contact, the transport, the endpoint and the temporary secret
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addTransport(txn, transportId, latency);
db.addEndpoint(txn, ep);
db.addSecrets(txn, Arrays.asList(s));
// Retrieve the secret
Collection<TemporarySecret> secrets = db.getSecrets(txn);
assertEquals(1, secrets.size());
s = secrets.iterator().next();
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(period, s.getPeriod());
assertArrayEquals(secret, s.getSecret());
assertEquals(outgoing, s.getOutgoingConnectionCounter());
assertEquals(centre, s.getWindowCentre());
assertArrayEquals(bitmap, s.getWindowBitmap());
// Increment the connection counter twice and retrieve the secret again
assertEquals(outgoing, db.incrementConnectionCounter(txn,
s.getContactId(), s.getTransportId(), s.getPeriod()));
assertEquals(outgoing + 1, db.incrementConnectionCounter(txn,
s.getContactId(), s.getTransportId(), s.getPeriod()));
secrets = db.getSecrets(txn);
assertEquals(1, secrets.size());
s = secrets.iterator().next();
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(period, s.getPeriod());
assertArrayEquals(secret, s.getSecret());
assertEquals(outgoing + 2, s.getOutgoingConnectionCounter());
assertEquals(centre, s.getWindowCentre());
assertArrayEquals(bitmap, s.getWindowBitmap());
db.commitTransaction(txn);
db.close();
}
@Test
public void testSetConnectionWindow() throws Exception {
// Create an endpoint and a temporary secret
long epoch = 123, latency = 234;
boolean alice = false;
long period = 345, outgoing = 456, centre = 567;
Endpoint ep = new Endpoint(contactId, transportId, epoch, alice);
Random random = new Random();
byte[] secret = new byte[32], bitmap = new byte[4];
random.nextBytes(secret);
TemporarySecret s = new TemporarySecret(contactId, transportId, epoch,
alice, period, secret, outgoing, centre, bitmap);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add the contact, the transport, the endpoint and the temporary secret
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addTransport(txn, transportId, latency);
db.addEndpoint(txn, ep);
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
db.addSecrets(txn, Arrays.asList(s));
// Retrieve the secret
Collection<TemporarySecret> secrets = db.getSecrets(txn);
assertEquals(1, secrets.size());
s = secrets.iterator().next();
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(period, s.getPeriod());
assertArrayEquals(secret, s.getSecret());
assertEquals(outgoing, s.getOutgoingConnectionCounter());
assertEquals(centre, s.getWindowCentre());
assertArrayEquals(bitmap, s.getWindowBitmap());
// Update the connection window and retrieve the secret again
random.nextBytes(bitmap);
db.setConnectionWindow(txn, contactId, transportId, period, centre,
bitmap);
secrets = db.getSecrets(txn);
assertEquals(1, secrets.size());
s = secrets.iterator().next();
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(period, s.getPeriod());
assertArrayEquals(secret, s.getSecret());
assertEquals(outgoing, s.getOutgoingConnectionCounter());
assertEquals(centre, s.getWindowCentre());
assertArrayEquals(bitmap, s.getWindowBitmap());
// Updating a nonexistent window should not throw an exception
db.setConnectionWindow(txn, contactId, transportId, period + 1, 1,
bitmap);
// The nonexistent window should not have been created
secrets = db.getSecrets(txn);
assertEquals(1, secrets.size());
s = secrets.iterator().next();
assertEquals(contactId, s.getContactId());
assertEquals(transportId, s.getTransportId());
assertEquals(period, s.getPeriod());
assertArrayEquals(secret, s.getSecret());
assertEquals(outgoing, s.getOutgoingConnectionCounter());
assertEquals(centre, s.getWindowCentre());
assertArrayEquals(bitmap, s.getWindowBitmap());
db.commitTransaction(txn);
db.close();
}
@Test
public void testContactTransports() throws Exception {
// Create some endpoints
long epoch1 = 123, latency1 = 234;
long epoch2 = 345, latency2 = 456;
boolean alice1 = true, alice2 = false;
TransportId transportId1 = new TransportId("bar");
TransportId transportId2 = new TransportId("baz");
Endpoint ep1 = new Endpoint(contactId, transportId1, epoch1, alice1);
Endpoint ep2 = new Endpoint(contactId, transportId2, epoch2, alice2);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Initially there should be no endpoints in the database
assertEquals(Collections.emptyList(), db.getEndpoints(txn));
// Add the contact, the transports and the endpoints
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addTransport(txn, transportId1, latency1);
db.addTransport(txn, transportId2, latency2);
db.addEndpoint(txn, ep1);
db.addEndpoint(txn, ep2);
// Retrieve the contact transports
Collection<Endpoint> endpoints = db.getEndpoints(txn);
assertEquals(2, endpoints.size());
boolean foundFirst = false, foundSecond = false;
for(Endpoint ep : endpoints) {
assertEquals(contactId, ep.getContactId());
if(ep.getTransportId().equals(transportId1)) {
assertEquals(epoch1, ep.getEpoch());
assertEquals(alice1, ep.getAlice());
foundFirst = true;
} else if(ep.getTransportId().equals(transportId2)) {
assertEquals(epoch2, ep.getEpoch());
assertEquals(alice2, ep.getAlice());
foundSecond = true;
} else {
fail();
}
}
assertTrue(foundFirst);
assertTrue(foundSecond);
// Removing the contact should remove the contact transports
db.removeContact(txn, contactId);
assertEquals(Collections.emptyList(), db.getEndpoints(txn));
db.commitTransaction(txn);
db.close();
}
@Test
public void testGetAvailableGroups() throws Exception {
ContactId contactId1 = new ContactId(2);
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
Author author1 = new Author(authorId1, "Carol",
new byte[MAX_PUBLIC_KEY_LENGTH]);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add two contacts who subscribe to a group
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
assertEquals(contactId1, db.addContact(txn, author1, localAuthorId));
db.setGroups(txn, contactId, Arrays.asList(group), 1);
db.setGroups(txn, contactId1, Arrays.asList(group), 1);
// The group should be available, not subscribed, not visible to all
assertEquals(Collections.emptyList(), db.getGroups(txn));
Iterator<GroupStatus> it = db.getAvailableGroups(txn).iterator();
assertTrue(it.hasNext());
GroupStatus status = it.next();
assertEquals(group, status.getGroup());
assertFalse(status.isSubscribed());
assertFalse(status.isVisibleToAll());
assertFalse(it.hasNext());
// Subscribe to the group - it should be available, subscribed,
// not visible to all
db.addGroup(txn, group);
assertEquals(Arrays.asList(group), db.getGroups(txn));
it = db.getAvailableGroups(txn).iterator();
assertTrue(it.hasNext());
status = it.next();
assertEquals(group, status.getGroup());
assertTrue(status.isSubscribed());
assertFalse(status.isVisibleToAll());
assertFalse(it.hasNext());
// The first contact unsubscribes - the group should be available,
// subscribed, not visible to all
db.setGroups(txn, contactId, Collections.<Group>emptyList(), 2);
assertEquals(Arrays.asList(group), db.getGroups(txn));
it = db.getAvailableGroups(txn).iterator();
assertTrue(it.hasNext());
status = it.next();
assertEquals(group, status.getGroup());
assertTrue(status.isSubscribed());
assertFalse(status.isVisibleToAll());
assertFalse(it.hasNext());
// Make the group visible to all contacts - it should be available,
// subscribed, visible to all
db.setVisibleToAll(txn, groupId, true);
assertEquals(Arrays.asList(group), db.getGroups(txn));
it = db.getAvailableGroups(txn).iterator();
assertTrue(it.hasNext());
status = it.next();
assertEquals(group, status.getGroup());
assertTrue(status.isSubscribed());
assertTrue(status.isVisibleToAll());
assertFalse(it.hasNext());
// Unsubscribe from the group - it should be available, not subscribed,
// not visible to all
db.removeGroup(txn, groupId);
assertEquals(Collections.emptyList(), db.getGroups(txn));
it = db.getAvailableGroups(txn).iterator();
assertTrue(it.hasNext());
status = it.next();
assertEquals(group, status.getGroup());
assertFalse(status.isSubscribed());
assertFalse(status.isVisibleToAll());
assertFalse(it.hasNext());
// The second contact unsubscribes - the group should no longer be
// available
db.setGroups(txn, contactId1, Collections.<Group>emptyList(), 2);
assertEquals(Collections.emptyList(), db.getGroups(txn));
assertEquals(Collections.emptyList(), db.getAvailableGroups(txn));
db.commitTransaction(txn);
db.close();
}
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
@Test
public void testGetContactsByLocalAuthorId() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a local author - no contacts should be associated
db.addLocalAuthor(txn, localAuthor);
Collection<ContactId> contacts = db.getContacts(txn, localAuthorId);
assertEquals(Collections.emptyList(), contacts);
// Add a contact associated with the local author
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
contacts = db.getContacts(txn, localAuthorId);
assertEquals(Collections.singletonList(contactId), contacts);
// Remove the local author - the contact should be removed
db.removeLocalAuthor(txn, localAuthorId);
contacts = db.getContacts(txn, localAuthorId);
assertEquals(Collections.emptyList(), contacts);
assertFalse(db.containsContact(txn, contactId));
db.commitTransaction(txn);
db.close();
}
@Test
public void testGetInboxMessageHeaders() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact and an inbox group - no headers should be returned
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addGroup(txn, group);
db.setInboxGroup(txn, contactId, group);
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
assertEquals(Collections.emptyList(),
db.getInboxMessageHeaders(txn, contactId));
// Add a message to the inbox group - the header should be returned
db.addMessage(txn, message, false);
Collection<MessageHeader> headers =
db.getInboxMessageHeaders(txn, contactId);
assertEquals(1, headers.size());
MessageHeader header = headers.iterator().next();
assertEquals(messageId, header.getId());
assertNull(header.getParent());
assertEquals(groupId, header.getGroupId());
assertEquals(localAuthor, header.getAuthor());
assertEquals(contentType, header.getContentType());
assertEquals(timestamp, header.getTimestamp());
assertFalse(header.isRead());
db.commitTransaction(txn);
db.close();
}
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
@Test
public void testOfferedMessages() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact - initially there should be no offered messages
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
assertEquals(0, db.countOfferedMessages(txn, contactId));
// Add some offered messages and count them
List<MessageId> ids = new ArrayList<MessageId>();
for(int i = 0; i < 10; i++) {
MessageId m = new MessageId(TestUtils.getRandomId());
db.addOfferedMessage(txn, contactId, m);
ids.add(m);
}
assertEquals(10, db.countOfferedMessages(txn, contactId));
// Remove some of the offered messages and count again
List<MessageId> half = ids.subList(0, 5);
db.removeOfferedMessages(txn, contactId, half);
assertTrue(db.removeOfferedMessage(txn, contactId, ids.get(5)));
assertEquals(4, db.countOfferedMessages(txn, contactId));
db.commitTransaction(txn);
db.close();
}
@Test
public void testExceptionHandling() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
try {
// Ask for a nonexistent message - an exception should be thrown
db.getRawMessage(txn, messageId);
fail();
} catch(DbException expected) {
// It should be possible to abort the transaction without error
db.abortTransaction(txn);
}
// It should be possible to close the database cleanly
db.close();
}
private Database<Connection> open(boolean resume) throws Exception {
Database<Connection> db = new H2Database(new TestDatabaseConfig(testDir,
MAX_SIZE), new TestFileUtils(), new SystemClock());
if(!resume) TestUtils.deleteTestDirectory(testDir);
db.open();