diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/client/ClientHelper.java b/bramble-api/src/main/java/org/briarproject/bramble/api/client/ClientHelper.java index 940ee2f13..da9ee3163 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/client/ClientHelper.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/client/ClientHelper.java @@ -10,6 +10,7 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.mailbox.MailboxUpdate; +import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; @@ -19,6 +20,7 @@ import org.briarproject.bramble.api.sync.MessageId; import java.security.GeneralSecurityException; import java.util.Collection; +import java.util.List; import java.util.Map; @NotNullByDefault @@ -134,6 +136,9 @@ public interface ClientHelper { BdfList serverSupports, BdfDictionary properties) throws FormatException; + List parseMailboxVersionList(BdfList bdfList) + throws FormatException; + /** * Retrieves the contact ID from the group metadata of the given contact * group. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdate.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdate.java index 8ead367d3..891a8e5a9 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdate.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdate.java @@ -9,13 +9,16 @@ import javax.annotation.concurrent.Immutable; @Immutable @NotNullByDefault public class MailboxUpdate { - - boolean hasMailbox; + private final boolean hasMailbox; private final List clientSupports; public MailboxUpdate(List clientSupports) { - this.hasMailbox = false; + this(clientSupports, false); + } + + MailboxUpdate(List clientSupports, boolean hasMailbox) { this.clientSupports = clientSupports; + this.hasMailbox = hasMailbox; } public List getClientSupports() { diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateManager.java index 92c0ebc78..0e101a408 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateManager.java @@ -57,6 +57,12 @@ public interface MailboxUpdateManager { */ String MSG_KEY_LOCAL = "local"; + /** + * Key in the client's local group for storing the clientSupports list that + * was last sent out. + */ + String GROUP_KEY_SENT_CLIENT_SUPPORTS = "sentClientSupports"; + MailboxUpdate getLocalUpdate(Transaction txn, ContactId c) throws DbException; diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateWithMailbox.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateWithMailbox.java index e84506dbd..5e3110afa 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateWithMailbox.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxUpdateWithMailbox.java @@ -20,8 +20,7 @@ public class MailboxUpdateWithMailbox extends MailboxUpdate { MailboxAuthToken authToken, MailboxFolderId inboxId, MailboxFolderId outboxId ) { - super(clientSupports); - this.hasMailbox = true; + super(clientSupports, true); this.serverSupports = serverSupports; this.onion = onion; this.authToken = authToken; @@ -29,6 +28,12 @@ public class MailboxUpdateWithMailbox extends MailboxUpdate { this.outboxId = outboxId; } + public MailboxUpdateWithMailbox(MailboxUpdateWithMailbox o, + List newClientSupports) { + this(newClientSupports, o.serverSupports, o.onion, o.authToken, + o.inboxId, o.outboxId); + } + public String getOnion() { return onion; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java index 7baf56f56..584986c0f 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/client/ClientHelperImpl.java @@ -419,9 +419,9 @@ class ClientHelperImpl implements ClientHelper { BdfList serverSupports, BdfDictionary properties) throws FormatException { List clientSupportsList = - getMailboxVersionList(clientSupports); + parseMailboxVersionList(clientSupports); List serverSupportsList = - getMailboxVersionList(serverSupports); + parseMailboxVersionList(serverSupports); // We must always learn what Mailbox API version(s) the client supports if (clientSupports.isEmpty()) { @@ -460,7 +460,8 @@ class ClientHelperImpl implements ClientHelper { new MailboxFolderId(inboxId), new MailboxFolderId(outboxId)); } - private List getMailboxVersionList(BdfList bdfList) + @Override + public List parseMailboxVersionList(BdfList bdfList) throws FormatException { List list = new ArrayList<>(); for (int i = 0; i < bdfList.size(); i++) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxModule.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxModule.java index e1ad79555..d4969c8cb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxModule.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxModule.java @@ -7,10 +7,13 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.mailbox.MailboxManager; import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxUpdateManager; +import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.sync.validation.ValidationManager; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.versioning.ClientVersioningManager; +import java.util.List; + import javax.inject.Inject; import javax.inject.Singleton; @@ -20,6 +23,7 @@ import dagger.Provides; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MAJOR_VERSION; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MINOR_VERSION; +import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS; @Module public class MailboxModule { @@ -66,6 +70,11 @@ public class MailboxModule { return validator; } + @Provides + List provideClientSupports() { + return CLIENT_SUPPORTS; + } + @Provides @Singleton MailboxUpdateManager provideMailboxUpdateManager( diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImpl.java index 7cbaf2fb9..5262e4ebd 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImpl.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager.ContactHook; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.db.DatabaseComponent; @@ -45,13 +46,13 @@ import javax.annotation.Nullable; import javax.inject.Inject; import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE; -import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS; @NotNullByDefault class MailboxUpdateManagerImpl implements MailboxUpdateManager, OpenDatabaseHook, ContactHook, ClientVersioningHook, IncomingMessageHook, MailboxHook { + private final List clientSupports; private final DatabaseComponent db; private final ClientHelper clientHelper; private final ClientVersioningManager clientVersioningManager; @@ -63,12 +64,14 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, private final Group localGroup; @Inject - MailboxUpdateManagerImpl(DatabaseComponent db, ClientHelper clientHelper, + MailboxUpdateManagerImpl(List clientSupports, + DatabaseComponent db, ClientHelper clientHelper, ClientVersioningManager clientVersioningManager, MetadataParser metadataParser, ContactGroupFactory contactGroupFactory, Clock clock, MailboxSettingsManager mailboxSettingsManager, CryptoComponent crypto) { + this.clientSupports = clientSupports; this.db = db; this.clientHelper = clientHelper; this.clientVersioningManager = clientVersioningManager; @@ -84,12 +87,46 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, @Override public void onDatabaseOpened(Transaction txn) throws DbException { if (db.containsGroup(txn, localGroup.getId())) { - return; + try { + BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary( + txn, localGroup.getId()); + BdfList sent = meta.getList(GROUP_KEY_SENT_CLIENT_SUPPORTS); + if (clientHelper.parseMailboxVersionList(sent) + .equals(clientSupports)) { + return; + } + } catch (FormatException e) { + throw new DbException(); + } + // Our current clientSupports list has changed compared to what we + // last sent out. + for (Contact c : db.getContacts(txn)) { + MailboxUpdate latest = getLocalUpdate(txn, c.getId()); + MailboxUpdate updated; + if (latest.hasMailbox()) { + updated = new MailboxUpdateWithMailbox( + (MailboxUpdateWithMailbox) latest, clientSupports); + } else { + updated = new MailboxUpdate(clientSupports); + } + Group g = getContactGroup(c); + storeMessageReplaceLatest(txn, g.getId(), updated); + } + } else { + db.addGroup(txn, localGroup); + // Set things up for any pre-existing contacts + for (Contact c : db.getContacts(txn)) { + addingContact(txn, c); + } } - db.addGroup(txn, localGroup); - // Set things up for any pre-existing contacts - for (Contact c : db.getContacts(txn)) { - addingContact(txn, c); + + try { + BdfDictionary meta = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + encodeSupportsList(clientSupports))); + clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta); + } catch (FormatException e) { + throw new DbException(); } } @@ -123,8 +160,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, @Override public void mailboxPaired(Transaction txn, String ownOnion, - List serverSupports) - throws DbException { + List serverSupports) throws DbException { for (Contact c : db.getContacts(txn)) { createAndSendUpdateWithMailbox(txn, c, serverSupports, ownOnion); } @@ -192,8 +228,8 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, @Override @Nullable - public MailboxUpdate getRemoteUpdate(Transaction txn, ContactId c) throws - DbException { + public MailboxUpdate getRemoteUpdate(Transaction txn, ContactId c) + throws DbException { return getUpdate(txn, db.getContact(txn, c), false); } @@ -207,7 +243,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, List serverSupports, String ownOnion) throws DbException { MailboxUpdate u = new MailboxUpdateWithMailbox( - CLIENT_SUPPORTS, serverSupports, ownOnion, + clientSupports, serverSupports, ownOnion, new MailboxAuthToken(crypto.generateUniqueId().getBytes()), new MailboxFolderId(crypto.generateUniqueId().getBytes()), new MailboxFolderId(crypto.generateUniqueId().getBytes())); @@ -224,7 +260,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, private void sendUpdateNoMailbox(Transaction txn, Contact c) throws DbException { Group g = getContactGroup(c); - MailboxUpdate u = new MailboxUpdate(CLIENT_SUPPORTS); + MailboxUpdate u = new MailboxUpdate(clientSupports); storeMessageReplaceLatest(txn, g.getId(), u); } @@ -307,8 +343,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, return supports; } - private MailboxUpdate parseUpdate(BdfList body) - throws FormatException { + private MailboxUpdate parseUpdate(BdfList body) throws FormatException { BdfList clientSupports = body.getList(1); BdfList serverSupports = body.getList(2); BdfDictionary dict = body.getDictionary(3); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxPairingTaskImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxPairingTaskImplTest.java index be7555d90..10c1a74fa 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxPairingTaskImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxPairingTaskImplTest.java @@ -12,6 +12,7 @@ import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxUpdate; import org.briarproject.bramble.api.mailbox.MailboxUpdateManager; +import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.mailbox.OwnMailboxConnectionStatusEvent; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.test.BrambleMockTestCase; @@ -29,7 +30,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; import static java.util.Collections.singletonList; -import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS; import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; @@ -107,7 +107,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase { }}); Contact contact1 = getContact(); Transaction txn = new Transaction(null, false); - MailboxUpdate updateNoMailbox = new MailboxUpdate(CLIENT_SUPPORTS); + MailboxUpdate updateNoMailbox = new MailboxUpdate( + singletonList(new MailboxVersion(47, 11))); context.checking(new DbExpectations() {{ oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(mailboxSettingsManager).setOwnMailboxProperties( diff --git a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImplTest.java index 0301f4dd0..e1bdcf934 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxUpdateManagerImplTest.java @@ -32,9 +32,11 @@ import org.junit.Test; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Random; import static java.util.Collections.singletonList; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID; +import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.GROUP_KEY_SENT_CLIENT_SUPPORTS; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MAJOR_VERSION; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MSG_KEY_LOCAL; import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MSG_KEY_VERSION; @@ -44,7 +46,6 @@ import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY_OUTBOXID; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE; -import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS; import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getMessage; @@ -74,33 +75,44 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final BdfDictionary propsDict; private final BdfDictionary emptyPropsDict = new BdfDictionary(); - private final BdfList realClientSupports; - private final BdfList someClientSupports; private final List someClientSupportsList; - private final BdfList emptyServerSupports; - private final BdfList someServerSupports; + private final BdfList someClientSupports; + private final List newerClientSupportsList; + private final BdfList newerClientSupports; private final List someServerSupportsList; + private final BdfList someServerSupports; + private final BdfList emptyServerSupports = new BdfList(); private final MailboxUpdateWithMailbox updateWithMailbox; private final MailboxUpdate updateNoMailbox; private final MailboxProperties ownProps; public MailboxUpdateManagerImplTest() { - someClientSupports = BdfList.of(BdfList.of(1, 0)); - someClientSupportsList = singletonList(new MailboxVersion(1, 0)); - emptyServerSupports = new BdfList(); - someServerSupports = BdfList.of(BdfList.of(1, 0)); - someServerSupportsList = singletonList(new MailboxVersion(1, 0)); - realClientSupports = new BdfList(); - for (MailboxVersion v : CLIENT_SUPPORTS) { - realClientSupports.add(BdfList.of(v.getMajor(), v.getMinor())); - } + Random rnd = new Random(); + someClientSupportsList = singletonList(new MailboxVersion( + rnd.nextInt(), rnd.nextInt())); + someClientSupports = BdfList.of(BdfList.of( + someClientSupportsList.get(0).getMajor(), + someClientSupportsList.get(0).getMinor())); + newerClientSupportsList = singletonList(new MailboxVersion( + someClientSupportsList.get(0).getMajor(), + someClientSupportsList.get(0).getMinor() + 1)); + newerClientSupports = BdfList.of(BdfList.of( + newerClientSupportsList.get(0).getMajor(), + newerClientSupportsList.get(0).getMinor())); + + someServerSupportsList = singletonList(new MailboxVersion( + rnd.nextInt(), rnd.nextInt())); + someServerSupports = BdfList.of(BdfList.of( + someServerSupportsList.get(0).getMajor(), + someServerSupportsList.get(0).getMinor())); + + updateNoMailbox = new MailboxUpdate(someClientSupportsList); + ownProps = new MailboxProperties("http://bar.onion", new MailboxAuthToken(getRandomId()), true, someServerSupportsList); - updateWithMailbox = new MailboxUpdateWithMailbox( - singletonList(new MailboxVersion(1, 0)), - singletonList(new MailboxVersion(1, 0)), - ownProps.getOnion(), + updateWithMailbox = new MailboxUpdateWithMailbox(someClientSupportsList, + someServerSupportsList, ownProps.getOnion(), new MailboxAuthToken(getRandomId()), new MailboxFolderId(getRandomId()), new MailboxFolderId(getRandomId())); @@ -112,18 +124,18 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { .getBytes()); propsDict.put(PROP_KEY_OUTBOXID, updateWithMailbox.getOutboxId() .getBytes()); - updateNoMailbox = new MailboxUpdate(someClientSupportsList); } - private MailboxUpdateManagerImpl createInstance() { + private MailboxUpdateManagerImpl createInstance( + List clientSupports) { context.checking(new Expectations() {{ oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID, MAJOR_VERSION); will(returnValue(localGroup)); }}); - return new MailboxUpdateManagerImpl(db, clientHelper, - clientVersioningManager, metadataParser, contactGroupFactory, - clock, mailboxSettingsManager, crypto); + return new MailboxUpdateManagerImpl(clientSupports, db, + clientHelper, clientVersioningManager, metadataParser, + contactGroupFactory, clock, mailboxSettingsManager, crypto); } @Test @@ -132,6 +144,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { Contact contact = getContact(); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Map messageMetadata = new LinkedHashMap<>(); + BdfDictionary sentDict = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + someClientSupports)); context.checking(new Expectations() {{ oneOf(db).containsGroup(txn, localGroup.getId()); @@ -139,6 +154,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).addGroup(txn, localGroup); oneOf(db).getContacts(txn); will(returnValue(singletonList(contact))); + + // addingContact() oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); @@ -158,11 +175,14 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroup.getId()); will(returnValue(messageMetadata)); - expectStoreMessage(txn, contactGroup.getId(), 1, realClientSupports, + expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports, emptyServerSupports, emptyPropsDict, true); + + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + sentDict); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.onDatabaseOpened(txn); } @@ -173,6 +193,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { Contact contact = getContact(); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Map messageMetadata = new LinkedHashMap<>(); + BdfDictionary sentDict = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + someClientSupports)); context.checking(new Expectations() {{ oneOf(db).containsGroup(txn, localGroup.getId()); @@ -180,6 +203,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).addGroup(txn, localGroup); oneOf(db).getContacts(txn); will(returnValue(singletonList(contact))); + + // addingContact() oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); @@ -205,25 +230,190 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroup.getId()); will(returnValue(messageMetadata)); - expectStoreMessage(txn, contactGroup.getId(), 1, realClientSupports, + expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports, someServerSupports, propsDict, true); + + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + sentDict); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.onDatabaseOpened(txn); } @Test - public void testDoesNotCreateGroupsAtStartupIfAlreadyCreated() - throws Exception { + public void testUnchangedClientSupportsOnSecondStartup() throws Exception { Transaction txn = new Transaction(null, false); + Contact contact = getContact(); + Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); + + Map emptyMessageMetadata = + new LinkedHashMap<>(); + BdfDictionary sentDict = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + someClientSupports)); + + context.checking(new Expectations() {{ + oneOf(db).containsGroup(txn, localGroup.getId()); + will(returnValue(false)); + oneOf(db).addGroup(txn, localGroup); + oneOf(db).getContacts(txn); + will(returnValue(singletonList(contact))); + + // addingContact() + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(db).addGroup(txn, contactGroup); + oneOf(clientVersioningManager).getClientVisibility(txn, + contact.getId(), CLIENT_ID, MAJOR_VERSION); + will(returnValue(SHARED)); + oneOf(db).setGroupVisibility(txn, contact.getId(), + contactGroup.getId(), SHARED); + oneOf(clientHelper).setContactId(txn, contactGroup.getId(), + contact.getId()); + oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn); + will(returnValue(null)); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + contactGroup.getId()); + will(returnValue(emptyMessageMetadata)); + expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports, + emptyServerSupports, emptyPropsDict, true); + + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + sentDict); + }}); + + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); + t.onDatabaseOpened(txn); + context.checking(new Expectations() {{ oneOf(db).containsGroup(txn, localGroup.getId()); will(returnValue(true)); + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(sentDict)); + oneOf(clientHelper).parseMailboxVersionList(someClientSupports); + will(returnValue(someClientSupportsList)); }}); - MailboxUpdateManagerImpl t = createInstance(); + t = createInstance(someClientSupportsList); + t.onDatabaseOpened(txn); + } + + @Test + public void testSendsUpdateWhenClientSupportsChangedOnSecondStartup() + throws Exception { + Transaction txn = new Transaction(null, false); + + Contact contact = getContact(); + Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); + + Map emptyMessageMetadata = + new LinkedHashMap<>(); + BdfDictionary sentDict = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + someClientSupports)); + + context.checking(new Expectations() {{ + oneOf(db).containsGroup(txn, localGroup.getId()); + will(returnValue(false)); + oneOf(db).addGroup(txn, localGroup); + oneOf(db).getContacts(txn); + will(returnValue(singletonList(contact))); + + // addingContact() + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(db).addGroup(txn, contactGroup); + oneOf(clientVersioningManager).getClientVisibility(txn, + contact.getId(), CLIENT_ID, MAJOR_VERSION); + will(returnValue(SHARED)); + oneOf(db).setGroupVisibility(txn, contact.getId(), + contactGroup.getId(), SHARED); + oneOf(clientHelper).setContactId(txn, contactGroup.getId(), + contact.getId()); + oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn); + will(returnValue(null)); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + contactGroup.getId()); + will(returnValue(emptyMessageMetadata)); + expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports, + emptyServerSupports, emptyPropsDict, true); + + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + sentDict); + }}); + + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); + t.onDatabaseOpened(txn); + + BdfDictionary metaDictionary = BdfDictionary.of( + new BdfEntry(MSG_KEY_VERSION, 1), + new BdfEntry(MSG_KEY_LOCAL, true) + ); + Map messageMetadata = new LinkedHashMap<>(); + MessageId messageId = new MessageId(getRandomId()); + messageMetadata.put(messageId, metaDictionary); + BdfList body = BdfList.of(1, someClientSupports, someServerSupports, + propsDict); + BdfDictionary newerSentDict = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_CLIENT_SUPPORTS, + newerClientSupports)); + + context.checking(new Expectations() {{ + oneOf(db).containsGroup(txn, localGroup.getId()); + will(returnValue(true)); + + // Find out that we are now on newerClientSupportsList + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(sentDict)); + oneOf(clientHelper).parseMailboxVersionList(someClientSupports); + will(returnValue(someClientSupportsList)); + + oneOf(db).getContacts(txn); + will(returnValue(singletonList(contact))); + oneOf(db).getContact(txn, contact.getId()); + will(returnValue(contact)); + + // getLocalUpdate() + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + contactGroup.getId()); + will(returnValue(messageMetadata)); + oneOf(clientHelper).getMessageAsList(txn, messageId); + will(returnValue(body)); + oneOf(clientHelper).parseAndValidateMailboxUpdate( + someClientSupports, someServerSupports, propsDict); + will(returnValue(updateWithMailbox)); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + + // storeMessageReplaceLatest() + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + contactGroup.getId()); + will(returnValue(messageMetadata)); + expectStoreMessage(txn, contactGroup.getId(), 2, + newerClientSupports, someServerSupports, propsDict, true); + oneOf(db).removeMessage(txn, messageId); + + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + newerSentDict); + }}); + + t = createInstance(newerClientSupportsList); t.onDatabaseOpened(txn); } @@ -256,11 +446,11 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroup.getId()); will(returnValue(messageMetadata)); - expectStoreMessage(txn, contactGroup.getId(), 1, realClientSupports, + expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports, emptyServerSupports, emptyPropsDict, true); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.addingContact(txn, contact); } @@ -303,7 +493,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { someServerSupports, propsDict, true); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.addingContact(txn, contact); } @@ -320,7 +510,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).removeGroup(txn, contactGroup); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.removingContact(txn, contact); } @@ -362,7 +552,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).resetUnackedMessagesToSend(txn, contact.getId()); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); assertEquals(ACCEPT_DO_NOT_SHARE, t.incomingMessage(txn, message, meta)); assertTrue(hasEvent(txn, RemoteMailboxUpdateEvent.class)); @@ -414,7 +604,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).resetUnackedMessagesToSend(txn, contact.getId()); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); assertEquals(ACCEPT_DO_NOT_SHARE, t.incomingMessage(txn, message, meta)); assertTrue(hasEvent(txn, RemoteMailboxUpdateEvent.class)); @@ -448,7 +638,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).deleteMessageMetadata(txn, message.getId()); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); assertEquals(ACCEPT_DO_NOT_SHARE, t.incomingMessage(txn, message, meta)); assertFalse(hasEvent(txn, RemoteMailboxUpdateEvent.class)); @@ -494,7 +684,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).removeMessage(txn, latestId); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.mailboxPaired(txn, ownProps.getOnion(), someServerSupportsList); } @@ -532,7 +722,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { oneOf(db).removeMessage(txn, latestId); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); t.mailboxUnpaired(txn); } @@ -567,7 +757,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { will(returnValue(updateWithMailbox)); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdate remote = t.getRemoteUpdate(txn, contact.getId()); assertTrue(mailboxUpdateEqual(remote, updateWithMailbox)); } @@ -592,7 +782,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { will(returnValue(emptyMessageMetadata)); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); assertNull(t.getRemoteUpdate(txn, contact.getId())); } @@ -627,7 +817,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { will(returnValue(updateNoMailbox)); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdate remote = t.getRemoteUpdate(txn, contact.getId()); assertTrue(mailboxUpdateEqual(remote, updateNoMailbox)); } @@ -663,7 +853,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { will(returnValue(updateWithMailbox)); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdate local = t.getLocalUpdate(txn, contact.getId()); assertTrue(mailboxUpdateEqual(local, updateWithMailbox)); } @@ -699,7 +889,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { will(returnValue(updateNoMailbox)); }}); - MailboxUpdateManagerImpl t = createInstance(); + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdate local = t.getLocalUpdate(txn, contact.getId()); assertTrue(mailboxUpdateEqual(local, updateNoMailbox)); }