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