diff --git a/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java b/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..455c93603259bc875a5f05f53d5ba85405df72fd --- /dev/null +++ b/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java @@ -0,0 +1,283 @@ +package org.briarproject.introduction; + +import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.Bytes; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfEntry; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.identity.AuthorId; +import org.briarproject.api.introduction.IntroduceeProtocolState; +import org.briarproject.api.introduction.SessionId; +import org.briarproject.api.properties.TransportPropertyManager; +import org.briarproject.api.sync.ClientId; +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.system.Clock; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.lib.legacy.ClassImposteriser; +import org.junit.Test; + +import java.security.SecureRandom; + +import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.api.introduction.IntroduceeProtocolState.AWAIT_REQUEST; +import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT; +import static org.briarproject.api.introduction.IntroductionConstants.ANSWERED; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1; +import static org.briarproject.api.introduction.IntroductionConstants.EXISTS; +import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; +import static org.briarproject.api.introduction.IntroductionConstants.INTRODUCER; +import static org.briarproject.api.introduction.IntroductionConstants.LOCAL_AUTHOR_ID; +import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_ID; +import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME; +import static org.briarproject.api.introduction.IntroductionConstants.NAME; +import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE; +import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; +import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE; +import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.api.introduction.IntroductionConstants.STATE; +import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TIME; +import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; +import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class IntroduceeManagerTest extends BriarTestCase { + + final Mockery context; + final IntroduceeManager introduceeManager; + final DatabaseComponent db; + final CryptoComponent cryptoComponent; + final ClientHelper clientHelper; + final IntroductionGroupFactory introductionGroupFactory; + final MessageSender messageSender; + final TransportPropertyManager transportPropertyManager; + final AuthorFactory authorFactory; + final ContactManager contactManager; + final Clock clock; + final Contact introducer; + final Contact introducee1; + final Contact introducee2; + final Group localGroup1; + final Group introductionGroup1; + final Group introductionGroup2; + final Transaction txn; + final long time = 42L; + final Message localStateMessage; + final ClientId clientId; + final SessionId sessionId; + final Message message1; + + public IntroduceeManagerTest() { + context = new Mockery(); + context.setImposteriser(ClassImposteriser.INSTANCE); + messageSender = context.mock(MessageSender.class); + db = context.mock(DatabaseComponent.class); + cryptoComponent = context.mock(CryptoComponent.class); + clientHelper = context.mock(ClientHelper.class); + clock = context.mock(Clock.class); + introductionGroupFactory = + context.mock(IntroductionGroupFactory.class); + transportPropertyManager = context.mock(TransportPropertyManager.class); + authorFactory = context.mock(AuthorFactory.class); + contactManager = context.mock(ContactManager.class); + + introduceeManager = new IntroduceeManager(messageSender, db, + clientHelper, clock, cryptoComponent, transportPropertyManager, + authorFactory, contactManager, introductionGroupFactory); + + AuthorId authorId0 = new AuthorId(TestUtils.getRandomId()); + Author author0 = new Author(authorId0, "Introducer", + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + AuthorId localAuthorId = new AuthorId(TestUtils.getRandomId()); + ContactId contactId0 = new ContactId(234); + introducer = new Contact(contactId0, author0, localAuthorId, true); + + AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); + Author author1 = new Author(authorId1, "Introducee1", + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId()); + ContactId contactId1 = new ContactId(234); + introducee1 = new Contact(contactId1, author1, localAuthorId1, true); + + AuthorId authorId2 = new AuthorId(TestUtils.getRandomId()); + Author author2 = new Author(authorId2, "Introducee2", + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + ContactId contactId2 = new ContactId(235); + introducee2 = new Contact(contactId2, author2, localAuthorId, true); + + clientId = IntroductionManagerImpl.CLIENT_ID; + localGroup1 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + + sessionId = new SessionId(TestUtils.getRandomId()); + localStateMessage = new Message( + new MessageId(TestUtils.getRandomId()), + localGroup1.getId(), + time, + TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1) + ); + message1 = new Message( + new MessageId(TestUtils.getRandomId()), + introductionGroup1.getId(), + time, + TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1) + ); + + txn = new Transaction(null, false); + } + + @Test + public void testIncomingRequestMessage() + throws DbException, FormatException { + + final BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_REQUEST); + msg.put(GROUP_ID, introductionGroup1.getId()); + msg.put(SESSION_ID, sessionId); + msg.put(MESSAGE_ID, message1.getId()); + msg.put(MESSAGE_TIME, time); + msg.put(NAME, introducee2.getAuthor().getName()); + msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey()); + + final BdfDictionary state = + initializeSessionState(txn, introductionGroup1.getId(), msg); + + context.checking(new Expectations() {{ + oneOf(clientHelper).mergeMessageMetadata(txn, + localStateMessage.getId(), state); + }}); + + introduceeManager.incomingMessage(txn, state, msg); + + context.assertIsSatisfied(); + + assertFalse(txn.isComplete()); + } + + @Test + public void testIncomingResponseMessage() + throws DbException, FormatException { + + final BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_RESPONSE); + msg.put(GROUP_ID, introductionGroup1.getId()); + msg.put(SESSION_ID, sessionId); + msg.put(MESSAGE_ID, message1.getId()); + msg.put(MESSAGE_TIME, time); + msg.put(NAME, introducee2.getAuthor().getName()); + msg.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey()); + + final BdfDictionary state = + initializeSessionState(txn, introductionGroup1.getId(), msg); + state.put(STATE, IntroduceeProtocolState.AWAIT_RESPONSES.ordinal()); + + // turn request message into a response + msg.put(ACCEPT, true); + msg.put(TIME, time); + msg.put(E_PUBLIC_KEY, TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + msg.put(TRANSPORT, new BdfDictionary()); + + context.checking(new Expectations() {{ + oneOf(clientHelper).mergeMessageMetadata(txn, + localStateMessage.getId(), state); + }}); + + introduceeManager.incomingMessage(txn, state, msg); + + context.assertIsSatisfied(); + + assertFalse(txn.isComplete()); + } + + private BdfDictionary initializeSessionState(final Transaction txn, + final GroupId groupId, final BdfDictionary msg) + throws DbException, FormatException { + + final SecureRandom secureRandom = context.mock(SecureRandom.class); + final Bytes salt = new Bytes(new byte[64]); + final BdfDictionary groupMetadata = BdfDictionary.of( + new BdfEntry(CONTACT, introducee1.getId().getInt()) + ); + final boolean contactExists = true; + final BdfDictionary state = new BdfDictionary(); + state.put(STORAGE_ID, localStateMessage.getId()); + state.put(STATE, AWAIT_REQUEST.getValue()); + state.put(ROLE, ROLE_INTRODUCEE); + state.put(GROUP_ID, groupId); + state.put(INTRODUCER, introducer.getAuthor().getName()); + state.put(CONTACT_ID_1, introducer.getId().getInt()); + state.put(LOCAL_AUTHOR_ID, introducer.getLocalAuthorId().getBytes()); + state.put(NOT_OUR_RESPONSE, localStateMessage.getId()); + state.put(ANSWERED, false); + state.put(EXISTS, true); + state.put(REMOTE_AUTHOR_ID, introducee2.getAuthor().getId()); + + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(time)); + oneOf(cryptoComponent).getSecureRandom(); + will(returnValue(secureRandom)); + oneOf(secureRandom).nextBytes(salt.getBytes()); + oneOf(introductionGroupFactory).createLocalGroup(); + will(returnValue(localGroup1)); + oneOf(clientHelper) + .createMessage(localGroup1.getId(), time, BdfList.of(salt)); + will(returnValue(localStateMessage)); + + // who is making the introduction? who is the introducer? + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + groupId); + will(returnValue(groupMetadata)); + oneOf(db).getContact(txn, introducer.getId()); + will(returnValue(introducer)); + + // create remote author to check if contact exists + oneOf(authorFactory).createAuthor(introducee2.getAuthor().getName(), + introducee2.getAuthor().getPublicKey()); + will(returnValue(introducee2.getAuthor())); + oneOf(contactManager) + .contactExists(txn, introducee2.getAuthor().getId(), + introducer.getLocalAuthorId()); + will(returnValue(contactExists)); + + // store session state + oneOf(clientHelper) + .addLocalMessage(txn, localStateMessage, clientId, state, + false); + }}); + + BdfDictionary result = introduceeManager.initialize(txn, groupId, msg); + + context.assertIsSatisfied(); + return result; + } + +} diff --git a/briar-tests/src/org/briarproject/introduction/IntroducerManagerTest.java b/briar-tests/src/org/briarproject/introduction/IntroducerManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9f4cc21746de77fe94fa1d63e75171ffc9d28d9e --- /dev/null +++ b/briar-tests/src/org/briarproject/introduction/IntroducerManagerTest.java @@ -0,0 +1,189 @@ +package org.briarproject.introduction; + +import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.Bytes; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorId; +import org.briarproject.api.sync.ClientId; +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.system.Clock; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.lib.legacy.ClassImposteriser; +import org.junit.Test; + +import java.security.SecureRandom; + +import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.api.introduction.IntroducerProtocolState.AWAIT_RESPONSES; +import static org.briarproject.api.introduction.IntroducerProtocolState.PREPARE_REQUESTS; +import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_1; +import static org.briarproject.api.introduction.IntroductionConstants.AUTHOR_ID_2; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_1; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_2; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_1; +import static org.briarproject.api.introduction.IntroductionConstants.CONTACT_ID_2; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2; +import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TIME; +import static org.briarproject.api.introduction.IntroductionConstants.NAME; +import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER; +import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.api.introduction.IntroductionConstants.STATE; +import static org.briarproject.api.introduction.IntroductionConstants.STORAGE_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; +import static org.junit.Assert.assertFalse; + +public class IntroducerManagerTest extends BriarTestCase { + + final Mockery context; + final IntroducerManager introducerManager; + final CryptoComponent cryptoComponent; + final ClientHelper clientHelper; + final IntroductionGroupFactory introductionGroupFactory; + final MessageSender messageSender; + final Clock clock; + final Contact introducee1; + final Contact introducee2; + final Group localGroup0; + final Group introductionGroup1; + final Group introductionGroup2; + + public IntroducerManagerTest() { + context = new Mockery(); + context.setImposteriser(ClassImposteriser.INSTANCE); + messageSender = context.mock(MessageSender.class); + cryptoComponent = context.mock(CryptoComponent.class); + clientHelper = context.mock(ClientHelper.class); + clock = context.mock(Clock.class); + introductionGroupFactory = + context.mock(IntroductionGroupFactory.class); + + introducerManager = + new IntroducerManager(messageSender, clientHelper, clock, + cryptoComponent, introductionGroupFactory); + + AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); + Author author1 = new Author(authorId1, "Introducee1", + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId()); + ContactId contactId1 = new ContactId(234); + introducee1 = new Contact(contactId1, author1, localAuthorId1, true); + + AuthorId authorId2 = new AuthorId(TestUtils.getRandomId()); + Author author2 = new Author(authorId2, "Introducee2", + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId()); + ContactId contactId2 = new ContactId(235); + introducee2 = new Contact(contactId2, author2, localAuthorId2, true); + + localGroup0 = new Group(new GroupId(TestUtils.getRandomId()), + getClientId(), new byte[0]); + introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()), + getClientId(), new byte[0]); + introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()), + getClientId(), new byte[0]); + + context.assertIsSatisfied(); + } + + @Test + public void testMakeIntroduction() throws DbException, FormatException { + final Transaction txn = new Transaction(null, false); + final long time = 42L; + context.setImposteriser(ClassImposteriser.INSTANCE); + final SecureRandom secureRandom = context.mock(SecureRandom.class); + final Bytes salt = new Bytes(new byte[64]); + final Message msg = new Message(new MessageId(TestUtils.getRandomId()), + localGroup0.getId(), time, TestUtils.getRandomBytes(64)); + final BdfDictionary state = new BdfDictionary(); + state.put(SESSION_ID, msg.getId()); + state.put(STORAGE_ID, msg.getId()); + state.put(STATE, PREPARE_REQUESTS.getValue()); + state.put(ROLE, ROLE_INTRODUCER); + state.put(GROUP_ID_1, introductionGroup1.getId()); + state.put(GROUP_ID_2, introductionGroup2.getId()); + state.put(CONTACT_1, introducee1.getAuthor().getName()); + state.put(CONTACT_2, introducee2.getAuthor().getName()); + state.put(CONTACT_ID_1, introducee1.getId().getInt()); + state.put(CONTACT_ID_2, introducee2.getId().getInt()); + state.put(AUTHOR_ID_1, introducee1.getAuthor().getId()); + state.put(AUTHOR_ID_2, introducee2.getAuthor().getId()); + final BdfDictionary state2 = (BdfDictionary) state.clone(); + state2.put(STATE, AWAIT_RESPONSES.getValue()); + + final BdfDictionary msg1 = new BdfDictionary(); + msg1.put(TYPE, TYPE_REQUEST); + msg1.put(SESSION_ID, state.getRaw(SESSION_ID)); + msg1.put(GROUP_ID, state.getRaw(GROUP_ID_1)); + msg1.put(NAME, state.getString(CONTACT_2)); + msg1.put(PUBLIC_KEY, introducee2.getAuthor().getPublicKey()); + final BdfDictionary msg1send = (BdfDictionary) msg1.clone(); + msg1send.put(MESSAGE_TIME, time); + + final BdfDictionary msg2 = new BdfDictionary(); + msg2.put(TYPE, TYPE_REQUEST); + msg2.put(SESSION_ID, state.getRaw(SESSION_ID)); + msg2.put(GROUP_ID, state.getRaw(GROUP_ID_2)); + msg2.put(NAME, state.getString(CONTACT_1)); + msg2.put(PUBLIC_KEY, introducee1.getAuthor().getPublicKey()); + final BdfDictionary msg2send = (BdfDictionary) msg2.clone(); + msg2send.put(MESSAGE_TIME, time); + + context.checking(new Expectations() {{ + // initialize and store session state + oneOf(clock).currentTimeMillis(); + will(returnValue(time)); + oneOf(cryptoComponent).getSecureRandom(); + will(returnValue(secureRandom)); + oneOf(secureRandom).nextBytes(salt.getBytes()); + oneOf(introductionGroupFactory).createLocalGroup(); + will(returnValue(localGroup0)); + oneOf(clientHelper).createMessage(localGroup0.getId(), time, + BdfList.of(salt)); + will(returnValue(msg)); + oneOf(introductionGroupFactory) + .createIntroductionGroup(introducee1); + will(returnValue(introductionGroup1)); + oneOf(introductionGroupFactory) + .createIntroductionGroup(introducee2); + will(returnValue(introductionGroup2)); + oneOf(clientHelper).addLocalMessage(txn, msg, getClientId(), state, + false); + + // send message + oneOf(clientHelper).mergeMessageMetadata(txn, msg.getId(), state2); + oneOf(messageSender).sendMessage(txn, msg1send); + oneOf(messageSender).sendMessage(txn, msg2send); + }}); + + introducerManager + .makeIntroduction(txn, introducee1, introducee2, null, time); + + context.assertIsSatisfied(); + + assertFalse(txn.isComplete()); + } + + private ClientId getClientId() { + return IntroductionManagerImpl.CLIENT_ID; + } + +} diff --git a/briar-tests/src/org/briarproject/introduction/IntroductionManagerImplTest.java b/briar-tests/src/org/briarproject/introduction/IntroductionManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f6b5e90e88d5f375bc7a325722873ef9abf2c1b9 --- /dev/null +++ b/briar-tests/src/org/briarproject/introduction/IntroductionManagerImplTest.java @@ -0,0 +1,287 @@ +package org.briarproject.introduction; + +import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.clients.MessageQueueManager; +import org.briarproject.api.clients.PrivateGroupFactory; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfEntry; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.MetadataEncoder; +import org.briarproject.api.data.MetadataParser; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorId; +import org.briarproject.api.introduction.SessionId; +import org.briarproject.api.sync.ClientId; +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.MessageStatus; +import org.briarproject.api.system.Clock; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.lib.legacy.ClassImposteriser; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import static junit.framework.TestCase.assertTrue; +import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_1; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID_2; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE; +import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER; +import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; +import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; +import static org.junit.Assert.assertFalse; + +public class IntroductionManagerImplTest extends BriarTestCase { + + final Mockery context; + final IntroductionManagerImpl introductionManager; + final IntroducerManager introducerManager; + final IntroduceeManager introduceeManager; + final DatabaseComponent db; + final PrivateGroupFactory privateGroupFactory; + final ClientHelper clientHelper; + final MetadataEncoder metadataEncoder; + final MessageQueueManager messageQueueManager; + final IntroductionGroupFactory introductionGroupFactory; + final Clock clock; + final SessionId sessionId = new SessionId(TestUtils.getRandomId()); + final long time = 42L; + final Contact introducee1; + final Contact introducee2; + final Group localGroup0; + final Group introductionGroup1; + final Group introductionGroup2; + final Message message1; + Transaction txn; + + public IntroductionManagerImplTest() { + AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); + Author author1 = new Author(authorId1, "Introducee1", + new byte[MAX_PUBLIC_KEY_LENGTH]); + AuthorId localAuthorId1 = new AuthorId(TestUtils.getRandomId()); + ContactId contactId1 = new ContactId(234); + introducee1 = new Contact(contactId1, author1, localAuthorId1, true); + + AuthorId authorId2 = new AuthorId(TestUtils.getRandomId()); + Author author2 = new Author(authorId2, "Introducee2", + new byte[MAX_PUBLIC_KEY_LENGTH]); + AuthorId localAuthorId2 = new AuthorId(TestUtils.getRandomId()); + ContactId contactId2 = new ContactId(235); + introducee2 = new Contact(contactId2, author2, localAuthorId2, true); + + ClientId clientId = new ClientId(TestUtils.getRandomId()); + localGroup0 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()), + clientId, new byte[0]); + + message1 = new Message( + new MessageId(TestUtils.getRandomId()), + introductionGroup1.getId(), + time, + TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1) + ); + + // mock ALL THE THINGS!!! + context = new Mockery(); + context.setImposteriser(ClassImposteriser.INSTANCE); + introducerManager = context.mock(IntroducerManager.class); + introduceeManager = context.mock(IntroduceeManager.class); + db = context.mock(DatabaseComponent.class); + privateGroupFactory = context.mock(PrivateGroupFactory.class); + clientHelper = context.mock(ClientHelper.class); + metadataEncoder = + context.mock(MetadataEncoder.class); + messageQueueManager = + context.mock(MessageQueueManager.class); + MetadataParser metadataParser = context.mock(MetadataParser.class); + introductionGroupFactory = context.mock(IntroductionGroupFactory.class); + clock = context.mock(Clock.class); + + introductionManager = new IntroductionManagerImpl( + db, clientHelper, metadataParser, clock, introducerManager, + introduceeManager, introductionGroupFactory + ); + } + + @Test + public void testMakeIntroduction() throws DbException, FormatException { + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(introducerManager) + .makeIntroduction(txn, introducee1, introducee2, null, + time); + oneOf(db).endTransaction(txn); + }}); + + introductionManager + .makeIntroduction(introducee1, introducee2, null, time); + + context.assertIsSatisfied(); + assertTrue(txn.isComplete()); + } + + @Test + public void testAcceptIntroduction() throws DbException, FormatException { + final BdfDictionary state = BdfDictionary.of( + new BdfEntry(GROUP_ID_1, introductionGroup1.getId()), + new BdfEntry(GROUP_ID_2, introductionGroup2.getId()) + ); + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(db).getContact(txn, introducee1.getId()); + will(returnValue(introducee1)); + oneOf(introductionGroupFactory).createIntroductionGroup(introducee1); + will(returnValue(introductionGroup1)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); + will(returnValue(state)); + oneOf(introduceeManager).acceptIntroduction(txn, state, time); + oneOf(db).endTransaction(txn); + }}); + + introductionManager + .acceptIntroduction(introducee1.getId(), sessionId, time); + + context.assertIsSatisfied(); + assertTrue(txn.isComplete()); + } + + @Test + public void testDeclineIntroduction() throws DbException, FormatException { + final BdfDictionary state = BdfDictionary.of( + new BdfEntry(GROUP_ID_1, introductionGroup1.getId()), + new BdfEntry(GROUP_ID_2, introductionGroup2.getId()) + ); + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(db).getContact(txn, introducee1.getId()); + will(returnValue(introducee1)); + oneOf(introductionGroupFactory).createIntroductionGroup(introducee1); + will(returnValue(introductionGroup1)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); + will(returnValue(state)); + oneOf(introduceeManager).declineIntroduction(txn, state, time); + oneOf(db).endTransaction(txn); + }}); + + introductionManager + .declineIntroduction(introducee1.getId(), sessionId, time); + + context.assertIsSatisfied(); + assertTrue(txn.isComplete()); + } + + @Test + public void testGetIntroductionMessages() + throws DbException, FormatException { + + final Map<MessageId, BdfDictionary> metadata = Collections.emptyMap(); + final Collection<MessageStatus> statuses = Collections.emptyList(); + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(db).getContact(txn, introducee1.getId()); + will(returnValue(introducee1)); + oneOf(introductionGroupFactory).createIntroductionGroup(introducee1); + will(returnValue(introductionGroup1)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + introductionGroup1.getId()); + will(returnValue(metadata)); + oneOf(db).getMessageStatus(txn, introducee1.getId(), + introductionGroup1.getId()); + will(returnValue(statuses)); + oneOf(db).endTransaction(txn); + }}); + + introductionManager.getIntroductionMessages(introducee1.getId()); + + context.assertIsSatisfied(); + assertTrue(txn.isComplete()); + } + + @Test + public void testIncomingRequestMessage() + throws DbException, FormatException { + + final BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_REQUEST); + + final BdfDictionary state = new BdfDictionary(); + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(introduceeManager) + .initialize(txn, introductionGroup1.getId(), msg); + will(returnValue(state)); + oneOf(introduceeManager) + .incomingMessage(txn, state, msg); + }}); + + introductionManager + .incomingMessage(txn, message1, new BdfList(), msg); + + context.assertIsSatisfied(); + assertFalse(txn.isComplete()); + } + + @Test + public void testIncomingResponseMessage() + throws DbException, FormatException { + + final BdfDictionary msg = BdfDictionary.of( + new BdfEntry(TYPE, TYPE_RESPONSE), + new BdfEntry(SESSION_ID, sessionId) + ); + + final BdfDictionary state = new BdfDictionary(); + state.put(ROLE, ROLE_INTRODUCER); + state.put(GROUP_ID_1, introductionGroup1.getId()); + state.put(GROUP_ID_2, introductionGroup2.getId()); + + txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); + will(returnValue(state)); + oneOf(introducerManager).incomingMessage(txn, state, msg); + }}); + + introductionManager + .incomingMessage(txn, message1, new BdfList(), msg); + + context.assertIsSatisfied(); + assertFalse(txn.isComplete()); + } + + +} diff --git a/briar-tests/src/org/briarproject/introduction/IntroductionValidatorTest.java b/briar-tests/src/org/briarproject/introduction/IntroductionValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dbacbc8f740273e4f6bf1988406ab0cccf00eee2 --- /dev/null +++ b/briar-tests/src/org/briarproject/introduction/IntroductionValidatorTest.java @@ -0,0 +1,357 @@ +package org.briarproject.introduction; + +import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.FormatException; +import org.briarproject.api.TransportId; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfEntry; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.MetadataEncoder; +import org.briarproject.api.introduction.SessionId; +import org.briarproject.api.sync.ClientId; +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.system.Clock; +import org.briarproject.system.SystemClock; +import org.jmock.Mockery; +import org.junit.Test; + +import java.io.IOException; + +import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; +import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.api.introduction.IntroductionConstants.ACCEPT; +import static org.briarproject.api.introduction.IntroductionConstants.E_PUBLIC_KEY; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; +import static org.briarproject.api.introduction.IntroductionConstants.MSG; +import static org.briarproject.api.introduction.IntroductionConstants.NAME; +import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY; +import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TIME; +import static org.briarproject.api.introduction.IntroductionConstants.TRANSPORT; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; +import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; +import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class IntroductionValidatorTest extends BriarTestCase { + + private final Mockery context = new Mockery(); + private final Group group; + private final Message message; + private final IntroductionValidator validator; + private final Clock clock = new SystemClock(); + + public IntroductionValidatorTest() { + GroupId groupId = new GroupId(TestUtils.getRandomId()); + ClientId clientId = new ClientId(TestUtils.getRandomId()); + byte[] descriptor = TestUtils.getRandomBytes(12); + group = new Group(groupId, clientId, descriptor); + + MessageId messageId = new MessageId(TestUtils.getRandomId()); + long timestamp = System.currentTimeMillis(); + byte[] raw = TestUtils.getRandomBytes(123); + message = new Message(messageId, group.getId(), timestamp, raw); + + + ClientHelper clientHelper = context.mock(ClientHelper.class); + MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class); + validator = new IntroductionValidator(clientHelper, metadataEncoder, + clock); + context.assertIsSatisfied(); + } + + // + // Introduction Requests + // + + @Test + public void testValidateProperIntroductionRequest() throws IOException { + final byte[] sessionId = TestUtils.getRandomId(); + final String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH); + final byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + final String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH); + + BdfList body = BdfList.of(TYPE_REQUEST, sessionId, + name, publicKey, text); + + final BdfDictionary result = + validator.validateMessage(message, group, body); + + assertEquals(Long.valueOf(TYPE_REQUEST), result.getLong(TYPE)); + assertEquals(sessionId, result.getRaw(SESSION_ID)); + assertEquals(name, result.getString(NAME)); + assertEquals(publicKey, result.getRaw(PUBLIC_KEY)); + assertEquals(text, result.getString(MSG)); + context.assertIsSatisfied(); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionRequestWithNoName() throws IOException { + BdfDictionary msg = getValidIntroductionRequest(); + + // no NAME is message + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getRaw(PUBLIC_KEY)); + if (msg.containsKey(MSG)) body.add(msg.getString(MSG)); + + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionRequestWithLongName() throws IOException { + // too long NAME in message + BdfDictionary msg = getValidIntroductionRequest(); + msg.put(NAME, msg.get(NAME) + "x"); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getString(NAME), msg.getRaw(PUBLIC_KEY)); + if (msg.containsKey(MSG)) body.add(msg.getString(MSG)); + + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionRequestWithWrongType() + throws IOException { + // wrong message type + BdfDictionary msg = getValidIntroductionRequest(); + msg.put(TYPE, 324234); + + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getString(NAME), msg.getRaw(PUBLIC_KEY)); + if (msg.containsKey(MSG)) body.add(msg.getString(MSG)); + validator.validateMessage(message, group, body); + } + + private BdfDictionary getValidIntroductionRequest() throws FormatException { + byte[] sessionId = TestUtils.getRandomId(); + String name = TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH); + byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + String text = TestUtils.getRandomString(MAX_MESSAGE_BODY_LENGTH); + + BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_REQUEST); + msg.put(SESSION_ID, sessionId); + msg.put(NAME, name); + msg.put(PUBLIC_KEY, publicKey); + msg.put(MSG, text); + + return msg; + } + + // + // Introduction Responses + // + + @Test + public void testValidateIntroductionAcceptResponse() throws IOException { + byte[] groupId = TestUtils.getRandomId(); + byte[] sessionId = TestUtils.getRandomId(); + long time = clock.currentTimeMillis(); + byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + String transportId = TestUtils + .getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH); + BdfDictionary tProps = BdfDictionary.of( + new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH), + TestUtils.getRandomString(MAX_PROPERTY_LENGTH)) + ); + BdfDictionary tp = BdfDictionary.of( + new BdfEntry(transportId, tProps) + ); + + BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_RESPONSE); + msg.put(GROUP_ID, groupId); + msg.put(SESSION_ID, sessionId); + msg.put(ACCEPT, true); + msg.put(TIME, time); + msg.put(E_PUBLIC_KEY, publicKey); + msg.put(TRANSPORT, tp); + + BdfList body = BdfList.of(TYPE_RESPONSE, msg.getRaw(SESSION_ID), + msg.getBoolean(ACCEPT), msg.getLong(TIME), + msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT)); + + final BdfDictionary result = + validator.validateMessage(message, group, body); + + assertEquals(Long.valueOf(TYPE_RESPONSE), result.getLong(TYPE)); + assertEquals(sessionId, result.getRaw(SESSION_ID)); + assertEquals(true, result.getBoolean(ACCEPT)); + assertEquals(publicKey, result.getRaw(E_PUBLIC_KEY)); + assertEquals(tp, result.getDictionary(TRANSPORT)); + context.assertIsSatisfied(); + } + + @Test + public void testValidateIntroductionDeclineResponse() + throws IOException { + BdfDictionary msg = getValidIntroductionResponse(false); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getBoolean(ACCEPT)); + + BdfDictionary result = validator.validateMessage(message, group, body); + + assertFalse(result.getBoolean(ACCEPT)); + context.assertIsSatisfied(); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionResponseWithoutAccept() + throws IOException { + BdfDictionary msg = getValidIntroductionResponse(false); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID)); + + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionResponseWithBrokenTp() + throws IOException { + BdfDictionary msg = getValidIntroductionResponse(true); + BdfDictionary tp = msg.getDictionary(TRANSPORT); + tp.put(TestUtils + .getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH), "X"); + msg.put(TRANSPORT, tp); + + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getBoolean(ACCEPT), msg.getLong(TIME), + msg.getRaw(E_PUBLIC_KEY), msg.getDictionary(TRANSPORT)); + + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionResponseWithoutPublicKey() + throws IOException { + BdfDictionary msg = getValidIntroductionResponse(true); + + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getBoolean(ACCEPT), msg.getLong(TIME), + msg.getDictionary(TRANSPORT)); + + validator.validateMessage(message, group, body); + } + + private BdfDictionary getValidIntroductionResponse(boolean accept) + throws FormatException { + + byte[] groupId = TestUtils.getRandomId(); + byte[] sessionId = TestUtils.getRandomId(); + long time = clock.currentTimeMillis(); + byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + String transportId = TestUtils + .getRandomString(TransportId.MAX_TRANSPORT_ID_LENGTH); + BdfDictionary tProps = BdfDictionary.of( + new BdfEntry(TestUtils.getRandomString(MAX_PROPERTY_LENGTH), + TestUtils.getRandomString(MAX_PROPERTY_LENGTH)) + ); + BdfDictionary tp = BdfDictionary.of( + new BdfEntry(transportId, tProps) + ); + + BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_RESPONSE); + msg.put(GROUP_ID, groupId); + msg.put(SESSION_ID, sessionId); + msg.put(ACCEPT, accept); + if (accept) { + msg.put(TIME, time); + msg.put(E_PUBLIC_KEY, publicKey); + msg.put(TRANSPORT, tp); + } + + return msg; + } + + // + // Introduction ACK + // + + @Test + public void testValidateProperIntroductionAck() throws IOException { + final byte[] sessionId = TestUtils.getRandomId(); + + BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_ACK); + msg.put(SESSION_ID, sessionId); + + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID)); + + BdfDictionary result = + validator.validateMessage(message, group, body); + + assertEquals(Long.valueOf(TYPE_ACK), result.getLong(TYPE)); + assertEquals(sessionId, result.getRaw(SESSION_ID)); + context.assertIsSatisfied(); + } + + @Test(expected = FormatException.class) + public void testValidateTooLongIntroductionAck() throws IOException { + BdfDictionary msg = BdfDictionary.of( + new BdfEntry(TYPE, TYPE_ACK), + new BdfEntry(SESSION_ID, TestUtils.getRandomId()), + new BdfEntry("garbage", TestUtils.getRandomString(255)) + ); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getString("garbage")); + + validator.validateMessage(message, group, body); + } + + @Test(expected = FormatException.class) + public void testValidateIntroductionAckWithLongSessionId() throws IOException { + BdfDictionary msg = BdfDictionary.of( + new BdfEntry(TYPE, TYPE_ACK), + new BdfEntry(SESSION_ID, new byte[SessionId.LENGTH + 1]) + ); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID)); + + validator.validateMessage(message, group, body); + } + + // + // Introduction Abort + // + + @Test + public void testValidateProperIntroductionAbort() throws IOException { + byte[] sessionId = TestUtils.getRandomId(); + + BdfDictionary msg = new BdfDictionary(); + msg.put(TYPE, TYPE_ABORT); + msg.put(SESSION_ID, sessionId); + + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID)); + + BdfDictionary result = + validator.validateMessage(message, group, body); + + assertEquals(Long.valueOf(TYPE_ABORT), result.getLong(TYPE)); + assertEquals(sessionId, result.getRaw(SESSION_ID)); + context.assertIsSatisfied(); + } + + @Test(expected = FormatException.class) + public void testValidateTooLongIntroductionAbort() throws IOException { + BdfDictionary msg = BdfDictionary.of( + new BdfEntry(TYPE, TYPE_ABORT), + new BdfEntry(SESSION_ID, TestUtils.getRandomId()), + new BdfEntry("garbage", TestUtils.getRandomString(255)) + ); + BdfList body = BdfList.of(msg.getLong(TYPE), msg.getRaw(SESSION_ID), + msg.getString("garbage")); + + validator.validateMessage(message, group, body); + } + +} diff --git a/briar-tests/src/org/briarproject/introduction/MessageSenderTest.java b/briar-tests/src/org/briarproject/introduction/MessageSenderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1c9d3b82c72e5de6d9b3a84db37871be524f4e7e --- /dev/null +++ b/briar-tests/src/org/briarproject/introduction/MessageSenderTest.java @@ -0,0 +1,95 @@ +package org.briarproject.introduction; + +import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.clients.MessageQueueManager; +import org.briarproject.api.clients.PrivateGroupFactory; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfEntry; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.MetadataEncoder; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Metadata; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.introduction.SessionId; +import org.briarproject.api.sync.ClientId; +import org.briarproject.api.sync.Group; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.system.Clock; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import static junit.framework.Assert.assertFalse; +import static org.briarproject.api.introduction.IntroductionConstants.GROUP_ID; +import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE; +import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; + +public class MessageSenderTest extends BriarTestCase { + + final Mockery context; + final MessageSender messageSender; + final DatabaseComponent db; + final PrivateGroupFactory privateGroupFactory; + final ClientHelper clientHelper; + final MetadataEncoder metadataEncoder; + final MessageQueueManager messageQueueManager; + final Clock clock; + + public MessageSenderTest() { + context = new Mockery(); + db = context.mock(DatabaseComponent.class); + privateGroupFactory = context.mock(PrivateGroupFactory.class); + clientHelper = context.mock(ClientHelper.class); + metadataEncoder = + context.mock(MetadataEncoder.class); + messageQueueManager = + context.mock(MessageQueueManager.class); + clock = context.mock(Clock.class); + + messageSender = + new MessageSender(db, clientHelper, clock, metadataEncoder, + messageQueueManager); + } + + @Test + public void testSendMessage() throws DbException, FormatException { + final Transaction txn = new Transaction(null, false); + final Group privateGroup = new Group(new GroupId(TestUtils.getRandomId()), + new ClientId(TestUtils.getRandomId()), new byte[0]); + final SessionId sessionId = new SessionId(TestUtils.getRandomId()); + final long time = 42L; + final BdfDictionary msg = BdfDictionary.of( + new BdfEntry(TYPE, TYPE_ACK), + new BdfEntry(GROUP_ID, privateGroup.getId()), + new BdfEntry(SESSION_ID, sessionId) + ); + final BdfList bodyList = + BdfList.of(TYPE_ACK, msg.getRaw(SESSION_ID)); + final byte[] body = TestUtils.getRandomBytes(8); + final Metadata metadata = new Metadata(); + + context.checking(new Expectations() {{ + oneOf(clientHelper).toByteArray(bodyList); + will(returnValue(body)); + oneOf(db).getGroup(txn, privateGroup.getId()); + will(returnValue(privateGroup)); + oneOf(metadataEncoder).encode(msg); + will(returnValue(metadata)); + oneOf(clock).currentTimeMillis(); + will(returnValue(time)); + oneOf(messageQueueManager) + .sendMessage(txn, privateGroup, time, body, metadata); + }}); + + messageSender.sendMessage(txn, msg); + + context.assertIsSatisfied(); + assertFalse(txn.isComplete()); + } + +}