Implement AvatarManager with unit and integration tests

This commit is contained in:
Torsten Grote
2020-11-20 13:51:41 -03:00
parent ef9b22670d
commit 83ac866cc1
11 changed files with 1322 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
package org.briarproject.briar.api.avatar;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.briar.api.media.Attachment;
import org.briarproject.briar.api.media.AttachmentHeader;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;
@NotNullByDefault
public interface AvatarManager {
/**
* The unique ID of the avatar client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.avatar");
/**
* The current major version of the avatar client.
*/
int MAJOR_VERSION = 0;
/**
* The current minor version of the avatar client.
*/
int MINOR_VERSION = 0;
/**
* Store a new profile image represented by the given InputStream
* and share it with all contacts.
*/
AttachmentHeader addAvatar(String contentType, InputStream in)
throws DbException, IOException;
/**
* Returns the current known profile image header for the given contact
* or null if none is known.
*/
@Nullable
AttachmentHeader getAvatarHeader(Contact c) throws DbException;
/**
* Returns our current profile image header or null if none has been added.
*/
@Nullable
AttachmentHeader getMyAvatarHeader() throws DbException;
/**
* Returns the profile image attachment for the given header.
*/
Attachment getAvatar(AttachmentHeader h) throws DbException;
}

View File

@@ -0,0 +1,33 @@
package org.briarproject.briar.api.avatar.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.media.AttachmentHeader;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a new avatar is received.
*/
@Immutable
@NotNullByDefault
public class AvatarUpdatedEvent extends Event {
private final ContactId contactId;
private final AttachmentHeader attachmentHeader;
public AvatarUpdatedEvent(ContactId contactId,
AttachmentHeader attachmentHeader) {
this.contactId = contactId;
this.attachmentHeader = attachmentHeader;
}
public ContactId getContactId() {
return contactId;
}
public AttachmentHeader getAttachmentHeader() {
return attachmentHeader;
}
}

View File

@@ -0,0 +1,14 @@
package org.briarproject.briar.avatar;
interface AvatarConstants {
// Message type constants
int MSG_TYPE_UPDATE = 0;
// Metadata keys for groups
String GROUP_KEY_CONTACT_ID = "contactId";
// Message metadata keys
String MSG_KEY_VERSION = "version";
}

View File

@@ -0,0 +1,312 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.avatar.AvatarManager;
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
import org.briarproject.briar.api.media.Attachment;
import org.briarproject.briar.api.media.AttachmentHeader;
import org.briarproject.briar.api.media.FileTooBigException;
import org.briarproject.briar.api.media.InvalidAttachmentException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
@Immutable
@NotNullByDefault
class AvatarManagerImpl implements AvatarManager, OpenDatabaseHook, ContactHook,
ClientVersioningHook, IncomingMessageHook {
private final DatabaseComponent db;
private final IdentityManager identityManager;
private final ClientHelper clientHelper;
private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser;
private final GroupFactory groupFactory;
private final Clock clock;
@Inject
AvatarManagerImpl(
DatabaseComponent db,
IdentityManager identityManager,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser,
GroupFactory groupFactory,
Clock clock) {
this.db = db;
this.identityManager = identityManager;
this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser;
this.groupFactory = groupFactory;
this.clock = clock;
}
@Override
public void onDatabaseOpened(Transaction txn) throws DbException {
// Create our avatar group if necessary
LocalAuthor a = identityManager.getLocalAuthor(txn);
Group ourGroup = getGroup(a.getId());
if (db.containsGroup(txn, ourGroup.getId())) return;
db.addGroup(txn, ourGroup);
// Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group theirGroup = getGroup(c.getAuthor().getId());
db.addGroup(txn, theirGroup);
// Attach the contact ID to the group
BdfDictionary d = new BdfDictionary();
d.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, theirGroup.getId(), d);
} catch (FormatException e) {
throw new AssertionError(e);
}
// Apply the client's visibility to our and their group
Group ourGroup = getOurGroup(txn);
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), ourGroup.getId(), client);
db.setGroupVisibility(txn, c.getId(), theirGroup.getId(), client);
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getGroup(c.getAuthor().getId()));
}
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to our and the contact group
Group ourGroup = getOurGroup(txn);
Group theirGroup = getGroup(c.getAuthor().getId());
db.setGroupVisibility(txn, c.getId(), ourGroup.getId(), v);
db.setGroupVisibility(txn, c.getId(), theirGroup.getId(), v);
}
@Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException {
try {
// Find the latest update, if any
BdfDictionary d = metadataParser.parse(meta);
LatestUpdate latest = findLatest(txn, m.getGroupId());
if (latest != null) {
if (d.getLong(MSG_KEY_VERSION) > latest.version) {
// This update is newer - delete the previous update
db.deleteMessage(txn, latest.messageId);
db.deleteMessageMetadata(txn, latest.messageId);
} else {
// We've already received a newer update - delete this one
db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId());
return false; // don't broadcast update
}
}
ContactId contactId = getContactId(txn, m.getGroupId());
String contentType = d.getString(MSG_KEY_CONTENT_TYPE);
AttachmentHeader a = new AttachmentHeader(m.getId(), contentType);
txn.attach(new AvatarUpdatedEvent(contactId, a));
} catch (FormatException e) {
throw new InvalidMessageException(e);
}
return false;
}
@Override
public AttachmentHeader addAvatar(String contentType, InputStream in)
throws DbException, IOException {
// find latest avatar
GroupId groupId;
LatestUpdate latest;
Transaction txn = db.startTransaction(true);
try {
groupId = getOurGroup(txn).getId();
// TODO this might not be the latest anymore at the end of this method
// Can we run everything in one transaction? Probably not.
latest = findLatest(txn, groupId);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
long version = latest == null ? 0 : latest.version + 1;
// 0.0: Message Type, Version, Content-Type
// TODO do we need to add the message type explicitly?
BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType);
byte[] descriptor = clientHelper.toByteArray(list);
// add BdfList and stream content to body
ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
bodyOut.write(descriptor);
copyAndClose(in, bodyOut);
if (bodyOut.size() > MAX_MESSAGE_BODY_LENGTH)
throw new FileTooBigException();
// assemble message
byte[] body = bodyOut.toByteArray();
long timestamp = clock.currentTimeMillis();
Message m = clientHelper.createMessage(groupId, timestamp, body);
// add metadata to message
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_VERSION, version);
meta.put(MSG_KEY_CONTENT_TYPE, contentType);
meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptor.length);
// send message
db.transaction(false, txn2 -> {
if (latest != null) {
// delete previous update
db.deleteMessage(txn2, latest.messageId);
db.deleteMessageMetadata(txn2, latest.messageId);
}
clientHelper.addLocalMessage(txn2, m, meta, true, false);
});
return new AttachmentHeader(m.getId(), contentType);
}
@Nullable
@Override
public AttachmentHeader getAvatarHeader(Contact c) throws DbException {
try {
Group g = getGroup(c.getAuthor().getId());
return db.transactionWithNullableResult(true, txn ->
getAvatarHeader(txn, g.getId())
);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Nullable
@Override
public AttachmentHeader getMyAvatarHeader() throws DbException {
try {
return db.transactionWithNullableResult(true, txn -> {
Group g = getOurGroup(txn);
return getAvatarHeader(txn, g.getId());
});
} catch (FormatException e) {
throw new DbException(e);
}
}
@Nullable
private AttachmentHeader getAvatarHeader(Transaction txn, GroupId groupId)
throws DbException, FormatException {
LatestUpdate latest = findLatest(txn, groupId);
if (latest == null) return null;
return new AttachmentHeader(latest.messageId, latest.contentType);
}
@Override
public Attachment getAvatar(AttachmentHeader h) throws DbException {
MessageId m = h.getMessageId();
byte[] body = clientHelper.getMessage(m).getBody();
try {
BdfDictionary meta = clientHelper.getMessageMetadataAsDictionary(m);
String contentType = meta.getString(MSG_KEY_CONTENT_TYPE);
if (!contentType.equals(h.getContentType()))
throw new InvalidAttachmentException();
int offset = meta.getLong(MSG_KEY_DESCRIPTOR_LENGTH).intValue();
return new Attachment(h, new ByteArrayInputStream(body, offset,
body.length - offset));
} catch (FormatException e) {
throw new DbException(e);
}
}
@Nullable
private LatestUpdate findLatest(Transaction txn, GroupId g)
throws DbException, FormatException {
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Map.Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue();
long version = meta.getLong(MSG_KEY_VERSION);
String contentType = meta.getString(MSG_KEY_CONTENT_TYPE);
return new LatestUpdate(e.getKey(), version, contentType);
}
return null;
}
private ContactId getContactId(Transaction txn, GroupId g)
throws DbException {
try {
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
} catch (FormatException e) {
throw new DbException(e);
}
}
private Group getOurGroup(Transaction txn) throws DbException {
LocalAuthor a = identityManager.getLocalAuthor(txn);
return getGroup(a.getId());
}
private Group getGroup(AuthorId authorId) {
return groupFactory
.createGroup(CLIENT_ID, MAJOR_VERSION, authorId.getBytes());
}
private static class LatestUpdate {
private final MessageId messageId;
private final long version;
private final String contentType;
private LatestUpdate(MessageId messageId, long version,
String contentType) {
this.messageId = messageId;
this.version = version;
this.contentType = contentType;
}
}
}

View File

@@ -0,0 +1,61 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.validation.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.avatar.AvatarManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.briar.api.avatar.AvatarManager.CLIENT_ID;
import static org.briarproject.briar.api.avatar.AvatarManager.MAJOR_VERSION;
import static org.briarproject.briar.api.avatar.AvatarManager.MINOR_VERSION;
@Module
public class AvatarModule {
public static class EagerSingletons {
@Inject
AvatarValidator avatarValidator;
@Inject
AvatarManager avatarManager;
}
@Provides
@Singleton
AvatarValidator provideAvatarValidator(ValidationManager validationManager,
BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder,
Clock clock) {
AvatarValidator introductionValidator =
new AvatarValidator(bdfReaderFactory, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
introductionValidator);
return introductionValidator;
}
@Provides
@Singleton
AvatarManager provideAvatarManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
ValidationManager validationManager,
ClientVersioningManager clientVersioningManager,
AvatarManagerImpl avatarManager) {
lifecycleManager.registerOpenDatabaseHook(avatarManager);
contactManager.registerContactHook(avatarManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
MAJOR_VERSION, avatarManager);
clientVersioningManager.registerClient(CLIENT_ID,
MAJOR_VERSION, MINOR_VERSION, avatarManager);
return avatarManager;
}
}

View File

@@ -0,0 +1,96 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.media.CountingInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
@Immutable
@NotNullByDefault
class AvatarValidator implements MessageValidator {
private final BdfReaderFactory bdfReaderFactory;
private final MetadataEncoder metadataEncoder;
private final Clock clock;
AvatarValidator(BdfReaderFactory bdfReaderFactory,
MetadataEncoder metadataEncoder, Clock clock) {
this.bdfReaderFactory = bdfReaderFactory;
this.metadataEncoder = metadataEncoder;
this.clock = clock;
}
@Override
public MessageContext validateMessage(Message m, Group g)
throws InvalidMessageException {
// Reject the message if it's too far in the future
long now = clock.currentTimeMillis();
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) {
throw new InvalidMessageException(
"Timestamp is too far in the future");
}
try {
InputStream in = new ByteArrayInputStream(m.getBody());
CountingInputStream countIn =
new CountingInputStream(in, MAX_MESSAGE_BODY_LENGTH);
BdfReader reader = bdfReaderFactory.createReader(countIn);
BdfList list = reader.readList();
long bytesRead = countIn.getBytesRead();
BdfDictionary d = validateUpdate(list, bytesRead);
Metadata meta = metadataEncoder.encode(d);
return new MessageContext(meta);
} catch (IOException e) {
throw new InvalidMessageException(e);
}
}
private BdfDictionary validateUpdate(BdfList body, long descriptorLength)
throws FormatException {
// 0.0: Message Type, Version, Content-Type
checkSize(body, 3);
// Message Type
long messageType = body.getLong(0);
if (messageType != MSG_TYPE_UPDATE) throw new FormatException();
// Version
long version = body.getLong(1);
if (version < 0) throw new FormatException();
// Content-Type
String contentType = body.getString(2);
checkLength(contentType, 1, MAX_CONTENT_TYPE_BYTES);
// Return the metadata
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_VERSION, version);
meta.put(MSG_KEY_CONTENT_TYPE, contentType);
meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptorLength);
return meta;
}
}

View File

@@ -0,0 +1,368 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.EventAction;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
import org.briarproject.briar.api.media.AttachmentHeader;
import org.jmock.Expectations;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.avatar.AvatarManager.CLIENT_ID;
import static org.briarproject.briar.api.avatar.AvatarManager.MAJOR_VERSION;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class AvatarManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final ClientVersioningManager clientVersioningManager =
context.mock(ClientVersioningManager.class);
private final MetadataParser metadataParser =
context.mock(MetadataParser.class);
private final GroupFactory groupFactory = context.mock(GroupFactory.class);
private final Clock clock = context.mock(Clock.class);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final GroupId localGroupId = localGroup.getId();
private final LocalAuthor localAuthor = getLocalAuthor();
private final Contact contact = getContact();
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION, 32);
private final GroupId contactGroupId = contactGroup.getId();
private final Message ourMsg = getMessage(localGroupId);
private final Message contactMsg = getMessage(contactGroupId);
private final Metadata meta = new Metadata();
private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
private final BdfDictionary metaDict = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
private final AvatarManagerImpl avatarManager =
new AvatarManagerImpl(db, identityManager, clientHelper,
clientVersioningManager, metadataParser, groupFactory,
clock);
@Test
public void testOpenDatabaseHook() throws DbException, FormatException {
Transaction txn = new Transaction(null, false);
// local group already exists, so nothing more to do
expectCreateGroup(localAuthor.getId(), localGroup);
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsGroup(txn, localGroupId);
will(returnValue(true));
}});
avatarManager.onDatabaseOpened(txn);
// local group does not exist, so we need to set things up for contacts
expectCreateGroup(localAuthor.getId(), localGroup);
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsGroup(txn, localGroupId);
will(returnValue(false));
oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
}});
expectAddingContact(txn, contact, SHARED);
avatarManager.onDatabaseOpened(txn);
}
@Test
public void testAddingContact() throws DbException, FormatException {
Transaction txn = new Transaction(null, false);
expectAddingContact(txn, contact, INVISIBLE);
avatarManager.addingContact(txn, contact);
Contact contact2 = getContact();
expectAddingContact(txn, contact2, VISIBLE);
avatarManager.addingContact(txn, contact2);
}
@Test
public void testRemovingContact() throws DbException {
Transaction txn = new Transaction(null, false);
expectCreateGroup(contact.getAuthor().getId(), contactGroup);
context.checking(new Expectations() {{
oneOf(db).removeGroup(txn, contactGroup);
}});
avatarManager.removingContact(txn, contact);
}
@Test
public void testOnClientVisibilityChanging() throws DbException {
Transaction txn = new Transaction(null, false);
expectGetOurGroup(txn);
expectCreateGroup(contact.getAuthor().getId(), contactGroup);
expectSetGroupVisibility(txn, contact.getId(), localGroupId, VISIBLE);
expectSetGroupVisibility(txn, contact.getId(), contactGroupId, VISIBLE);
avatarManager.onClientVisibilityChanging(txn, contact, VISIBLE);
expectGetOurGroup(txn);
expectCreateGroup(contact.getAuthor().getId(), contactGroup);
expectSetGroupVisibility(txn, contact.getId(), localGroupId, SHARED);
expectSetGroupVisibility(txn, contact.getId(), contactGroupId, SHARED);
avatarManager.onClientVisibilityChanging(txn, contact, SHARED);
expectGetOurGroup(txn);
expectCreateGroup(contact.getAuthor().getId(), contactGroup);
expectSetGroupVisibility(txn, contact.getId(), localGroupId, INVISIBLE);
expectSetGroupVisibility(txn, contact.getId(), contactGroupId,
INVISIBLE);
avatarManager.onClientVisibilityChanging(txn, contact, INVISIBLE);
}
@Test
public void testFirstIncomingMessage()
throws DbException, InvalidMessageException, FormatException {
Transaction txn = new Transaction(null, false);
BdfDictionary d = BdfDictionary.of(
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
context.checking(new Expectations() {{
oneOf(metadataParser).parse(meta);
will(returnValue(d));
}});
expectFindLatest(txn, contactGroupId, new MessageId(getRandomId()),
null);
expectGetContactId(txn, contactGroupId, contact.getId());
assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
assertEquals(1, txn.getActions().size());
Event event = ((EventAction) txn.getActions().get(0)).getEvent();
AvatarUpdatedEvent avatarUpdatedEvent = (AvatarUpdatedEvent) event;
assertEquals(contactMsg.getId(),
avatarUpdatedEvent.getAttachmentHeader().getMessageId());
assertEquals(contact.getId(), avatarUpdatedEvent.getContactId());
}
@Test
public void testNewerIncomingMessage()
throws DbException, InvalidMessageException, FormatException {
Transaction txn = new Transaction(null, false);
BdfDictionary d = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
MessageId latestMsgId = new MessageId(getRandomId());
BdfDictionary latest = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 0),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
context.checking(new Expectations() {{
oneOf(metadataParser).parse(meta);
will(returnValue(d));
// delete old "latest" message
oneOf(db).deleteMessage(txn, latestMsgId);
oneOf(db).deleteMessageMetadata(txn, latestMsgId);
}});
expectFindLatest(txn, contactGroupId, latestMsgId, latest);
expectGetContactId(txn, contactGroupId, contact.getId());
assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
// event to broadcast
assertEquals(1, txn.getActions().size());
}
@Test
public void testDeleteOlderIncomingMessage()
throws DbException, InvalidMessageException, FormatException {
Transaction txn = new Transaction(null, false);
BdfDictionary d = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 0),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
MessageId latestMsgId = new MessageId(getRandomId());
BdfDictionary latest = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 1),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
);
context.checking(new Expectations() {{
oneOf(metadataParser).parse(meta);
will(returnValue(d));
// delete older incoming message
oneOf(db).deleteMessage(txn, contactMsg.getId());
oneOf(db).deleteMessageMetadata(txn, contactMsg.getId());
}});
expectFindLatest(txn, contactGroupId, latestMsgId, latest);
assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
// no event to broadcast
assertEquals(0, txn.getActions().size());
}
@Test
public void testAddAvatar() throws Exception {
byte[] avatarBytes = getRandomBytes(42);
InputStream inputStream = new ByteArrayInputStream(avatarBytes);
Transaction txn = new Transaction(null, true);
Transaction txn2 = new Transaction(null, false);
long latestVersion = metaDict.getLong(MSG_KEY_VERSION);
long version = latestVersion + 1;
BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType);
long now = System.currentTimeMillis();
Message newMsg = getMessage(localGroupId);
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, version),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0)
);
context.checking(new DbExpectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
oneOf(clientHelper).toByteArray(list);
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(with(equal(localGroupId)),
with(equal(now)), with(any(byte[].class)));
will(returnValue(newMsg));
oneOf(db).transaction(with(false), withDbRunnable(txn2));
oneOf(db).deleteMessage(txn2, ourMsg.getId());
oneOf(db).deleteMessageMetadata(txn2, ourMsg.getId());
oneOf(clientHelper)
.addLocalMessage(txn2, newMsg, newMeta, true, false);
}});
expectGetOurGroup(txn);
expectFindLatest(txn, localGroupId, ourMsg.getId(), metaDict);
AttachmentHeader header =
avatarManager.addAvatar(contentType, inputStream);
assertEquals(newMsg.getId(), header.getMessageId());
assertEquals(contentType, header.getContentType());
}
private void expectGetContactId(Transaction txn, GroupId groupId,
ContactId contactId) throws DbException, FormatException {
BdfDictionary d = BdfDictionary
.of(new BdfEntry(GROUP_KEY_CONTACT_ID, contactId.getInt()));
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn, groupId);
will(returnValue(d));
}});
}
private void expectFindLatest(Transaction txn, GroupId groupId,
MessageId messageId, @Nullable BdfDictionary d)
throws DbException, FormatException {
Map<MessageId, BdfDictionary> map = new HashMap<>();
if (d != null) map.put(messageId, d);
context.checking(new Expectations() {{
oneOf(clientHelper)
.getMessageMetadataAsDictionary(txn, groupId);
will(returnValue(map));
}});
}
private void expectSetGroupVisibility(Transaction txn, ContactId contactId,
GroupId groupId, Visibility v) throws DbException {
context.checking(new Expectations() {{
oneOf(db).setGroupVisibility(txn, contactId, groupId, v);
}});
}
private void expectAddingContact(Transaction txn, Contact c, Visibility v)
throws DbException, FormatException {
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, c.getId().getInt())
);
expectGetOurGroup(txn);
context.checking(new Expectations() {{
oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
c.getAuthor().getId().getBytes());
will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup);
oneOf(clientHelper)
.mergeGroupMetadata(txn, contactGroupId, groupMeta);
oneOf(clientVersioningManager)
.getClientVisibility(txn, c.getId(), CLIENT_ID,
MAJOR_VERSION);
will(returnValue(v));
}});
expectSetGroupVisibility(txn, c.getId(), localGroupId, v);
expectSetGroupVisibility(txn, c.getId(), contactGroupId, v);
}
private void expectGetOurGroup(Transaction txn) throws DbException {
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
}});
expectCreateGroup(localAuthor.getId(), localGroup);
}
private void expectCreateGroup(AuthorId authorId, Group group) {
context.checking(new Expectations() {{
oneOf(groupFactory)
.createGroup(CLIENT_ID, MAJOR_VERSION, authorId.getBytes());
will(returnValue(group));
}});
}
}

View File

@@ -0,0 +1,183 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.test.TestDatabaseConfigModule;
import org.briarproject.briar.api.avatar.AvatarManager;
import org.briarproject.briar.api.media.Attachment;
import org.briarproject.briar.api.media.AttachmentHeader;
import org.briarproject.briar.test.BriarIntegrationTest;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class AvatarManagerIntegrationTest
extends BriarIntegrationTest<BriarIntegrationTestComponent> {
private AvatarManager avatarManager0, avatarManager1;
private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
@Before
@Override
public void setUp() throws Exception {
super.setUp();
avatarManager0 = c0.getAvatarManager();
avatarManager1 = c1.getAvatarManager();
}
@Override
protected void createComponents() {
BriarIntegrationTestComponent component =
DaggerBriarIntegrationTestComponent.builder().build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(component);
component.inject(this);
c0 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t0Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c0);
c1 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t1Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c1);
c2 = DaggerBriarIntegrationTestComponent.builder()
.testDatabaseConfigModule(new TestDatabaseConfigModule(t2Dir))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c2);
}
@Test
public void testAddingAndSyncAvatars() throws Exception {
// Both contacts don't have avatars
assertNull(avatarManager0.getMyAvatarHeader());
assertNull(avatarManager1.getMyAvatarHeader());
// Both contacts don't see avatars for each other
assertNull(avatarManager0.getAvatarHeader(contact1From0));
assertNull(avatarManager1.getAvatarHeader(contact0From1));
// 0 adds avatar
byte[] avatar0bytes = getRandomBytes(42);
InputStream avatar0inputStream = new ByteArrayInputStream(avatar0bytes);
AttachmentHeader header0 =
avatarManager0.addAvatar(contentType, avatar0inputStream);
assertEquals(contentType, header0.getContentType());
// 0 sees their own avatar
header0 = avatarManager0.getMyAvatarHeader();
assertNotNull(header0);
assertEquals(contentType, header0.getContentType());
assertNotNull(header0.getMessageId());
// 0 can retrieve their own avatar
Attachment attachment0 = avatarManager0.getAvatar(header0);
assertEquals(contentType, attachment0.getHeader().getContentType());
assertStreamMatches(avatar0bytes, attachment0.getStream());
// send the avatar from 0 to 1
sync0To1(1, true);
// 1 also sees 0's avatar now
AttachmentHeader header0From1 =
avatarManager1.getAvatarHeader(contact0From1);
assertNotNull(header0From1);
assertEquals(contentType, header0From1.getContentType());
assertNotNull(header0From1.getMessageId());
// 1 can retrieve 0's avatar
Attachment attachment0From1 = avatarManager1.getAvatar(header0From1);
assertEquals(contentType,
attachment0From1.getHeader().getContentType());
assertStreamMatches(avatar0bytes, attachment0From1.getStream());
// 1 also adds avatar
String contentType1 = getRandomString(MAX_CONTENT_TYPE_BYTES);
byte[] avatar1bytes = getRandomBytes(42);
InputStream avatar1inputStream = new ByteArrayInputStream(avatar1bytes);
avatarManager1.addAvatar(contentType1, avatar1inputStream);
// send the avatar from 1 to 0
sync1To0(1, true);
// 0 sees 1's avatar now
AttachmentHeader header1From0 =
avatarManager0.getAvatarHeader(contact1From0);
assertNotNull(header1From0);
assertEquals(contentType1, header1From0.getContentType());
assertNotNull(header1From0.getMessageId());
// 0 can retrieve 1's avatar
Attachment attachment1From0 = avatarManager0.getAvatar(header1From0);
assertEquals(contentType1,
attachment1From0.getHeader().getContentType());
assertStreamMatches(avatar1bytes, attachment1From0.getStream());
}
@Test
public void testUpdatingAvatars() throws Exception {
// 0 adds avatar
byte[] avatar0bytes = getRandomBytes(42);
InputStream avatar0inputStream = new ByteArrayInputStream(avatar0bytes);
avatarManager0.addAvatar(contentType, avatar0inputStream);
// 0 can retrieve their own avatar
AttachmentHeader header0 = avatarManager0.getMyAvatarHeader();
assertNotNull(header0);
Attachment attachment0 = avatarManager0.getAvatar(header0);
assertStreamMatches(avatar0bytes, attachment0.getStream());
// send the avatar from 0 to 1
sync0To1(1, true);
// 1 only sees 0's avatar
AttachmentHeader header0From1 =
avatarManager1.getAvatarHeader(contact0From1);
assertNotNull(header0From1);
Attachment attachment0From1 = avatarManager1.getAvatar(header0From1);
assertStreamMatches(avatar0bytes, attachment0From1.getStream());
// 0 adds a new avatar
byte[] avatar0bytes2 = getRandomBytes(42);
InputStream avatar0inputStream2 =
new ByteArrayInputStream(avatar0bytes2);
avatarManager0.addAvatar(contentType, avatar0inputStream2);
// 0 now only sees their new avatar
header0 = avatarManager0.getMyAvatarHeader();
assertNotNull(header0);
attachment0 = avatarManager0.getAvatar(header0);
assertStreamMatches(avatar0bytes2, attachment0.getStream());
// send the new avatar from 0 to 1
sync0To1(1, true);
// 1 only sees 0's new avatar
header0From1 =
avatarManager1.getAvatarHeader(contact0From1);
assertNotNull(header0From1);
attachment0From1 = avatarManager1.getAvatar(header0From1);
assertStreamMatches(avatar0bytes2, attachment0From1.getStream());
}
private void assertStreamMatches(byte[] bytes, InputStream inputStream) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
copyAndClose(inputStream, outputStream);
assertArrayEquals(bytes, outputStream.toByteArray());
}
}

View File

@@ -0,0 +1,188 @@
package org.briarproject.briar.avatar;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.io.InputStream;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_CONTENT_TYPE;
import static org.briarproject.briar.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.junit.Assert.assertEquals;
public class AvatarValidatorTest extends BrambleMockTestCase {
private final BdfReaderFactory bdfReaderFactory =
context.mock(BdfReaderFactory.class);
private final MetadataEncoder metadataEncoder =
context.mock(MetadataEncoder.class);
private final Clock clock = context.mock(Clock.class);
private final BdfReader reader = context.mock(BdfReader.class);
private final Group group = getGroup(getClientId(), 123);
private final Message message = getMessage(group.getId());
private final long now = message.getTimestamp() + 1000;
private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
private final long version = System.currentTimeMillis();
private final BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, version),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
// Descriptor length is zero as the test doesn't read from the
// counting input stream
new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0L)
);
private final AvatarValidator validator =
new AvatarValidator(bdfReaderFactory, metadataEncoder, clock);
@Test(expected = InvalidMessageException.class)
public void testRejectsFarFutureTimestamp() throws Exception {
expectCheckTimestamp(message.getTimestamp() - MAX_CLOCK_DIFFERENCE - 1);
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsEmptyBody() throws Exception {
expectCheckTimestamp(now);
expectParseList(new BdfList());
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsTooShortBody() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, version));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsUnknownMessageType() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE + 1, version, contentType));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsNonLongVersion() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, "foo", contentType));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsNonStringContentType() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, 1337));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsEmptyContentType() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, ""));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsTooLongContentType() throws Exception {
String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES + 1);
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, contentType));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsTooLongBody() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, contentType, 1));
validator.validateMessage(message, group);
}
@Test(expected = InvalidMessageException.class)
public void testRejectsNegativeVersion() throws Exception {
expectCheckTimestamp(now);
expectParseList(BdfList.of(MSG_TYPE_UPDATE, -1, contentType));
validator.validateMessage(message, group);
}
@Test
public void testAcceptsUpdateMessage() throws Exception {
testAcceptsUpdateMessage(
BdfList.of(MSG_TYPE_UPDATE, version, contentType), meta);
}
@Test
public void testAcceptsZeroVersion() throws Exception {
BdfList body = BdfList.of(MSG_TYPE_UPDATE, 0L, contentType);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 0L),
new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0L)
);
testAcceptsUpdateMessage(body, meta);
}
private void testAcceptsUpdateMessage(BdfList body, BdfDictionary meta)
throws Exception {
expectCheckTimestamp(now);
expectParseList(body);
expectEncodeMetadata(meta);
MessageContext result = validator.validateMessage(message, group);
assertEquals(0, result.getDependencies().size());
}
private void expectCheckTimestamp(long now) {
context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(now));
}});
}
private void expectParseList(BdfList body) throws Exception {
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(with(any(InputStream.class)));
will(returnValue(reader));
oneOf(reader).readList();
will(returnValue(body));
}});
}
private void expectEncodeMetadata(BdfDictionary meta) throws Exception {
context.checking(new Expectations() {{
oneOf(metadataEncoder).encode(meta);
will(returnValue(new Metadata()));
}});
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.introduction;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
@@ -19,6 +20,7 @@ import dagger.Component;
@Component(modules = {
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
ForumModule.class,

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import org.briarproject.briar.api.avatar.AvatarManager;
import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager;
@@ -24,6 +25,7 @@ import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
@@ -41,6 +43,7 @@ import dagger.Component;
@Component(modules = {
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
ForumModule.class,
@@ -55,6 +58,8 @@ public interface BriarIntegrationTestComponent
void inject(BriarIntegrationTest<BriarIntegrationTestComponent> init);
void inject(AvatarModule.EagerSingletons init);
void inject(BlogModule.EagerSingletons init);
void inject(ForumModule.EagerSingletons init);
@@ -75,6 +80,8 @@ public interface BriarIntegrationTestComponent
IdentityManager getIdentityManager();
AvatarManager getAvatarManager();
ClientHelper getClientHelper();
ContactManager getContactManager();
@@ -117,6 +124,7 @@ public interface BriarIntegrationTestComponent
BriarIntegrationTestComponent c) {
BrambleCoreIntegrationTestEagerSingletons.Helper
.injectEagerSingletons(c);
c.inject(new AvatarModule.EagerSingletons());
c.inject(new BlogModule.EagerSingletons());
c.inject(new ForumModule.EagerSingletons());
c.inject(new GroupInvitationModule.EagerSingletons());