diff --git a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingIntegrationTest.java index 6a394f6d87a5575b29a9bffc802ad3fe236e49b9..b66b98124d5c99b42996344397e104b42420eb69 100644 --- a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingIntegrationTest.java @@ -5,7 +5,6 @@ import net.jodah.concurrentunit.Waiter; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.NoSuchGroupException; -import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -493,26 +492,6 @@ public class BlogSharingIntegrationTest // 1 can again share blog 1 with 0 assertTrue( blogSharingManager1.canBeShared(blog1.getId(), contact0From1)); - - // re-create local state (simulates sign-out and sign-in) - Transaction txn0 = db0.startTransaction(false); - ((BlogSharingManagerImpl) blogSharingManager0).createLocalState(txn0); - db0.commitTransaction(txn0); - db0.endTransaction(txn0); - Transaction txn1 = db1.startTransaction(false); - ((BlogSharingManagerImpl) blogSharingManager1).createLocalState(txn1); - db1.commitTransaction(txn1); - db1.endTransaction(txn1); - - // ensure there's no error with the sessions by checking shareability - assertFalse( - blogSharingManager0.canBeShared(blog1.getId(), contact1From0)); - assertTrue( - blogSharingManager1.canBeShared(blog1.getId(), contact0From1)); - - // contacts should still be able to remove each other without errors - contactManager0.removeContact(contactId1From0); - contactManager1.removeContact(contactId0From1); } @Test diff --git a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11ef75f1f9872a6466f4aedc935b8c8e968a699a --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingManagerImplTest.java @@ -0,0 +1,253 @@ +package org.briarproject.briar.sharing; + +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.client.ContactGroupFactory; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.data.MetadataParser; +import org.briarproject.bramble.api.db.DatabaseComponent; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Metadata; +import org.briarproject.bramble.api.db.Transaction; +import org.briarproject.bramble.api.identity.Author; +import org.briarproject.bramble.api.identity.AuthorId; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.test.BrambleMockTestCase; +import org.briarproject.briar.api.blog.Blog; +import org.briarproject.briar.api.blog.BlogInvitationResponse; +import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.client.MessageTracker; +import org.briarproject.briar.api.client.SessionId; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; +import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.briarproject.briar.api.blog.BlogSharingManager.CLIENT_ID; +import static org.briarproject.briar.sharing.SharingConstants.GROUP_KEY_CONTACT_ID; + +public class BlogSharingManagerImplTest extends BrambleMockTestCase { + + private final Mockery context = new Mockery(); + private final BlogSharingManagerImpl blogSharingManager; + 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 SessionEncoder sessionEncoder = + context.mock(SessionEncoder.class); + private final SessionParser sessionParser = + context.mock(SessionParser.class); + private final ContactGroupFactory contactGroupFactory = + context.mock(ContactGroupFactory.class); + private final BlogManager blogManager = context.mock(BlogManager.class); + + private final AuthorId localAuthorId = new AuthorId(getRandomId()); + private final ContactId contactId = new ContactId(0); + private final AuthorId authorId = new AuthorId(getRandomId()); + private final Author author = new Author(authorId, "Author", + getRandomBytes(MAX_PUBLIC_KEY_LENGTH)); + private final Contact contact = + new Contact(contactId, author, localAuthorId, true, true); + private final Collection<Contact> contacts = + Collections.singletonList(contact); + private final Group contactGroup = + new Group(new GroupId(getRandomId()), CLIENT_ID, + getRandomBytes(42)); + private final Group blogGroup = + new Group(new GroupId(getRandomId()), BlogManager.CLIENT_ID, + getRandomBytes(42)); + private final Blog blog = new Blog(blogGroup, author, false); + @SuppressWarnings("unchecked") + private final ProtocolEngine<Blog> engine = + context.mock(ProtocolEngine.class); + + @SuppressWarnings("unchecked") + public BlogSharingManagerImplTest() { + MetadataParser metadataParser = context.mock(MetadataParser.class); + MessageTracker messageTracker = context.mock(MessageTracker.class); + MessageParser<Blog> messageParser = context.mock(MessageParser.class); + InvitationFactory<Blog, BlogInvitationResponse> invitationFactory = + context.mock(InvitationFactory.class); + blogSharingManager = + new BlogSharingManagerImpl(db, clientHelper, metadataParser, + messageParser, sessionEncoder, sessionParser, + messageTracker, contactGroupFactory, + engine, invitationFactory, identityManager, + blogManager); + } + + @Test + public void testAddingContactFreshState() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(0); + testAddingContact(sessions); + } + + @Test + public void testAddingContactExistingState() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(1); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + testAddingContact(sessions); + } + + @Test(expected = DbException.class) + public void testAddingContactMultipleSessions() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(2); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + testAddingContact(sessions); + } + + @Test + public void testRemovingBlogFreshState() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(0); + testRemovingBlog(sessions); + } + + @Test + public void testRemovingBlogExistingState() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(1); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + testRemovingBlog(sessions); + } + + @Test(expected = DbException.class) + public void testRemovingBlogMultipleSessions() throws Exception { + Map<MessageId, BdfDictionary> sessions = + new HashMap<MessageId, BdfDictionary>(2); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + sessions.put(new MessageId(getRandomId()), new BdfDictionary()); + testRemovingBlog(sessions); + } + + private void testAddingContact(final Map<MessageId, BdfDictionary> sessions) + throws Exception { + final Transaction txn = new Transaction(null, false); + final LocalAuthor localAuthor = + new LocalAuthor(localAuthorId, "Local Author", + getRandomBytes(MAX_PUBLIC_KEY_LENGTH), + getRandomBytes(MAX_PUBLIC_KEY_LENGTH), + System.currentTimeMillis()); + final BdfDictionary meta = BdfDictionary + .of(new BdfEntry(GROUP_KEY_CONTACT_ID, contactId.getInt())); + final Group localBlogGroup = + new Group(new GroupId(getRandomId()), BlogManager.CLIENT_ID, + getRandomBytes(42)); + final Blog localBlog = new Blog(localBlogGroup, localAuthor, false); + + context.checking(new Expectations() {{ + oneOf(db).getContacts(txn); + will(returnValue(contacts)); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact); + will(returnValue(contactGroup)); + oneOf(db).containsGroup(txn, contactGroup.getId()); + will(returnValue(false)); + oneOf(db).addGroup(txn, contactGroup); + oneOf(db).setGroupVisibility(txn, contactId, contactGroup.getId(), + SHARED); + oneOf(clientHelper) + .mergeGroupMetadata(txn, contactGroup.getId(), meta); + oneOf(identityManager).getLocalAuthor(txn); + will(returnValue(localAuthor)); + oneOf(blogManager).getPersonalBlog(localAuthor); + will(returnValue(localBlog)); + oneOf(blogManager).getPersonalBlog(author); + will(returnValue(blog)); + }}); + expectPreShareShareable(txn, contact, localBlog, sessions); + expectPreShareShareable(txn, contact, blog, sessions); + + blogSharingManager.createLocalState(txn); + } + + private void expectPreShareShareable(final Transaction txn, + final Contact contact, final Blog blog, + final Map<MessageId, BdfDictionary> sessions) throws Exception { + final Group contactGroup = + new Group(new GroupId(getRandomId()), CLIENT_ID, + getRandomBytes(42)); + final BdfDictionary sessionDict = new BdfDictionary(); + final Message message = + new Message(new MessageId(getRandomId()), contactGroup.getId(), + 42L, getRandomBytes(1337)); + context.checking(new Expectations() {{ + oneOf(contactGroupFactory) + .createContactGroup(CLIENT_ID, contact); + will(returnValue(contactGroup)); + oneOf(sessionParser) + .getSessionQuery(new SessionId(blog.getId().getBytes())); + will(returnValue(sessionDict)); + oneOf(clientHelper) + .getMessageMetadataAsDictionary(txn, contactGroup.getId(), + sessionDict); + will(returnValue(sessions)); + if (sessions.size() == 0) { + oneOf(db).addGroup(txn, blog.getGroup()); + oneOf(db).setGroupVisibility(txn, contact.getId(), + blog.getGroup().getId(), SHARED); + oneOf(clientHelper) + .createMessageForStoringMetadata(contactGroup.getId()); + will(returnValue(message)); + oneOf(db).addLocalMessage(txn, message, new Metadata(), false); + oneOf(sessionEncoder).encodeSession(with(any(Session.class))); + will(returnValue(sessionDict)); + oneOf(clientHelper).mergeMessageMetadata(txn, message.getId(), + sessionDict); + } + }}); + } + + private void testRemovingBlog(final Map<MessageId, BdfDictionary> sessions) + throws Exception { + final Transaction txn = new Transaction(null, false); + final BdfDictionary sessionDict = new BdfDictionary(); + final Session session = new Session(contactGroup.getId(), blog.getId()); + + context.checking(new Expectations() {{ + oneOf(db).getContacts(txn); + will(returnValue(contacts)); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, contact); + will(returnValue(contactGroup)); + oneOf(sessionParser) + .getSessionQuery(new SessionId(blog.getId().getBytes())); + will(returnValue(sessionDict)); + oneOf(clientHelper) + .getMessageMetadataAsDictionary(txn, contactGroup.getId(), + sessionDict); + will(returnValue(sessions)); + if (sessions.size() == 1) { + oneOf(sessionParser) + .parseSession(contactGroup.getId(), sessionDict); + will(returnValue(session)); + oneOf(engine).onLeaveAction(txn, session); + will(returnValue(session)); + oneOf(sessionEncoder).encodeSession(session); + will(returnValue(sessionDict)); + oneOf(clientHelper).mergeMessageMetadata(txn, + sessions.keySet().iterator().next(), sessionDict); + } + }}); + blogSharingManager.removingBlog(txn, blog); + } + +}