Skip to content
Snippets Groups Projects
Commit 3ae3fdab authored by Ernir Erlingsson's avatar Ernir Erlingsson
Browse files

Merge branch '450-blog-client-unit-tests' into 'master'

Blog Client Unit and Integration Tests

Closes #450

See merge request !230
parents 759b1c74 f7965edc
No related branches found
No related tags found
No related merge requests found
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static junit.framework.Assert.assertFalse;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.briarproject.api.sync.ValidationManager.State.VALID;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BlogManagerTest {
private LifecycleManager lifecycleManager0, lifecycleManager1;
private SyncSessionFactory sync0, sync1;
private BlogManager blogManager0, blogManager1;
private ContactManager contactManager0, contactManager1;
private ContactId contactId0,contactId1;
private IdentityManager identityManager0, identityManager1;
private LocalAuthor author0, author1;
private Blog blog0, blog1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
CryptoComponent crypto;
@Inject
BlogFactory blogFactory;
@Inject
BlogPostFactory blogPostFactory;
// objects accessed from background threads need to be volatile
private volatile Waiter validationWaiter;
private volatile Waiter deliveryWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String AUTHOR1 = "Author 1";
private final String AUTHOR2 = "Author 2";
private final String CONTENT_TYPE = "text/plain";
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private BlogManagerTestComponent t0, t1;
@Before
public void setUp() throws Exception {
BlogManagerTestComponent component =
DaggerBlogManagerTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, AUTHOR1);
t0 = DaggerBlogManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, AUTHOR2);
t1 = DaggerBlogManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
blogManager0 = t0.getBlogManager();
blogManager1 = t1.getBlogManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
// initialize waiters fresh for each test
validationWaiter = new Waiter();
deliveryWaiter = new Waiter();
}
@Test
public void testPersonalBlogInitialisation() throws Exception {
startLifecycles();
defaultInit();
Collection<Blog> blogs0 = blogManager0.getBlogs();
assertEquals(2, blogs0.size());
Iterator<Blog> i0 = blogs0.iterator();
assertEquals(author0, i0.next().getAuthor());
assertEquals(author1, i0.next().getAuthor());
Collection<Blog> blogs1 = blogManager1.getBlogs();
assertEquals(2, blogs1.size());
Iterator<Blog> i1 = blogs1.iterator();
assertEquals(author1, i1.next().getAuthor());
assertEquals(author0, i1.next().getAuthor());
assertEquals(blog0, blogManager0.getPersonalBlog(author0));
assertEquals(blog0, blogManager1.getPersonalBlog(author0));
assertEquals(blog1, blogManager0.getPersonalBlog(author1));
assertEquals(blog1, blogManager1.getPersonalBlog(author1));
assertEquals(blog0, blogManager0.getBlog(blog0.getId()));
assertEquals(blog0, blogManager1.getBlog(blog0.getId()));
assertEquals(blog1, blogManager0.getBlog(blog1.getId()));
assertEquals(blog1, blogManager1.getBlog(blog1.getId()));
assertEquals(1, blogManager0.getBlogs(author0).size());
assertEquals(1, blogManager1.getBlogs(author0).size());
assertEquals(1, blogManager0.getBlogs(author1).size());
assertEquals(1, blogManager1.getBlogs(author1).size());
stopLifecycles();
}
@Test
public void testBlogPost() throws Exception {
startLifecycles();
defaultInit();
// check that blog0 has no posts
final byte[] body = TestUtils.getRandomBytes(42);
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog0.getId());
assertEquals(0, headers0.size());
// add a post to blog0
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), null, clock.currentTimeMillis(),
null, author0, CONTENT_TYPE, body);
blogManager0.addLocalPost(p);
// check that post is now in blog0
headers0 = blogManager0.getPostHeaders(blog0.getId());
assertEquals(1, headers0.size());
// check that body is there
assertArrayEquals(body,
blogManager0.getPostBody(p.getMessage().getId()));
// make sure that blog0 at author1 doesn't have the post yet
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(0, headers1.size());
// sync the post over
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// make sure post arrived
headers1 = blogManager1.getPostHeaders(blog0.getId());
assertEquals(1, headers1.size());
// check that body is there
assertArrayEquals(body,
blogManager1.getPostBody(p.getMessage().getId()));
stopLifecycles();
}
@Test
public void testBlogPostInWrongBlog() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog1
final byte[] body = TestUtils.getRandomBytes(42);
BlogPost p = blogPostFactory
.createBlogPost(blog1.getId(), null, clock.currentTimeMillis(),
null, author0, CONTENT_TYPE, body);
blogManager0.addLocalPost(p);
// check that post is now in blog1
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(1, headers0.size());
// sync the post over
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// make sure post did not arrive, because of wrong signature
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog1.getId());
assertEquals(0, headers1.size());
stopLifecycles();
}
@Test
public void testAddAndRemoveBlog() throws Exception {
startLifecycles();
defaultInit();
String name = "Test Blog";
String desc = "Description";
// add blog
Blog blog = blogManager0.addBlog(author0, name, desc);
Collection<Blog> blogs0 = blogManager0.getBlogs();
assertEquals(3, blogs0.size());
assertTrue(blogs0.contains(blog));
assertEquals(2, blogManager0.getBlogs(author0).size());
// remove blog
blogManager0.removeBlog(blog);
blogs0 = blogManager0.getBlogs();
assertEquals(2, blogs0.size());
assertFalse(blogs0.contains(blog));
assertEquals(1, blogManager0.getBlogs(author0).size());
stopLifecycles();
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
}
private class Listener implements EventListener {
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (!event.isLocal()) {
if (event.getState() == DELIVERED) {
deliveryWaiter.resume();
} else if (event.getState() == VALID ||
event.getState() == INVALID ||
event.getState() == PENDING) {
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws DbException {
addDefaultIdentities();
addDefaultContacts();
listenToEvents();
}
private void addDefaultIdentities() throws DbException {
KeyPair keyPair0 = crypto.generateSignatureKeyPair();
byte[] publicKey0 = keyPair0.getPublic().getEncoded();
byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
author0 = authorFactory
.createLocalAuthor(AUTHOR1, publicKey0, privateKey0);
identityManager0.addLocalAuthor(author0);
blog0 = blogFactory.createPersonalBlog(author0);
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
byte[] publicKey1 = keyPair1.getPublic().getEncoded();
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
author1 = authorFactory
.createLocalAuthor(AUTHOR2, publicKey1, privateKey1);
identityManager1.addLocalAuthor(author1);
blog1 = blogFactory.createPersonalBlog(author1);
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
}
private void listenToEvents() {
Listener listener0 = new Listener();
t0.getEventBus().addListener(listener0);
Listener listener1 = new Listener();
t1.getEventBus().addListener(listener1);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager0.startServices();
lifecycleManager1.startServices();
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
}
private void injectEagerSingletons(BlogManagerTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new BlogsModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}
package org.briarproject;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
interface BlogManagerTestComponent {
void inject(BlogManagerTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(BlogsModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
BlogManager getBlogManager();
SyncSessionFactory getSyncSessionFactory();
}
package org.briarproject.blogs;
import org.briarproject.BriarTestCase;
import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.BlogPostAddedEvent;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
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.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.util.Collection;
import java.util.Collections;
import static org.briarproject.TestUtils.getRandomBytes;
import static org.briarproject.TestUtils.getRandomId;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BlogManagerImplTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final BlogManagerImpl blogManager;
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final MetadataParser metadataParser =
context.mock(MetadataParser.class);
private final BlogFactory blogFactory = context.mock(BlogFactory.class);
private final Blog blog1, blog2;
private final Message message;
private final MessageId messageId;
public BlogManagerImplTest() {
blogManager = new BlogManagerImpl(db, identityManager, clientHelper,
metadataParser, blogFactory);
blog1 = getBlog("Test Blog 1", "Test Description 1");
blog2 = getBlog("Test Blog 2", "Test Description 2");
messageId = new MessageId(getRandomId());
message = new Message(messageId, blog1.getId(), 42, getRandomBytes(42));
}
@Test
public void testClientId() {
assertEquals(CLIENT_ID, blogManager.getClientId());
}
@Test
public void testCreateLocalState() throws DbException {
final Transaction txn = new Transaction(null, false);
final Collection<LocalAuthor> localAuthors =
Collections.singletonList((LocalAuthor) blog1.getAuthor());
final ContactId contactId = new ContactId(0);
final Collection<ContactId> contactIds =
Collections.singletonList(contactId);
Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true);
final Collection<Contact> contacts = Collections.singletonList(contact);
context.checking(new Expectations() {{
oneOf(db).getLocalAuthors(txn);
will(returnValue(localAuthors));
oneOf(blogFactory).createPersonalBlog(blog1.getAuthor());
will(returnValue(blog1));
oneOf(db).containsGroup(txn, blog1.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, blog1.getGroup());
oneOf(db).getContacts(txn, blog1.getAuthor().getId());
will(returnValue(contactIds));
oneOf(db).setVisibleToContact(txn, contactId, blog1.getId(), true);
oneOf(db).getContacts(txn);
will(returnValue(contacts));
oneOf(blogFactory).createPersonalBlog(blog2.getAuthor());
will(returnValue(blog2));
oneOf(db).containsGroup(txn, blog2.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, blog2.getGroup());
oneOf(db).setVisibleToContact(txn, contactId, blog2.getId(), true);
oneOf(db).getLocalAuthor(txn, blog1.getAuthor().getId());
will(returnValue(blog1.getAuthor()));
oneOf(blogFactory).createPersonalBlog(blog1.getAuthor());
will(returnValue(blog1));
oneOf(db).setVisibleToContact(txn, contactId, blog1.getId(), true);
}});
blogManager.createLocalState(txn);
}
@Test
public void testRemovingContact() throws DbException {
final Transaction txn = new Transaction(null, false);
final ContactId contactId = new ContactId(0);
Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true);
context.checking(new Expectations() {{
oneOf(blogFactory).createPersonalBlog(blog2.getAuthor());
will(returnValue(blog2));
oneOf(db).removeGroup(txn, blog2.getGroup());
}});
blogManager.removingContact(txn, contact);
}
@Test
public void testAddingIdentity() throws DbException {
final Transaction txn = new Transaction(null, false);
Author a = blog1.getAuthor();
final LocalAuthor localAuthor =
new LocalAuthor(a.getId(), a.getName(), a.getPublicKey(),
a.getPublicKey(), 0);
context.checking(new Expectations() {{
oneOf(blogFactory).createPersonalBlog(localAuthor);
will(returnValue(blog1));
oneOf(db).addGroup(txn, blog1.getGroup());
}});
blogManager.addingIdentity(txn, localAuthor);
}
@Test
public void testRemovingIdentity() throws DbException {
final Transaction txn = new Transaction(null, false);
Author a = blog1.getAuthor();
final LocalAuthor localAuthor =
new LocalAuthor(a.getId(), a.getName(), a.getPublicKey(),
a.getPublicKey(), 0);
context.checking(new Expectations() {{
oneOf(blogFactory).createPersonalBlog(localAuthor);
will(returnValue(blog1));
oneOf(db).removeGroup(txn, blog1.getGroup());
}});
blogManager.removingIdentity(txn, localAuthor);
}
@Test
public void testIncomingMessage() throws DbException, FormatException {
final Transaction txn = new Transaction(null, false);
BdfList list = new BdfList();
BdfDictionary author = authorToBdfDictionary(blog1.getAuthor());
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_TIMESTAMP, 0),
new BdfEntry(KEY_TIME_RECEIVED, 1),
new BdfEntry(KEY_AUTHOR, author),
new BdfEntry(KEY_CONTENT_TYPE, 0),
new BdfEntry(KEY_READ, false),
new BdfEntry(KEY_CONTENT_TYPE, "text/plain")
);
context.checking(new Expectations() {{
oneOf(identityManager)
.getAuthorStatus(txn, blog1.getAuthor().getId());
will(returnValue(VERIFIED));
}});
blogManager.incomingMessage(txn, message, list, meta);
assertEquals(1, txn.getEvents().size());
assertTrue(txn.getEvents().get(0) instanceof BlogPostAddedEvent);
BlogPostAddedEvent e = (BlogPostAddedEvent) txn.getEvents().get(0);
assertEquals(blog1.getId(), e.getGroupId());
BlogPostHeader h = e.getHeader();
assertEquals(1, h.getTimeReceived());
assertEquals(messageId, h.getId());
assertEquals(null, h.getParentId());
assertEquals(VERIFIED, h.getAuthorStatus());
assertEquals("text/plain", h.getContentType());
assertEquals(blog1.getAuthor(), h.getAuthor());
}
@Test
public void testAddBlog() throws DbException, FormatException {
final Transaction txn = new Transaction(null, false);
Author a = blog1.getAuthor();
final LocalAuthor localAuthor =
new LocalAuthor(a.getId(), a.getName(), a.getPublicKey(),
a.getPublicKey(), 0);
final BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_DESCRIPTION, blog1.getDescription())
);
context.checking(new Expectations() {{
oneOf(blogFactory)
.createBlog(blog1.getName(), blog1.getDescription(),
blog1.getAuthor());
will(returnValue(blog1));
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).addGroup(txn, blog1.getGroup());
oneOf(clientHelper).mergeGroupMetadata(txn, blog1.getId(), meta);
oneOf(db).endTransaction(txn);
}});
blogManager
.addBlog(localAuthor, blog1.getName(), blog1.getDescription());
assertTrue(txn.isComplete());
}
@Test
public void testRemoveBlog() throws DbException, FormatException {
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
oneOf(db).removeGroup(txn, blog1.getGroup());
oneOf(db).endTransaction(txn);
}});
blogManager.removeBlog(blog1);
assertTrue(txn.isComplete());
}
@Test
public void testAddLocalPost() throws DbException, FormatException {
final Transaction txn = new Transaction(null, true);
final BlogPost post =
new BlogPost(null, message, null, blog1.getAuthor(),
"text/plain");
BdfDictionary authorMeta = authorToBdfDictionary(blog1.getAuthor());
final BdfDictionary meta = BdfDictionary.of(
new BdfEntry(KEY_TIMESTAMP, message.getTimestamp()),
new BdfEntry(KEY_AUTHOR, authorMeta),
new BdfEntry(KEY_CONTENT_TYPE, "text/plain"),
new BdfEntry(KEY_READ, true)
);
context.checking(new Expectations() {{
oneOf(clientHelper).addLocalMessage(message, CLIENT_ID, meta, true);
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(identityManager)
.getAuthorStatus(txn, blog1.getAuthor().getId());
will(returnValue(VERIFIED));
oneOf(db).endTransaction(txn);
}});
blogManager.addLocalPost(post);
assertTrue(txn.isComplete());
assertEquals(1, txn.getEvents().size());
assertTrue(txn.getEvents().get(0) instanceof BlogPostAddedEvent);
BlogPostAddedEvent e = (BlogPostAddedEvent) txn.getEvents().get(0);
assertEquals(blog1.getId(), e.getGroupId());
BlogPostHeader h = e.getHeader();
assertEquals(message.getTimestamp(), h.getTimeReceived());
assertEquals(messageId, h.getId());
assertEquals(null, h.getParentId());
assertEquals(VERIFIED, h.getAuthorStatus());
assertEquals("text/plain", h.getContentType());
assertEquals(blog1.getAuthor(), h.getAuthor());
}
private Blog getBlog(String name, String desc) {
final GroupId groupId = new GroupId(getRandomId());
final Group group = new Group(groupId, CLIENT_ID, getRandomBytes(42));
final AuthorId authorId = new AuthorId(getRandomId());
final byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
final byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
final long created = System.currentTimeMillis();
final LocalAuthor localAuthor =
new LocalAuthor(authorId, "Author", publicKey, privateKey,
created);
return new Blog(group, name, desc, localAuthor, false);
}
private BdfDictionary authorToBdfDictionary(Author a) {
return BdfDictionary.of(
new BdfEntry(KEY_AUTHOR_ID, a.getId()),
new BdfEntry(KEY_AUTHOR_NAME, a.getName()),
new BdfEntry(KEY_PUBLIC_KEY, a.getPublicKey())
);
}
}
package org.briarproject.blogs;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser;
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.MetadataEncoder;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.briarproject.system.SystemClock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.io.IOException;
import java.security.GeneralSecurityException;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class BlogPostValidatorTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final Blog blog;
private final Author author;
private final Group group;
private final Message message;
private final BlogPostValidator validator;
private final CryptoComponent cryptoComponent =
context.mock(CryptoComponent.class);
private final BlogFactory blogFactory = context.mock(BlogFactory.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final Clock clock = new SystemClock();
private final byte[] body = TestUtils.getRandomBytes(
MAX_BLOG_POST_BODY_LENGTH);
private final String contentType = "text/plain";
public BlogPostValidatorTest() {
GroupId groupId = new GroupId(TestUtils.getRandomId());
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = TestUtils.getRandomBytes(12);
group = new Group(groupId, clientId, descriptor);
AuthorId authorId = new AuthorId(TestUtils.getRandomBytes(AuthorId.LENGTH));
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
author = new Author(authorId, "Author", publicKey);
blog = new Blog(group, "Test Blog", "", author, false);
MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis();
byte[] raw = TestUtils.getRandomBytes(123);
message = new Message(messageId, group.getId(), timestamp, raw);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
validator = new BlogPostValidator(cryptoComponent, blogFactory,
clientHelper, metadataEncoder, clock);
context.assertIsSatisfied();
}
@Test
public void testValidateProperBlogPost()
throws IOException, GeneralSecurityException {
// Parent ID, content type, title (optional), post body, attachments
BdfList content = BdfList.of(null, contentType, null, body, null);
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(content, sigBytes);
expectCrypto(m, true);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
BdfDictionary authorDict = BdfDictionary.of(
new BdfEntry(KEY_AUTHOR_ID, author.getId()),
new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
);
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertFalse(result.getBoolean(KEY_READ));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateBlogPostWithoutAttachments()
throws IOException, GeneralSecurityException {
BdfList content = BdfList.of(null, contentType, null, body);
BdfList m = BdfList.of(content, null);
validator.validateMessage(message, group, m).getDictionary();
}
@Test(expected = FormatException.class)
public void testValidateBlogPostWithoutSignature()
throws IOException, GeneralSecurityException {
BdfList content = BdfList.of(null, contentType, null, body, null);
BdfList m = BdfList.of(content, null);
validator.validateMessage(message, group, m).getDictionary();
}
@Test(expected = InvalidMessageException.class)
public void testValidateBlogPostWithBadSignature()
throws IOException, GeneralSecurityException {
// Parent ID, content type, title (optional), post body, attachments
BdfList content = BdfList.of(null, contentType, null, body, null);
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(content, sigBytes);
expectCrypto(m, false);
validator.validateMessage(message, group, m).getDictionary();
}
private void expectCrypto(BdfList m, final boolean pass)
throws IOException, GeneralSecurityException {
final Signature signature = context.mock(Signature.class);
final KeyParser keyParser = context.mock(KeyParser.class);
final PublicKey publicKey = context.mock(PublicKey.class);
final BdfList content = m.getList(0);
final byte[] sigBytes = m.getRaw(1);
final BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), content);
context.checking(new Expectations() {{
oneOf(blogFactory).parseBlog(group, "");
will(returnValue(blog));
oneOf(cryptoComponent).getSignatureKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(blog.getAuthor().getPublicKey());
will(returnValue(publicKey));
oneOf(cryptoComponent).getSignature();
will(returnValue(signature));
oneOf(signature).initVerify(publicKey);
oneOf(clientHelper).toByteArray(signed);
will(returnValue(sigBytes));
oneOf(signature).update(sigBytes);
oneOf(signature).verify(sigBytes);
will(returnValue(pass));
}});
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment