From dfdde9799f9768eda3417d16fa4f2d49ca8bc356 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 31 Oct 2016 17:36:53 -0200 Subject: [PATCH] Add Unit Tests for ClientHelper --- .../briarproject/CaptureArgumentAction.java | 33 ++ .../clients/ClientHelperImplTest.java | 368 +++++++++++++++++- .../clients/MessageQueueManagerImplTest.java | 53 +-- 3 files changed, 414 insertions(+), 40 deletions(-) create mode 100644 briar-tests/src/org/briarproject/CaptureArgumentAction.java diff --git a/briar-tests/src/org/briarproject/CaptureArgumentAction.java b/briar-tests/src/org/briarproject/CaptureArgumentAction.java new file mode 100644 index 0000000000..6fda69885a --- /dev/null +++ b/briar-tests/src/org/briarproject/CaptureArgumentAction.java @@ -0,0 +1,33 @@ +package org.briarproject; + +import org.hamcrest.Description; +import org.jmock.api.Action; +import org.jmock.api.Invocation; + +import java.util.concurrent.atomic.AtomicReference; + +public class CaptureArgumentAction<T> implements Action { + + private final AtomicReference<T> captured; + private final Class<T> capturedClass; + private final int index; + + public CaptureArgumentAction(AtomicReference<T> captured, + Class<T> capturedClass, int index) { + this.captured = captured; + this.capturedClass = capturedClass; + this.index = index; + } + + @Override + public Object invoke(Invocation invocation) throws Throwable { + captured.set(capturedClass.cast(invocation.getParameter(index))); + return null; + } + + @Override + public void describeTo(Description description) { + description.appendText("captures an argument"); + } + +} diff --git a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java index 9ea0e901f9..26aa375a45 100644 --- a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java +++ b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java @@ -1,14 +1,378 @@ package org.briarproject.clients; import org.briarproject.BriarTestCase; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.KeyParser; +import org.briarproject.api.crypto.PrivateKey; +import org.briarproject.api.crypto.PublicKey; +import org.briarproject.api.crypto.Signature; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfEntry; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.BdfReader; +import org.briarproject.api.data.BdfReaderFactory; +import org.briarproject.api.data.BdfWriter; +import org.briarproject.api.data.BdfWriterFactory; +import org.briarproject.api.data.MetadataEncoder; +import org.briarproject.api.data.MetadataParser; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.Metadata; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.Message; +import org.briarproject.api.sync.MessageFactory; +import org.briarproject.api.sync.MessageId; +import org.jmock.Expectations; +import org.jmock.Mockery; import org.junit.Test; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.briarproject.TestUtils.getRandomBytes; +import static org.briarproject.TestUtils.getRandomId; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class ClientHelperImplTest extends BriarTestCase { + private final Mockery context = new Mockery(); + private final DatabaseComponent db = context.mock(DatabaseComponent.class); + private final MessageFactory messageFactory = + context.mock(MessageFactory.class); + private final BdfReaderFactory bdfReaderFactory = + context.mock(BdfReaderFactory.class); + private final BdfWriterFactory bdfWriterFactory = + context.mock(BdfWriterFactory.class); + private final MetadataParser metadataParser = + context.mock(MetadataParser.class); + private final MetadataEncoder metadataEncoder = + context.mock(MetadataEncoder.class); + private final CryptoComponent cryptoComponent = + context.mock(CryptoComponent.class); + private final KeyParser keyParser = context.mock(KeyParser.class); + private final Signature signature = context.mock(Signature.class); + private final ClientHelper clientHelper; + + private final GroupId groupId = new GroupId(getRandomId()); + private final BdfDictionary dictionary = new BdfDictionary(); + private final long timestamp = 42L; + private final byte[] rawMessage = getRandomBytes(42); + private final MessageId messageId = new MessageId(getRandomId()); + private final Message message = + new Message(messageId, groupId, timestamp, rawMessage); + private final Metadata metadata = new Metadata(); + private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42)); + + public ClientHelperImplTest() { + clientHelper = + new ClientHelperImpl(db, messageFactory, bdfReaderFactory, + bdfWriterFactory, metadataParser, metadataEncoder, + cryptoComponent); + } + @Test - public void testUnitTestsExist() { - fail(); // FIXME: Write tests + public void testAddLocalMessage() throws Exception { + final boolean shared = true; + final Transaction txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(metadataEncoder).encode(dictionary); + will(returnValue(metadata)); + oneOf(db).addLocalMessage(txn, message, metadata, shared); + oneOf(db).endTransaction(txn); + }}); + + clientHelper.addLocalMessage(message, dictionary, shared); + context.assertIsSatisfied(); } + + @Test + public void testCreateMessage() throws Exception { + final byte[] bytes = expectToByteArray(list); + + context.checking(new Expectations() {{ + oneOf(messageFactory).createMessage(groupId, timestamp, bytes); + }}); + + clientHelper.createMessage(groupId, timestamp, list); + context.assertIsSatisfied(); + } + + @Test + public void testGetMessageAsList() throws Exception { + final Transaction txn = new Transaction(null, true); + + expectToList(true); + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(db).getRawMessage(txn, messageId); + will(returnValue(rawMessage)); + oneOf(db).endTransaction(txn); + }}); + + clientHelper.getMessageAsList(messageId); + context.assertIsSatisfied(); + } + + @Test + public void testGetGroupMetadataAsDictionary() throws Exception { + final Transaction txn = new Transaction(null, true); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(db).getGroupMetadata(txn, groupId); + will(returnValue(metadata)); + oneOf(metadataParser).parse(metadata); + will(returnValue(dictionary)); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(dictionary, + clientHelper.getGroupMetadataAsDictionary(groupId)); + context.assertIsSatisfied(); + } + + @Test + public void testGetMessageMetadataAsDictionary() throws Exception { + final Transaction txn = new Transaction(null, true); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(db).getMessageMetadata(txn, messageId); + will(returnValue(metadata)); + oneOf(metadataParser).parse(metadata); + will(returnValue(dictionary)); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(dictionary, + clientHelper.getMessageMetadataAsDictionary(messageId)); + context.assertIsSatisfied(); + } + + @Test + public void testGetMessageMetadataAsDictionaryMap() throws Exception { + final Map<MessageId, BdfDictionary> map = new HashMap<>(); + map.put(messageId, dictionary); + final Transaction txn = new Transaction(null, true); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(db).getMessageMetadata(txn, groupId); + will(returnValue(Collections.singletonMap(messageId, metadata))); + oneOf(metadataParser).parse(metadata); + will(returnValue(dictionary)); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(map, + clientHelper.getMessageMetadataAsDictionary(groupId)); + context.assertIsSatisfied(); + } + + @Test + public void testGetMessageMetadataAsDictionaryQuery() throws Exception { + final Map<MessageId, BdfDictionary> map = new HashMap<>(); + map.put(messageId, dictionary); + final BdfDictionary query = + BdfDictionary.of(new BdfEntry("query", "me")); + final Metadata queryMetadata = new Metadata(); + queryMetadata.put("query", getRandomBytes(42)); + final Transaction txn = new Transaction(null, true); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(true); + will(returnValue(txn)); + oneOf(metadataEncoder).encode(query); + will(returnValue(queryMetadata)); + oneOf(db).getMessageMetadata(txn, groupId, queryMetadata); + will(returnValue(Collections.singletonMap(messageId, metadata))); + oneOf(metadataParser).parse(metadata); + will(returnValue(dictionary)); + oneOf(db).endTransaction(txn); + }}); + + assertEquals(map, + clientHelper.getMessageMetadataAsDictionary(groupId, query)); + context.assertIsSatisfied(); + } + + @Test + public void testMergeGroupMetadata() throws Exception { + final Transaction txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(metadataEncoder).encode(dictionary); + will(returnValue(metadata)); + oneOf(db).mergeGroupMetadata(txn, groupId, metadata); + oneOf(db).endTransaction(txn); + }}); + + clientHelper.mergeGroupMetadata(groupId, dictionary); + context.assertIsSatisfied(); + } + + @Test + public void testMergeMessageMetadata() throws Exception { + final Transaction txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(db).startTransaction(false); + will(returnValue(txn)); + oneOf(metadataEncoder).encode(dictionary); + will(returnValue(metadata)); + oneOf(db).mergeMessageMetadata(txn, messageId, metadata); + oneOf(db).endTransaction(txn); + }}); + + clientHelper.mergeMessageMetadata(messageId, dictionary); + context.assertIsSatisfied(); + } + + @Test + public void testToByteArray() throws Exception { + byte[] bytes = expectToByteArray(list); + + assertArrayEquals(bytes, clientHelper.toByteArray(list)); + context.assertIsSatisfied(); + } + + @Test + public void testToList() throws Exception { + expectToList(true); + + assertEquals(list, clientHelper.toList(rawMessage)); + context.assertIsSatisfied(); + } + + @Test + public void testToListWithNoEof() throws Exception { + expectToList(false); // no EOF after list + + try { + clientHelper.toList(rawMessage); + fail(); + } catch (FormatException e) { + // expected + context.assertIsSatisfied(); + } + } + + @Test + public void testSign() throws Exception { + final byte[] privateKeyBytes = getRandomBytes(42); + final PrivateKey privateKey = context.mock(PrivateKey.class); + final byte[] signed = getRandomBytes(42); + + final byte[] bytes = expectToByteArray(list); + context.checking(new Expectations() {{ + oneOf(cryptoComponent).getSignature(); + will(returnValue(signature)); + oneOf(cryptoComponent).getSignatureKeyParser(); + will(returnValue(keyParser)); + oneOf(keyParser).parsePrivateKey(privateKeyBytes); + will(returnValue(privateKey)); + oneOf(signature).initSign(privateKey); + oneOf(signature).update(bytes); + oneOf(signature).sign(); + will(returnValue(signed)); + }}); + + assertArrayEquals(signed, clientHelper.sign(list, privateKeyBytes)); + context.assertIsSatisfied(); + } + + @Test + public void testVerifySignature() throws Exception { + final PublicKey publicKey = context.mock(PublicKey.class); + final byte[] publicKeyBytes = getRandomBytes(42); + + final byte[] bytes = expectToByteArray(list); + context.checking(new Expectations() {{ + oneOf(cryptoComponent).getSignatureKeyParser(); + will(returnValue(keyParser)); + oneOf(keyParser).parsePublicKey(publicKeyBytes); + will(returnValue(publicKey)); + oneOf(cryptoComponent).getSignature(); + will(returnValue(signature)); + oneOf(signature).initVerify(publicKey); + oneOf(signature).update(bytes); + oneOf(signature).verify(rawMessage); + will(returnValue(true)); + }}); + + clientHelper.verifySignature(rawMessage, publicKeyBytes, list); + context.assertIsSatisfied(); + } + + @Test + public void testVerifyWrongSignature() throws Exception { + final PublicKey publicKey = context.mock(PublicKey.class); + final byte[] publicKeyBytes = getRandomBytes(42); + + final byte[] bytes = expectToByteArray(list); + context.checking(new Expectations() {{ + oneOf(cryptoComponent).getSignatureKeyParser(); + will(returnValue(keyParser)); + oneOf(keyParser).parsePublicKey(publicKeyBytes); + will(returnValue(publicKey)); + oneOf(cryptoComponent).getSignature(); + will(returnValue(signature)); + oneOf(signature).initVerify(publicKey); + oneOf(signature).update(bytes); + oneOf(signature).verify(rawMessage); + will(returnValue(false)); + }}); + + try { + clientHelper.verifySignature(rawMessage, publicKeyBytes, list); + fail(); + } catch (GeneralSecurityException e) { + // expected + context.assertIsSatisfied(); + } + } + + private byte[] expectToByteArray(final BdfList list) throws Exception { + final BdfWriter bdfWriter = context.mock(BdfWriter.class); + + context.checking(new Expectations() {{ + oneOf(bdfWriterFactory) + .createWriter(with(any(ByteArrayOutputStream.class))); + will(returnValue(bdfWriter)); + oneOf(bdfWriter).writeList(list); + }}); + return new byte[0]; + } + + private void expectToList(final boolean eof) throws Exception { + final BdfReader bdfReader = context.mock(BdfReader.class); + + context.checking(new Expectations() {{ + oneOf(bdfReaderFactory) + .createReader(with(any(InputStream.class))); + will(returnValue(bdfReader)); + oneOf(bdfReader).readList(); + will(returnValue(list)); + oneOf(bdfReader).eof(); + will(returnValue(eof)); + }}); + } + } diff --git a/briar-tests/src/org/briarproject/clients/MessageQueueManagerImplTest.java b/briar-tests/src/org/briarproject/clients/MessageQueueManagerImplTest.java index a46b0227a6..797251a1de 100644 --- a/briar-tests/src/org/briarproject/clients/MessageQueueManagerImplTest.java +++ b/briar-tests/src/org/briarproject/clients/MessageQueueManagerImplTest.java @@ -1,6 +1,7 @@ package org.briarproject.clients; import org.briarproject.BriarTestCase; +import org.briarproject.CaptureArgumentAction; import org.briarproject.TestUtils; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.MessageQueueManager.IncomingQueueMessageHook; @@ -127,7 +128,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.mock(ValidationManager.class); final AtomicReference<MessageValidator> captured = - new AtomicReference<MessageValidator>(); + new AtomicReference<>(); final QueueMessageValidator queueMessageValidator = context.mock(QueueMessageValidator.class); // The message is too short to be a valid queue message @@ -138,7 +139,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerMessageValidator(with(clientId), with(any(MessageValidator.class))); - will(new CaptureArgumentAction<MessageValidator>(captured, + will(new CaptureArgumentAction<>(captured, MessageValidator.class, 1)); }}); @@ -166,7 +167,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.mock(ValidationManager.class); final AtomicReference<MessageValidator> captured = - new AtomicReference<MessageValidator>(); + new AtomicReference<>(); final QueueMessageValidator queueMessageValidator = context.mock(QueueMessageValidator.class); // The message has a negative queue position @@ -179,7 +180,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerMessageValidator(with(clientId), with(any(MessageValidator.class))); - will(new CaptureArgumentAction<MessageValidator>(captured, + will(new CaptureArgumentAction<>(captured, MessageValidator.class, 1)); }}); @@ -207,7 +208,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.mock(ValidationManager.class); final AtomicReference<MessageValidator> captured = - new AtomicReference<MessageValidator>(); + new AtomicReference<>(); final QueueMessageValidator queueMessageValidator = context.mock(QueueMessageValidator.class); final Metadata metadata = new Metadata(); @@ -221,7 +222,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerMessageValidator(with(clientId), with(any(MessageValidator.class))); - will(new CaptureArgumentAction<MessageValidator>(captured, + will(new CaptureArgumentAction<>(captured, MessageValidator.class, 1)); // The message should be delegated oneOf(queueMessageValidator).validateMessage( @@ -254,7 +255,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { final ValidationManager validationManager = context.mock(ValidationManager.class); final AtomicReference<IncomingMessageHook> captured = - new AtomicReference<IncomingMessageHook>(); + new AtomicReference<>(); final IncomingQueueMessageHook incomingQueueMessageHook = context.mock(IncomingQueueMessageHook.class); @@ -270,7 +271,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerIncomingMessageHook(with(clientId), with(any(IncomingMessageHook.class))); - will(new CaptureArgumentAction<IncomingMessageHook>(captured, + will(new CaptureArgumentAction<>(captured, IncomingMessageHook.class, 1)); oneOf(db).getGroupMetadata(txn, groupId); will(returnValue(groupMetadata)); @@ -306,7 +307,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { final ValidationManager validationManager = context.mock(ValidationManager.class); final AtomicReference<IncomingMessageHook> captured = - new AtomicReference<IncomingMessageHook>(); + new AtomicReference<>(); final IncomingQueueMessageHook incomingQueueMessageHook = context.mock(IncomingQueueMessageHook.class); @@ -324,7 +325,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerIncomingMessageHook(with(clientId), with(any(IncomingMessageHook.class))); - will(new CaptureArgumentAction<IncomingMessageHook>(captured, + will(new CaptureArgumentAction<>(captured, IncomingMessageHook.class, 1)); oneOf(db).getGroupMetadata(txn, groupId); will(returnValue(groupMetadata)); @@ -362,7 +363,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { final ValidationManager validationManager = context.mock(ValidationManager.class); final AtomicReference<IncomingMessageHook> captured = - new AtomicReference<IncomingMessageHook>(); + new AtomicReference<>(); final IncomingQueueMessageHook incomingQueueMessageHook = context.mock(IncomingQueueMessageHook.class); @@ -379,7 +380,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerIncomingMessageHook(with(clientId), with(any(IncomingMessageHook.class))); - will(new CaptureArgumentAction<IncomingMessageHook>(captured, + will(new CaptureArgumentAction<>(captured, IncomingMessageHook.class, 1)); oneOf(db).getGroupMetadata(txn, groupId); will(returnValue(groupMetadata)); @@ -420,7 +421,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { final ValidationManager validationManager = context.mock(ValidationManager.class); final AtomicReference<IncomingMessageHook> captured = - new AtomicReference<IncomingMessageHook>(); + new AtomicReference<>(); final IncomingQueueMessageHook incomingQueueMessageHook = context.mock(IncomingQueueMessageHook.class); @@ -444,7 +445,7 @@ public class MessageQueueManagerImplTest extends BriarTestCase { context.checking(new Expectations() {{ oneOf(validationManager).registerIncomingMessageHook(with(clientId), with(any(IncomingMessageHook.class))); - will(new CaptureArgumentAction<IncomingMessageHook>(captured, + will(new CaptureArgumentAction<>(captured, IncomingMessageHook.class, 1)); oneOf(db).getGroupMetadata(txn, groupId); will(returnValue(groupMetadata)); @@ -557,28 +558,4 @@ public class MessageQueueManagerImplTest extends BriarTestCase { } } - private class CaptureArgumentAction<T> implements Action { - - private final AtomicReference<T> captured; - private final Class<T> capturedClass; - private final int index; - - private CaptureArgumentAction(AtomicReference<T> captured, - Class<T> capturedClass, int index) { - this.captured = captured; - this.capturedClass = capturedClass; - this.index = index; - } - - @Override - public Object invoke(Invocation invocation) throws Throwable { - captured.set(capturedClass.cast(invocation.getParameter(index))); - return null; - } - - @Override - public void describeTo(Description description) { - description.appendText("captures an argument"); - } - } } -- GitLab