diff --git a/briar-tests/src/org/briarproject/BriarMockTestCase.java b/briar-tests/src/org/briarproject/BriarMockTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..45b662013b6050e1c12b554f1437c674aa13a4e5 --- /dev/null +++ b/briar-tests/src/org/briarproject/BriarMockTestCase.java @@ -0,0 +1,14 @@ +package org.briarproject; + +import org.jmock.Mockery; +import org.junit.After; + +public abstract class BriarMockTestCase extends BriarTestCase { + + protected final Mockery context = new Mockery(); + + @After + public void checkExpectations() { + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/org/briarproject/ValidatorTestCase.java b/briar-tests/src/org/briarproject/ValidatorTestCase.java new file mode 100644 index 0000000000000000000000000000000000000000..4e5a3b5e62392abe2cd2333bf73e164c5eb24e57 --- /dev/null +++ b/briar-tests/src/org/briarproject/ValidatorTestCase.java @@ -0,0 +1,32 @@ +package org.briarproject; + +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.data.MetadataEncoder; +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; + +public abstract class ValidatorTestCase extends BriarMockTestCase { + + protected final ClientHelper clientHelper = + context.mock(ClientHelper.class); + protected final MetadataEncoder metadataEncoder = + context.mock(MetadataEncoder.class); + protected final Clock clock = context.mock(Clock.class); + + protected final MessageId messageId = + new MessageId(TestUtils.getRandomId()); + protected final GroupId groupId = new GroupId(TestUtils.getRandomId()); + protected final long timestamp = 1234567890 * 1000L; + protected final byte[] raw = TestUtils.getRandomBytes(123); + protected final Message message = + new Message(messageId, groupId, timestamp, raw); + protected final ClientId clientId = + new ClientId(TestUtils.getRandomString(123)); + protected final byte[] descriptor = TestUtils.getRandomBytes(123); + protected final Group group = new Group(groupId, clientId, descriptor); + +} diff --git a/briar-tests/src/org/briarproject/clients/BdfMessageValidatorTest.java b/briar-tests/src/org/briarproject/clients/BdfMessageValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1714b261b3ac6713776d851cd1974fe50d9891b2 --- /dev/null +++ b/briar-tests/src/org/briarproject/clients/BdfMessageValidatorTest.java @@ -0,0 +1,165 @@ +package org.briarproject.clients; + +import org.briarproject.ValidatorTestCase; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.BdfMessageContext; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.db.Metadata; +import org.briarproject.api.sync.Group; +import org.briarproject.api.sync.InvalidMessageException; +import org.briarproject.api.sync.Message; +import org.briarproject.api.sync.MessageContext; +import org.jmock.Expectations; +import org.jmock.lib.legacy.ClassImposteriser; +import org.junit.Test; + +import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +public class BdfMessageValidatorTest extends ValidatorTestCase { + + private final BdfMessageValidator subclassNotCalled = + new BdfMessageValidator(clientHelper, metadataEncoder, clock) { + @Override + protected BdfMessageContext validateMessage(Message m, Group g, + BdfList body) + throws InvalidMessageException, FormatException { + fail(); + return null; + } + }; + + private final BdfList body = BdfList.of(123, 456); + private final BdfDictionary dictionary = new BdfDictionary(); + private final Metadata meta = new Metadata(); + + public BdfMessageValidatorTest() { + context.setImposteriser(ClassImposteriser.INSTANCE); + } + + @Test(expected = InvalidMessageException.class) + public void testRejectsFarFutureTimestamp() throws Exception { + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE - 1)); + }}); + + subclassNotCalled.validateMessage(message, group); + } + + @Test + public void testAcceptsMaxTimestamp() throws Exception { + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp - MAX_CLOCK_DIFFERENCE)); + oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH, + raw.length - MESSAGE_HEADER_LENGTH); + will(returnValue(body)); + oneOf(metadataEncoder).encode(dictionary); + will(returnValue(meta)); + }}); + + BdfMessageValidator v = new BdfMessageValidator(clientHelper, + metadataEncoder, clock) { + @Override + protected BdfMessageContext validateMessage(Message m, Group g, + BdfList b) throws InvalidMessageException, FormatException { + assertSame(message, m); + assertSame(group, g); + assertSame(body, b); + return new BdfMessageContext(dictionary); + } + }; + MessageContext messageContext = v.validateMessage(message, group); + assertEquals(0, messageContext.getDependencies().size()); + assertSame(meta, messageContext.getMetadata()); + } + + @Test(expected = InvalidMessageException.class) + public void testRejectsTooShortMessage() throws Exception { + final byte[] invalidRaw = new byte[MESSAGE_HEADER_LENGTH]; + // Use a mock message so the length of the raw message can be invalid + final Message invalidMessage = context.mock(Message.class); + + context.checking(new Expectations() {{ + oneOf(invalidMessage).getTimestamp(); + will(returnValue(timestamp)); + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp)); + oneOf(invalidMessage).getRaw(); + will(returnValue(invalidRaw)); + }}); + + subclassNotCalled.validateMessage(invalidMessage, group); + } + + @Test + public void testAcceptsMinLengthMessage() throws Exception { + final byte[] shortRaw = new byte[MESSAGE_HEADER_LENGTH + 1]; + final Message shortMessage = + new Message(messageId, groupId, timestamp, shortRaw); + + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp)); + oneOf(clientHelper).toList(shortRaw, MESSAGE_HEADER_LENGTH, + shortRaw.length - MESSAGE_HEADER_LENGTH); + will(returnValue(body)); + oneOf(metadataEncoder).encode(dictionary); + will(returnValue(meta)); + }}); + + BdfMessageValidator v = new BdfMessageValidator(clientHelper, + metadataEncoder, clock) { + @Override + protected BdfMessageContext validateMessage(Message m, Group g, + BdfList b) throws InvalidMessageException, FormatException { + assertSame(shortMessage, m); + assertSame(group, g); + assertSame(body, b); + return new BdfMessageContext(dictionary); + } + }; + MessageContext messageContext = v.validateMessage(shortMessage, group); + assertEquals(0, messageContext.getDependencies().size()); + assertSame(meta, messageContext.getMetadata()); + } + + @Test(expected = InvalidMessageException.class) + public void testRejectsInvalidBdfList() throws Exception { + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp)); + oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH, + raw.length - MESSAGE_HEADER_LENGTH); + will(throwException(new FormatException())); + }}); + + subclassNotCalled.validateMessage(message, group); + } + + @Test(expected = InvalidMessageException.class) + public void testRethrowsFormatExceptionFromSubclass() throws Exception { + context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(timestamp)); + oneOf(clientHelper).toList(raw, MESSAGE_HEADER_LENGTH, + raw.length - MESSAGE_HEADER_LENGTH); + will(returnValue(body)); + }}); + + BdfMessageValidator v = new BdfMessageValidator(clientHelper, + metadataEncoder, clock) { + @Override + protected BdfMessageContext validateMessage(Message m, Group g, + BdfList b) throws InvalidMessageException, FormatException { + throw new FormatException(); + } + }; + v.validateMessage(message, group); + } +} diff --git a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java index 089cce54d979cdc067eba10510e9dcdcd4494bbd..17e5c1266dca74739f381392f5deb9815fa34dfb 100644 --- a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java +++ b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java @@ -1,14 +1,394 @@ package org.briarproject.forum; -import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.ValidatorTestCase; +import org.briarproject.api.FormatException; +import org.briarproject.api.UniqueId; +import org.briarproject.api.clients.BdfMessageContext; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.identity.Author; +import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.identity.AuthorId; +import org.briarproject.api.sync.InvalidMessageException; +import org.briarproject.api.sync.MessageId; +import org.jmock.Expectations; import org.junit.Test; -import static org.junit.Assert.fail; +import java.security.GeneralSecurityException; +import java.util.Collection; -public class ForumPostValidatorTest extends BriarTestCase { +import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; +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.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class ForumPostValidatorTest extends ValidatorTestCase { + + private final AuthorFactory authorFactory = + context.mock(AuthorFactory.class); + + private final MessageId parentId = new MessageId(TestUtils.getRandomId()); + private final String authorName = + TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH); + private final byte[] authorPublicKey = + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); + private final BdfList authorList = BdfList.of(authorName, authorPublicKey); + private final String content = + TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH); + private final byte[] signature = + TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH); + private final AuthorId authorId = new AuthorId(TestUtils.getRandomId()); + private final Author author = + new Author(authorId, authorName, authorPublicKey); + private final BdfList signedWithParent = BdfList.of(groupId, timestamp, + parentId.getBytes(), authorList, content); + private final BdfList signedWithoutParent = BdfList.of(groupId, timestamp, + null, authorList, content); + + @Test(expected = FormatException.class) + public void testRejectsTooShortBody() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongBody() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, signature, 123)); + } + + @Test + public void testAcceptsNullParentId() throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + oneOf(clientHelper).verifySignature(signature, authorPublicKey, + signedWithoutParent); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(null, authorList, content, signature)); + assertExpectedContext(messageContext, false, authorName); + } + + @Test(expected = FormatException.class) + public void testRejectsNonRawParentId() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(123, authorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortParentId() throws Exception { + byte[] invalidParentId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(invalidParentId, authorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongParentId() throws Exception { + byte[] invalidParentId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(invalidParentId, authorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullAuthorList() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, null, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonListAuthorList() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, 123, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortAuthorList() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, new BdfList(), content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongAuthorList() throws Exception { + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, BdfList.of(1, 2, 3), content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullAuthorName() throws Exception { + BdfList invalidAuthorList = BdfList.of(null, authorPublicKey); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonStringAuthorName() throws Exception { + BdfList invalidAuthorList = BdfList.of(123, authorPublicKey); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortAuthorName() throws Exception { + BdfList invalidAuthorList = BdfList.of("", authorPublicKey); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test + public void testAcceptsMinLengthAuthorName() throws Exception { + final String shortAuthorName = TestUtils.getRandomString(1); + BdfList shortNameAuthorList = + BdfList.of(shortAuthorName, authorPublicKey); + final Author shortNameAuthor = + new Author(authorId, shortAuthorName, authorPublicKey); + final BdfList signedWithShortNameAuthor = BdfList.of(groupId, timestamp, + parentId.getBytes(), shortNameAuthorList, content); + + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(shortAuthorName, authorPublicKey); + will(returnValue(shortNameAuthor)); + oneOf(clientHelper).verifySignature(signature, authorPublicKey, + signedWithShortNameAuthor); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(parentId, shortNameAuthorList, content, signature)); + assertExpectedContext(messageContext, true, shortAuthorName); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongAuthorName() throws Exception { + String invalidAuthorName = + TestUtils.getRandomString(MAX_AUTHOR_NAME_LENGTH + 1); + BdfList invalidAuthorList = + BdfList.of(invalidAuthorName, authorPublicKey); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullAuthorPublicKey() throws Exception { + BdfList invalidAuthorList = BdfList.of(authorName, null); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonRawAuthorPublicKey() throws Exception { + BdfList invalidAuthorList = BdfList.of(authorName, 123); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongAuthorPublicKey() throws Exception { + byte[] invalidAuthorPublicKey = + TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1); + BdfList invalidAuthorList = + BdfList.of(authorName, invalidAuthorPublicKey); + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, invalidAuthorList, content, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullContent() throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, null, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonStringContent() throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, 123, signature)); + } @Test - public void testUnitTestsExist() { - fail(); // FIXME: Write tests + public void testAcceptsMinLengthContent() throws Exception { + String shortContent = ""; + final BdfList signedWithShortContent = BdfList.of(groupId, timestamp, + parentId.getBytes(), authorList, shortContent); + + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + oneOf(clientHelper).verifySignature(signature, authorPublicKey, + signedWithShortContent); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(parentId, authorList, shortContent, signature)); + assertExpectedContext(messageContext, true, authorName); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongContent() throws Exception { + String invalidContent = + TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH + 1); + + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, invalidContent, signature)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullSignature() throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, null)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonRawSignature() throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, 123)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongSignature() throws Exception { + byte[] invalidSignature = + TestUtils.getRandomBytes(MAX_SIGNATURE_LENGTH + 1); + + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, invalidSignature)); + } + + @Test(expected = FormatException.class) + public void testRejectsIfVerifyingSignatureThrowsFormatException() + throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + oneOf(clientHelper).verifySignature(signature, authorPublicKey, + signedWithParent); + will(throwException(new FormatException())); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, signature)); + } + + @Test(expected = InvalidMessageException.class) + public void testRejectsIfVerifyingSignatureThrowsGeneralSecurityException() + throws Exception { + context.checking(new Expectations() {{ + oneOf(authorFactory).createAuthor(authorName, authorPublicKey); + will(returnValue(author)); + oneOf(clientHelper).verifySignature(signature, authorPublicKey, + signedWithParent); + will(throwException(new GeneralSecurityException())); + }}); + + ForumPostValidator v = new ForumPostValidator(authorFactory, + clientHelper, metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(parentId, authorList, content, signature)); + } + + private void assertExpectedContext(BdfMessageContext messageContext, + boolean hasParent, String authorName) throws FormatException { + BdfDictionary meta = messageContext.getDictionary(); + Collection<MessageId> dependencies = messageContext.getDependencies(); + if (hasParent) { + assertEquals(4, meta.size()); + assertArrayEquals(parentId.getBytes(), meta.getRaw("parent")); + assertEquals(1, dependencies.size()); + assertEquals(parentId, dependencies.iterator().next()); + } else { + assertEquals(3, meta.size()); + assertEquals(0, dependencies.size()); + } + assertEquals(timestamp, meta.getLong("timestamp").longValue()); + assertFalse(meta.getBoolean("read")); + BdfDictionary authorMeta = meta.getDictionary("author"); + assertEquals(3, authorMeta.size()); + assertArrayEquals(authorId.getBytes(), authorMeta.getRaw("id")); + assertEquals(authorName, authorMeta.getString("name")); + assertArrayEquals(authorPublicKey, authorMeta.getRaw("publicKey")); } } diff --git a/briar-tests/src/org/briarproject/forum/ForumSharingValidatorTest.java b/briar-tests/src/org/briarproject/forum/ForumSharingValidatorTest.java deleted file mode 100644 index 0b87d04c19c36a768b5fcf4222b1445d5cffdaf1..0000000000000000000000000000000000000000 --- a/briar-tests/src/org/briarproject/forum/ForumSharingValidatorTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.briarproject.forum; - -import org.briarproject.BriarTestCase; -import org.junit.Test; - -import static org.junit.Assert.fail; - -public class ForumSharingValidatorTest extends BriarTestCase { - - @Test - public void testUnitTestsExist() { - fail(); // FIXME: Write tests - } -} diff --git a/briar-tests/src/org/briarproject/messaging/PrivateMessageValidatorTest.java b/briar-tests/src/org/briarproject/messaging/PrivateMessageValidatorTest.java index 28668520ff6ba9a821226a93e24d50cc4131494c..3d5fad97a6c95ea9335f8b67ebcaf116fd0104d4 100644 --- a/briar-tests/src/org/briarproject/messaging/PrivateMessageValidatorTest.java +++ b/briar-tests/src/org/briarproject/messaging/PrivateMessageValidatorTest.java @@ -1,14 +1,84 @@ package org.briarproject.messaging; -import org.briarproject.BriarTestCase; +import org.briarproject.TestUtils; +import org.briarproject.ValidatorTestCase; +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.BdfMessageContext; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; import org.junit.Test; -import static org.junit.Assert.fail; +import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH; +import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; -public class PrivateMessageValidatorTest extends BriarTestCase { +public class PrivateMessageValidatorTest extends ValidatorTestCase { + + @Test(expected = FormatException.class) + public void testRejectsTooShortBody() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, new BdfList()); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongBody() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of("", 123)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullContent() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of((String) null)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonStringContent() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of(123)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongContent() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + String invalidContent = + TestUtils.getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH + 1); + v.validateMessage(message, group, BdfList.of(invalidContent)); + } @Test - public void testUnitTestsExist() { - fail(); // FIXME: Write tests + public void testAcceptsMaxLengthContent() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + String content = + TestUtils.getRandomString(MAX_PRIVATE_MESSAGE_BODY_LENGTH); + BdfMessageContext messageContext = + v.validateMessage(message, group, BdfList.of(content)); + assertExpectedContext(messageContext); + } + + @Test + public void testAcceptsMinLengthContent() throws Exception { + PrivateMessageValidator v = new PrivateMessageValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = + v.validateMessage(message, group, BdfList.of("")); + assertExpectedContext(messageContext); + } + + private void assertExpectedContext(BdfMessageContext messageContext) + throws FormatException { + BdfDictionary meta = messageContext.getDictionary(); + assertEquals(3, meta.size()); + assertEquals(timestamp, meta.getLong("timestamp").longValue()); + assertFalse(meta.getBoolean("local")); + assertFalse(meta.getBoolean(MSG_KEY_READ)); + assertEquals(0, messageContext.getDependencies().size()); } } diff --git a/briar-tests/src/org/briarproject/sharing/ForumSharingValidatorTest.java b/briar-tests/src/org/briarproject/sharing/ForumSharingValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7d3c6e22b25012af428b554a5c7018419643e53c --- /dev/null +++ b/briar-tests/src/org/briarproject/sharing/ForumSharingValidatorTest.java @@ -0,0 +1,343 @@ +package org.briarproject.sharing; + +import org.briarproject.TestUtils; +import org.briarproject.ValidatorTestCase; +import org.briarproject.api.FormatException; +import org.briarproject.api.UniqueId; +import org.briarproject.api.clients.BdfMessageContext; +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; +import org.junit.Test; + +import javax.annotation.Nullable; + +import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; +import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; +import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; +import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; +import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG; +import static org.briarproject.api.sharing.SharingConstants.LOCAL; +import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH; +import static org.briarproject.api.sharing.SharingConstants.SESSION_ID; +import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT; +import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT; +import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE; +import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION; +import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE; +import static org.briarproject.api.sharing.SharingConstants.TIME; +import static org.briarproject.api.sharing.SharingConstants.TYPE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class ForumSharingValidatorTest extends ValidatorTestCase { + + private final SessionId sessionId = new SessionId(TestUtils.getRandomId()); + private final String forumName = + TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH); + private final byte[] salt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH); + private final String content = + TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH); + + @Test + public void testAcceptsInvitationWithContent() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, content)); + assertExpectedContextForInvitation(messageContext, forumName, content); + } + + @Test + public void testAcceptsInvitationWithoutContent() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt)); + assertExpectedContextForInvitation(messageContext, forumName, null); + } + + @Test + public void testAcceptsAccept() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ACCEPT, sessionId)); + assertExpectedContext(messageContext, SHARE_MSG_TYPE_ACCEPT); + } + + @Test + public void testAcceptsDecline() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_DECLINE, sessionId)); + assertExpectedContext(messageContext, SHARE_MSG_TYPE_DECLINE); + } + + @Test + public void testAcceptsLeave() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_LEAVE, sessionId)); + assertExpectedContext(messageContext, SHARE_MSG_TYPE_LEAVE); + } + + @Test + public void testAcceptsAbort() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId)); + assertExpectedContext(messageContext, SHARE_MSG_TYPE_ABORT); + } + + @Test(expected = FormatException.class) + public void testRejectsNullMessageType() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of(null, sessionId)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonLongMessageType() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of("", sessionId)); + } + + @Test(expected = FormatException.class) + public void testRejectsInvalidMessageType() throws Exception { + int invalidMessageType = SHARE_MSG_TYPE_ABORT + 1; + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(invalidMessageType, sessionId)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullSessionId() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, null)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonRawSessionId() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, 123)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortSessionId() throws Exception { + byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongSessionId() throws Exception { + byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortBodyForAbort() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, BdfList.of(SHARE_MSG_TYPE_ABORT)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongBodyForAbort() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId, 123)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortBodyForInvitation() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongBodyForInvitation() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, content, 123)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullForumName() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, null, + salt, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonStringForumName() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, 123, + salt, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortForumName() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, "", + salt, content)); + } + + @Test + public void testAcceptsMinLengthForumName() throws Exception { + String shortForumName = TestUtils.getRandomString(1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, shortForumName, + salt, content)); + assertExpectedContextForInvitation(messageContext, shortForumName, + content); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongForumName() throws Exception { + String invalidForumName = + TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH + 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, + invalidForumName, salt, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullSalt() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + null, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonRawSalt() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + 123, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooShortSalt() throws Exception { + byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH - 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + invalidSalt, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongSalt() throws Exception { + byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH + 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + invalidSalt, content)); + } + + @Test(expected = FormatException.class) + public void testRejectsNullContent() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, null)); + } + + @Test(expected = FormatException.class) + public void testRejectsNonStringContent() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, 123)); + } + + @Test + public void testAcceptsMinLengthContent() throws Exception { + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + BdfMessageContext messageContext = v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, "")); + assertExpectedContextForInvitation(messageContext, forumName, ""); + } + + @Test(expected = FormatException.class) + public void testRejectsTooLongContent() throws Exception { + String invalidContent = + TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH + 1); + ForumSharingValidator v = new ForumSharingValidator(clientHelper, + metadataEncoder, clock); + v.validateMessage(message, group, + BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, + salt, invalidContent)); + } + + private void assertExpectedContextForInvitation( + BdfMessageContext messageContext, String forumName, + @Nullable String content) throws FormatException { + BdfDictionary meta = messageContext.getDictionary(); + if (content == null) { + assertEquals(6, meta.size()); + } else { + assertEquals(7, meta.size()); + assertEquals(content, meta.getString(INVITATION_MSG)); + } + assertEquals(forumName, meta.getString(FORUM_NAME)); + assertEquals(salt, meta.getRaw(FORUM_SALT)); + assertEquals(SHARE_MSG_TYPE_INVITATION, meta.getLong(TYPE).intValue()); + assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID)); + assertFalse(meta.getBoolean(LOCAL)); + assertEquals(timestamp, meta.getLong(TIME).longValue()); + assertEquals(0, messageContext.getDependencies().size()); + } + + private void assertExpectedContext(BdfMessageContext messageContext, + int type) throws FormatException { + BdfDictionary meta = messageContext.getDictionary(); + assertEquals(4, meta.size()); + assertEquals(type, meta.getLong(TYPE).intValue()); + assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID)); + assertFalse(meta.getBoolean(LOCAL)); + assertEquals(timestamp, meta.getLong(TIME).longValue()); + assertEquals(0, messageContext.getDependencies().size()); + } +}