From 050111a99428df99170514243d146336d30eca3a Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 29 Nov 2016 17:46:46 +0000
Subject: [PATCH] Added some more private group validation tests, found a bug.

---
 .../privategroup/GroupMessageValidator.java   |  25 +-
 .../GroupMessageValidatorTest.java            | 550 +++++++++++-------
 2 files changed, 343 insertions(+), 232 deletions(-)

diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index 39b77cd274..76f5b6b374 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -9,7 +9,6 @@ import org.briarproject.api.data.MetadataEncoder;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.nullsafety.NotNullByDefault;
-import org.briarproject.api.privategroup.MessageType;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
@@ -79,17 +78,14 @@ class GroupMessageValidator extends BdfMessageValidator {
 
 		Author member = authorFactory.createAuthor(memberName, memberPublicKey);
 		BdfMessageContext c;
-		switch (MessageType.valueOf(type)) {
-			case JOIN:
-				c = validateJoin(m, g, body, member);
-				addMessageMetadata(c, member, m.getTimestamp());
-				break;
-			case POST:
-				c = validatePost(m, g, body, member);
-				addMessageMetadata(c, member, m.getTimestamp());
-				break;
-			default:
-				throw new InvalidMessageException("Unknown Message Type");
+		if (type == JOIN.getInt()) {
+			c = validateJoin(m, g, body, member);
+			addMessageMetadata(c, member, m.getTimestamp());
+		} else if (type == POST.getInt()) {
+			c = validatePost(m, g, body, member);
+			addMessageMetadata(c, member, m.getTimestamp());
+		} else {
+			throw new InvalidMessageException("Unknown Message Type");
 		}
 		c.getDictionary().put(KEY_TYPE, type);
 		return c;
@@ -133,8 +129,9 @@ class GroupMessageValidator extends BdfMessageValidator {
 					.createInviteToken(creator.getId(), member.getId(),
 							pg.getId(), inviteTimestamp);
 			try {
-				clientHelper.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
-						creator.getPublicKey(), token);
+				clientHelper
+						.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
+								creator.getPublicKey(), token);
 			} catch (GeneralSecurityException e) {
 				throw new InvalidMessageException(e);
 			}
diff --git a/briar-tests/src/org/briarproject/privategroup/GroupMessageValidatorTest.java b/briar-tests/src/org/briarproject/privategroup/GroupMessageValidatorTest.java
index d9da038600..cff74d31d3 100644
--- a/briar-tests/src/org/briarproject/privategroup/GroupMessageValidatorTest.java
+++ b/briar-tests/src/org/briarproject/privategroup/GroupMessageValidatorTest.java
@@ -7,6 +7,7 @@ import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.privategroup.MessageType;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
@@ -16,6 +17,9 @@ import org.jmock.Expectations;
 import org.junit.Test;
 
 import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 import static org.briarproject.TestUtils.getRandomBytes;
 import static org.briarproject.TestUtils.getRandomId;
@@ -28,6 +32,7 @@ import static org.briarproject.api.privategroup.GroupMessageFactory.SIGNING_LABE
 import static org.briarproject.api.privategroup.MessageType.JOIN;
 import static org.briarproject.api.privategroup.MessageType.POST;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
 import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
 import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
@@ -39,6 +44,7 @@ import static org.briarproject.privategroup.GroupConstants.KEY_PREVIOUS_MSG_ID;
 import static org.briarproject.privategroup.GroupConstants.KEY_READ;
 import static org.briarproject.privategroup.GroupConstants.KEY_TIMESTAMP;
 import static org.briarproject.privategroup.GroupConstants.KEY_TYPE;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -50,9 +56,8 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
 	private final GroupInvitationFactory groupInvitationFactory =
 			context.mock(GroupInvitationFactory.class);
 
-
-	private final String creatorName = "Member Name";
-	private final String memberName = "Member Name";
+	private final String creatorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
+	private final String memberName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
 	private final byte[] creatorKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
 	private final byte[] memberKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
 	private final byte[] creatorSignature =
@@ -63,220 +68,272 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
 	private final Author creator =
 			new Author(new AuthorId(getRandomId()), creatorName, creatorKey);
 	private final long inviteTimestamp = 42L;
-	private final PrivateGroup privateGroup =
-			new PrivateGroup(group, "Private Group Name", creator,
-					getRandomBytes(GROUP_SALT_LENGTH));
+	private final PrivateGroup privateGroup = new PrivateGroup(group,
+			getRandomString(MAX_GROUP_NAME_LENGTH), creator,
+			getRandomBytes(GROUP_SALT_LENGTH));
 	private final BdfList token = BdfList.of("token");
-	private MessageId parentId = new MessageId(getRandomId());
-	private MessageId previousMsgId = new MessageId(getRandomId());
-	private String postContent = "Post text";
+	private final MessageId parentId = new MessageId(getRandomId());
+	private final MessageId previousMsgId = new MessageId(getRandomId());
+	private final String postContent =
+			getRandomString(MAX_GROUP_POST_BODY_LENGTH);
 
-	private GroupMessageValidator validator =
+	private final GroupMessageValidator validator =
 			new GroupMessageValidator(privateGroupFactory, clientHelper,
 					metadataEncoder, clock, authorFactory,
 					groupInvitationFactory);
 
+	// JOIN message
+
+	@Test(expected = FormatException.class)
+	public void testRejectsTooShortJoinMessage() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey, null);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsTooLongJoinMessage() throws Exception {
+		expectCreateAuthor(creator);
+		BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey, null,
+				signature, "");
+		validator.validateMessage(message, group, body);
+	}
+
 	@Test(expected = FormatException.class)
-	public void testRejectTooShortMemberName() throws Exception {
-		BdfList list = BdfList.of(JOIN.getInt(), "", memberKey, null,
+	public void testRejectsJoinWithTooShortMemberName() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), "", memberKey, null,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectTooLongMemberName() throws Exception {
-		BdfList list = BdfList.of(JOIN.getInt(),
+	public void testRejectsJoinMessageWithTooLongMemberName() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(),
 				getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), memberKey, null,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectTooShortMemberKey() throws Exception {
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, new byte[0], null,
+	public void testRejectsJoinMessageWithNullMemberName() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), null, memberKey, null,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectTooLongMemberKey() throws Exception {
-		BdfList list = BdfList.of(JOIN.getInt(), memberName,
-				getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), null,
-				signature);
-		validator.validateMessage(message, group, list);
+	public void testRejectsJoinMessageWithNonStringMemberName() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), getRandomBytes(5), memberKey,
+				null, signature);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectNonRawMemberKey() throws Exception {
-		BdfList list =
-				BdfList.of(JOIN.getInt(), memberName, "non raw key", null,
-						signature);
-		validator.validateMessage(message, group, list);
+	public void testRejectsJoinWithTooShortMemberKey() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, new byte[0], null,
+				signature);
+		validator.validateMessage(message, group, body);
 	}
 
-	// JOIN message
+	@Test(expected = FormatException.class)
+	public void testRejectsJoinWithTooLongMemberKey() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), memberName,
+				getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), null, signature);
+		validator.validateMessage(message, group, body);
+	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsTooShortJoinMessage() throws Exception {
-		BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
-				null);
-		validator.validateMessage(message, group, list);
+	public void testRejectsJoinWithNoullMemberKey() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, null, null,
+				signature);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsTooLongJoinMessage() throws Exception {
-		expectCreateAuthor(creator);
-		BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
-				null, signature, "");
-		validator.validateMessage(message, group, list);
+	public void testRejectsJoinWithNonRawMemberKey() throws Exception {
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, "not raw", null,
+				signature);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithNonListInvitation() throws Exception {
+	public void testRejectsJoinWithNonListInvitation() throws Exception {
 		expectCreateAuthor(creator);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
+		BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
 				"not a list", signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test
-	public void testAcceptCreatorJoinMessage() throws Exception {
-		final BdfList invite = null;
-		expectJoinMessage(creator, invite, true, true);
-		BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
-				invite, signature);
+	public void testAcceptsCreatorJoin() throws Exception {
+		expectJoinMessage(creator, null, true, true);
+		BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
+				null, signature);
 		BdfMessageContext messageContext =
-				validator.validateMessage(message, group, list);
-		assertMessageContext(messageContext, creator);
+				validator.validateMessage(message, group, body);
+		assertExpectedMessageContext(messageContext, JOIN, creator,
+				Collections.<MessageId>emptyList());
 		assertTrue(messageContext.getDictionary()
 				.getBoolean(KEY_INITIAL_JOIN_MSG));
 	}
 
 	@Test(expected = InvalidMessageException.class)
-	public void testRejectsMemberJoinMessageWithoutInvitation()
-			throws Exception {
+	public void testRejectsMemberJoinWithNullInvitation() throws Exception {
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, null,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, null,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithTooShortInvitation() throws Exception {
+	public void testRejectsMemberJoinWithTooShortInvitation() throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithTooLongInvitation() throws Exception {
+	public void testRejectsMemberJoinWithTooLongInvitation() throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp, creatorSignature, "");
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = InvalidMessageException.class)
-	public void testRejectsJoinMessageWithEqualInvitationTime()
+	public void testRejectsMemberJoinWithEqualInvitationTime()
 			throws Exception {
 		BdfList invite = BdfList.of(message.getTimestamp(), creatorSignature);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = InvalidMessageException.class)
-	public void testRejectsJoinMessageWithLaterInvitationTime()
+	public void testRejectsMemberJoinWithLaterInvitationTime()
+			throws Exception {
+		BdfList invite = BdfList.of(message.getTimestamp() + 1,
+				creatorSignature);
+		expectCreateAuthor(member);
+		expectParsePrivateGroup();
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+				signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsMemberJoinWithNullInvitationTime()
 			throws Exception {
-		BdfList invite =
-				BdfList.of(message.getTimestamp() + 1, creatorSignature);
+		BdfList invite = BdfList.of(null, creatorSignature);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithNonRawCreatorSignature()
+	public void testRejectsMemberJoinWithNonLongInvitationTime()
 			throws Exception {
-		BdfList invite = BdfList.of(inviteTimestamp, "non-raw signature");
+		BdfList invite = BdfList.of("not long", creatorSignature);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithTooShortCreatorSignature()
+	public void testRejectsMemberJoinWithTooShortCreatorSignature()
 			throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp, new byte[0]);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsJoinMessageWithTooLongCreatorSignature()
+	public void testRejectsJoinWithTooLongCreatorSignature()
 			throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp,
 				getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+				signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsMemberJoinWithNullCreatorSignature()
+			throws Exception {
+		BdfList invite = BdfList.of(inviteTimestamp, null);
+		expectCreateAuthor(member);
+		expectParsePrivateGroup();
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+				signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsMemberJoinWithNonRawCreatorSignature()
+			throws Exception {
+		BdfList invite = BdfList.of(inviteTimestamp, "not raw");
+		expectCreateAuthor(member);
+		expectParsePrivateGroup();
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = InvalidMessageException.class)
-	public void testRejectsJoinMessageWithInvalidCreatorSignature()
+	public void testRejectsMemberJoinWithInvalidCreatorSignature()
 			throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
 		expectJoinMessage(member, invite, false, true);
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = InvalidMessageException.class)
-	public void testRejectsJoinMessageWithInvalidMemberSignature()
+	public void testRejectsMemberJoinWithInvalidMemberSignature()
 			throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
 		expectJoinMessage(member, invite, true, false);
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test
-	public void testAcceptMemberJoinMessage() throws Exception {
+	public void testAcceptsMemberJoin() throws Exception {
 		BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
 		expectJoinMessage(member, invite, true, true);
-		BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
+		BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
 				signature);
 		BdfMessageContext messageContext =
-				validator.validateMessage(message, group, list);
-		assertMessageContext(messageContext, member);
+				validator.validateMessage(message, group, body);
+		assertExpectedMessageContext(messageContext, JOIN, member,
+				Collections.<MessageId>emptyList());
 		assertFalse(messageContext.getDictionary()
 				.getBoolean(KEY_INITIAL_JOIN_MSG));
 	}
 
 	private void expectCreateAuthor(final Author member) {
 		context.checking(new Expectations() {{
-			oneOf(authorFactory)
-					.createAuthor(member.getName(), member.getPublicKey());
+			oneOf(authorFactory).createAuthor(member.getName(),
+					member.getPublicKey());
 			will(returnValue(member));
 		}});
 	}
@@ -291,27 +348,23 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
 	private void expectJoinMessage(final Author member, final BdfList invite,
 			final boolean creatorSigValid, final boolean memberSigValid)
 			throws Exception {
-		final BdfList signed =
-				BdfList.of(group.getId(), message.getTimestamp(), JOIN.getInt(),
-						member.getName(), member.getPublicKey(), invite);
+		final BdfList signed = BdfList.of(group.getId(), message.getTimestamp(),
+				JOIN.getInt(), member.getName(), member.getPublicKey(), invite);
 		expectCreateAuthor(member);
 		expectParsePrivateGroup();
 		context.checking(new Expectations() {{
 			if (invite != null) {
-				oneOf(groupInvitationFactory)
-						.createInviteToken(creator.getId(), member.getId(),
-								privateGroup.getId(), inviteTimestamp);
+				oneOf(groupInvitationFactory).createInviteToken(creator.getId(),
+						member.getId(), privateGroup.getId(), inviteTimestamp);
 				will(returnValue(token));
-				oneOf(clientHelper)
-						.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
-								creatorKey, token);
+				oneOf(clientHelper).verifySignature(SIGNING_LABEL_INVITE,
+						creatorSignature, creatorKey, token);
 				if (!memberSigValid)
 					will(throwException(new GeneralSecurityException()));
 			}
 			if (memberSigValid) {
-				oneOf(clientHelper)
-						.verifySignature(SIGNING_LABEL_JOIN, signature,
-								member.getPublicKey(), signed);
+				oneOf(clientHelper).verifySignature(SIGNING_LABEL_JOIN,
+						signature, member.getPublicKey(), signed);
 				if (!creatorSigValid)
 					will(throwException(new GeneralSecurityException()));
 			}
@@ -322,213 +375,274 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsTooShortPost() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsTooLongPost() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature, "");
-		validator.validateMessage(message, group, list);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent, signature, "");
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsPostWithNonRawParentId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, "non-raw",
-						previousMsgId, postContent, signature);
-		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+	public void testRejectsPostWithTooShortMemberName() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), "", memberKey, parentId,
+				previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithTooLongMemberName() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(),
+				getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), memberKey,
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNullMemberName() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), null, memberKey,
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNonStringMemberName() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), getRandomBytes(5), memberKey,
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithTooShortMemberKey() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, new byte[0],
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithTooLongMemberKey() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName,
+				getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), parentId,
+				previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNullMemberKey() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, null,
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNonRawMemberKey() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, "not raw",
+				parentId, previousMsgId, postContent, signature);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooShortParentId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey,
-						getRandomBytes(MessageId.LENGTH - 1), previousMsgId,
-						postContent, signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				getRandomBytes(MessageId.LENGTH - 1), previousMsgId,
+				postContent, signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooLongParentId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey,
-						getRandomBytes(MessageId.LENGTH + 1), previousMsgId,
-						postContent, signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				getRandomBytes(MessageId.LENGTH + 1), previousMsgId,
+				postContent, signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsPostWithNonRawPreviousMsgId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						"non-raw", postContent, signature);
+	public void testRejectsPostWithNonRawParentId() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				"not raw", previousMsgId, postContent, signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooShortPreviousMsgId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						getRandomBytes(MessageId.LENGTH - 1),
-						postContent, signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, getRandomBytes(MessageId.LENGTH - 1), postContent,
+				signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooLongPreviousMsgId() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						getRandomBytes(MessageId.LENGTH + 1),
-						postContent, signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, getRandomBytes(MessageId.LENGTH + 1), postContent,
+				signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsPostWithEmptyContent() throws Exception {
-		postContent = "";
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature);
+	public void testRejectsPostWithNullPreviousMsgId() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, null, postContent, signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNonRawPreviousMsgId() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, "not raw", postContent, signature);
+		expectCreateAuthor(member);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithTooShortContent() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, "", signature);
+		expectCreateAuthor(member);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooLongContent() throws Exception {
-		postContent = getRandomString(MAX_GROUP_POST_BODY_LENGTH + 1);
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId,
+				getRandomString(MAX_GROUP_POST_BODY_LENGTH + 1), signature);
+		expectCreateAuthor(member);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNullContent() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, null, signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithNonStringContent() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, getRandomBytes(5), signature);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, getRandomBytes(5), signature);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
-	public void testRejectsPostWithEmptySignature() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, new byte[0]);
+	public void testRejectsPostWithTooShortSignature() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent, new byte[0]);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithTooLongSignature() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent,
-						getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent,
+				getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
+		expectCreateAuthor(member);
+		validator.validateMessage(message, group, body);
+	}
+
+	@Test(expected = FormatException.class)
+	public void testRejectsPostWithNullSignature() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent,null);
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = FormatException.class)
 	public void testRejectsPostWithNonRawSignature() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, "non-raw");
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent, "not raw");
 		expectCreateAuthor(member);
-		validator.validateMessage(message, group, list);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test(expected = InvalidMessageException.class)
 	public void testRejectsPostWithInvalidSignature() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature);
-		expectPostMessage(member, false);
-		validator.validateMessage(message, group, list);
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent, signature);
+		expectPostMessage(member, parentId, false);
+		validator.validateMessage(message, group, body);
 	}
 
 	@Test
-	public void testAcceptPost() throws Exception {
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature);
-		expectPostMessage(member, true);
+	public void testAcceptsPost() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
+				parentId, previousMsgId, postContent, signature);
+		expectPostMessage(member, parentId, true);
 		BdfMessageContext messageContext =
-				validator.validateMessage(message, group, list);
-		assertMessageContext(messageContext, member);
-		assertEquals(previousMsgId.getBytes(),
+				validator.validateMessage(message, group, body);
+		assertExpectedMessageContext(messageContext, POST, member,
+				Arrays.asList(parentId, previousMsgId));
+		assertArrayEquals(previousMsgId.getBytes(),
 				messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID));
-		assertEquals(parentId.getBytes(),
+		assertArrayEquals(parentId.getBytes(),
 				messageContext.getDictionary().getRaw(KEY_PARENT_MSG_ID));
 	}
 
 	@Test
-	public void testAcceptTopLevelPost() throws Exception {
-		parentId = null;
-		BdfList list =
-				BdfList.of(POST.getInt(), memberName, memberKey, parentId,
-						previousMsgId, postContent, signature);
-		expectPostMessage(member, true);
+	public void testAcceptsTopLevelPost() throws Exception {
+		BdfList body = BdfList.of(POST.getInt(), memberName, memberKey, null,
+				previousMsgId, postContent, signature);
+		expectPostMessage(member, null, true);
 		BdfMessageContext messageContext =
-				validator.validateMessage(message, group, list);
-		assertMessageContext(messageContext, member);
-		assertEquals(previousMsgId.getBytes(),
+				validator.validateMessage(message, group, body);
+		assertExpectedMessageContext(messageContext, POST, member,
+				Collections.singletonList(previousMsgId));
+		assertArrayEquals(previousMsgId.getBytes(),
 				messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID));
 		assertFalse(
 				messageContext.getDictionary().containsKey(KEY_PARENT_MSG_ID));
 	}
 
-	private void expectPostMessage(final Author member, final boolean sigValid)
-			throws Exception {
-		final BdfList signed =
-				BdfList.of(group.getId(), message.getTimestamp(), POST.getInt(),
-						member.getName(), member.getPublicKey(),
-						parentId == null ? null : parentId.getBytes(),
-						previousMsgId == null ? null : previousMsgId.getBytes(),
-						postContent);
+	private void expectPostMessage(final Author member,
+			final MessageId parentId, final boolean sigValid) throws Exception {
+		final BdfList signed = BdfList.of(group.getId(), message.getTimestamp(),
+				POST.getInt(), member.getName(), member.getPublicKey(),
+				parentId == null ? null : parentId.getBytes(),
+				previousMsgId.getBytes(), postContent);
 		expectCreateAuthor(member);
 		context.checking(new Expectations() {{
-			oneOf(clientHelper)
-					.verifySignature(SIGNING_LABEL_POST, signature,
-							member.getPublicKey(), signed);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					member.getPublicKey(), signed);
 			if (!sigValid) will(throwException(new GeneralSecurityException()));
 		}});
 	}
 
-	private void assertMessageContext(BdfMessageContext c, Author member)
-			throws FormatException {
+	private void assertExpectedMessageContext(BdfMessageContext c,
+			MessageType type, Author member,
+			Collection<MessageId> dependencies) throws FormatException {
 		BdfDictionary d = c.getDictionary();
-		assertTrue(message.getTimestamp() == d.getLong(KEY_TIMESTAMP));
+		assertEquals(type.getInt(), d.getLong(KEY_TYPE).intValue());
+		assertEquals(message.getTimestamp(),
+				d.getLong(KEY_TIMESTAMP).longValue());
 		assertFalse(d.getBoolean(KEY_READ));
 		assertEquals(member.getId().getBytes(), d.getRaw(KEY_MEMBER_ID));
 		assertEquals(member.getName(), d.getString(KEY_MEMBER_NAME));
 		assertEquals(member.getPublicKey(), d.getRaw(KEY_MEMBER_PUBLIC_KEY));
-
-		// assert message dependencies
-		if (d.getLong(KEY_TYPE) == POST.getInt()) {
-			assertTrue(c.getDependencies().contains(previousMsgId));
-			if (parentId != null) {
-				assertTrue(c.getDependencies().contains(parentId));
-			} else {
-				assertFalse(c.getDependencies().contains(parentId));
-			}
-		} else {
-			assertEquals(JOIN.getInt(), d.getLong(KEY_TYPE).intValue());
-		}
+		assertEquals(dependencies, c.getDependencies());
 	}
 
+	@Test(expected = InvalidMessageException.class)
+	public void testRejectsMessageWithUnknownType() throws Exception {
+		BdfList body = BdfList.of(POST.getInt() + 1, memberName, memberKey,
+				parentId, previousMsgId, postContent, signature);
+		expectCreateAuthor(member);
+		validator.validateMessage(message, group, body);
+	}
 }
-- 
GitLab