From 9990fb3b8fe3ce3df5508857f8655f144cdff6c9 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 12 Aug 2022 16:38:15 +0100 Subject: [PATCH] When our mailbox's API versions change, send them to contacts. --- .../api/mailbox/MailboxSettingsManager.java | 12 +- .../api/mailbox/MailboxUpdateManager.java | 6 + .../mailbox/MailboxSettingsManagerImpl.java | 13 +++ .../mailbox/MailboxUpdateManagerImpl.java | 105 ++++++++++++++++-- .../MailboxSettingsManagerImplTest.java | 66 ++++++++--- .../mailbox/MailboxUpdateManagerImplTest.java | 104 ++++++++++++++++- 6 files changed, 277 insertions(+), 29 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxSettingsManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxSettingsManager.java index fd717eda4..06c383ddb 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxSettingsManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxSettingsManager.java @@ -46,7 +46,7 @@ public interface MailboxSettingsManager { interface MailboxHook { /** - * Called when Briar is paired with a mailbox + * Called when Briar is paired with a mailbox. * * @param txn A read-write transaction */ @@ -54,10 +54,18 @@ public interface MailboxSettingsManager { throws DbException; /** - * Called when the mailbox is unpaired + * Called when the mailbox is unpaired. * * @param txn A read-write transaction */ void mailboxUnpaired(Transaction txn) throws DbException; + + /** + * Called when we receive our mailbox's server-supported API versions. + * + * @param txn A read-write transaction + */ + void serverSupportedVersionsReceived(Transaction txn, + List serverSupports) throws DbException; } } 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 7a5cf0e18..2c82db331 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 @@ -79,6 +79,12 @@ public interface MailboxUpdateManager { */ String GROUP_KEY_SENT_CLIENT_SUPPORTS = "sentClientSupports"; + /** + * Key in the client's local group for storing the serverSupports list that + * was last sent out, if any. + */ + String GROUP_KEY_SENT_SERVER_SUPPORTS = "sentServerSupports"; + /** * Returns the latest {@link MailboxUpdate} sent to the given contact. *

diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImpl.java index 9e33216a2..62dc1e584 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImpl.java @@ -119,6 +119,12 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager { @Override public void recordSuccessfulConnection(Transaction txn, long now, List versions) throws DbException { + // if we no longer have a paired mailbox, return + Settings oldSettings = + settingsManager.getSettings(txn, SETTINGS_NAMESPACE); + String onion = oldSettings.get(SETTINGS_KEY_ONION); + String token = oldSettings.get(SETTINGS_KEY_TOKEN); + if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return; Settings s = new Settings(); // record the successful connection s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now); @@ -126,6 +132,9 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager { s.putInt(SETTINGS_KEY_ATTEMPTS, 0); encodeServerSupports(versions, s); settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE); + for (MailboxHook hook : hooks) { + hook.serverSupportedVersionsReceived(txn, versions); + } // broadcast status event MailboxStatus status = new MailboxStatus(now, now, 0, versions); txn.attach(new OwnMailboxConnectionStatusEvent(status)); @@ -134,8 +143,12 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager { @Override public void recordFailedConnectionAttempt(Transaction txn, long now) throws DbException { + // if we no longer have a paired mailbox, return Settings oldSettings = settingsManager.getSettings(txn, SETTINGS_NAMESPACE); + String onion = oldSettings.get(SETTINGS_KEY_ONION); + String token = oldSettings.get(SETTINGS_KEY_TOKEN); + if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return; int newAttempts = 1 + oldSettings.getInt(SETTINGS_KEY_ATTEMPTS, 0); long lastSuccess = oldSettings.getLong(SETTINGS_KEY_LAST_SUCCESS, 0); Settings newSettings = new Settings(); 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 4a6556c52..7c6ddadfd 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 @@ -49,6 +49,9 @@ import java.util.Map.Entry; import javax.annotation.Nullable; import javax.inject.Inject; +import static java.util.Collections.emptyList; +import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; +import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE; @NotNullByDefault @@ -175,6 +178,12 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, localUpdates.put(c.getId(), u); } txn.attach(new MailboxPairedEvent(p, localUpdates)); + // Store the list of server-supported versions + try { + storeSentServerSupports(txn, p.getServerSupports()); + } catch (FormatException e) { + throw new DbException(); + } } @Override @@ -185,6 +194,76 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, localUpdates.put(c.getId(), u); } txn.attach(new MailboxUnpairedEvent(localUpdates)); + // Remove the list of server-supported versions + try { + BdfDictionary meta = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_SERVER_SUPPORTS, NULL_VALUE)); + clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta); + } catch (FormatException e) { + throw new DbException(); + } + } + + @Override + public void serverSupportedVersionsReceived(Transaction txn, + List serverSupports) throws DbException { + try { + List oldServerSupports = + loadSentServerSupports(txn); + if (serverSupports.equals(oldServerSupports)) return; + storeSentServerSupports(txn, serverSupports); + for (Contact c : db.getContacts(txn)) { + Group contactGroup = getContactGroup(c); + LatestUpdate latest = + findLatest(txn, contactGroup.getId(), true); + // This method should only be called when we have a mailbox + if (latest == null) throw new DbException(); + BdfList body = + clientHelper.getMessageAsList(txn, latest.messageId); + MailboxUpdate oldUpdate = parseUpdate(body); + if (!oldUpdate.hasMailbox()) throw new DbException(); + MailboxUpdateWithMailbox newUpdate = updateServerSupports( + (MailboxUpdateWithMailbox) oldUpdate, serverSupports); + storeMessageReplaceLatest(txn, contactGroup.getId(), newUpdate, + latest); + } + } catch (FormatException e) { + throw new DbException(); + } + } + + private void storeSentServerSupports(Transaction txn, + List serverSupports) + throws DbException, FormatException { + BdfDictionary meta = BdfDictionary.of(new BdfEntry( + GROUP_KEY_SENT_SERVER_SUPPORTS, + encodeSupportsList(serverSupports))); + clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta); + } + + private List loadSentServerSupports(Transaction txn) + throws DbException, FormatException { + BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, + localGroup.getId()); + BdfList serverSupports = + meta.getOptionalList(GROUP_KEY_SENT_SERVER_SUPPORTS); + if (serverSupports == null) return emptyList(); + return clientHelper.parseMailboxVersionList(serverSupports); + } + + /** + * Returns a new {@link MailboxUpdateWithMailbox} that updates the list + * of server-supported API versions in the given + * {@link MailboxUpdateWithMailbox}. + */ + private MailboxUpdateWithMailbox updateServerSupports( + MailboxUpdateWithMailbox old, List serverSupports) { + MailboxProperties oldProps = old.getMailboxProperties(); + MailboxProperties newProps = new MailboxProperties(oldProps.getOnion(), + oldProps.getAuthToken(), serverSupports, + requireNonNull(oldProps.getInboxId()), + requireNonNull(oldProps.getOutboxId())); + return new MailboxUpdateWithMailbox(old.getClientSupports(), newProps); } @Override @@ -304,21 +383,27 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager, MailboxUpdate u) throws DbException { try { LatestUpdate latest = findLatest(txn, g, true); - long version = latest == null ? 1 : latest.version + 1; - Message m = clientHelper.createMessage(g, clock.currentTimeMillis(), - encodeProperties(version, u)); - BdfDictionary meta = new BdfDictionary(); - meta.put(MSG_KEY_VERSION, version); - meta.put(MSG_KEY_LOCAL, true); - clientHelper.addLocalMessage(txn, m, meta, true, false); - if (latest != null) { - db.removeMessage(txn, latest.messageId); - } + storeMessageReplaceLatest(txn, g, u, latest); } catch (FormatException e) { throw new DbException(e); } } + private void storeMessageReplaceLatest(Transaction txn, GroupId g, + MailboxUpdate u, @Nullable LatestUpdate latest) + throws DbException, FormatException { + long version = latest == null ? 1 : latest.version + 1; + Message m = clientHelper.createMessage(g, clock.currentTimeMillis(), + encodeProperties(version, u)); + BdfDictionary meta = new BdfDictionary(); + meta.put(MSG_KEY_VERSION, version); + meta.put(MSG_KEY_LOCAL, true); + clientHelper.addLocalMessage(txn, m, meta, true, false); + if (latest != null) { + db.removeMessage(txn, latest.messageId); + } + } + @Nullable private LatestUpdate findLatest(Transaction txn, GroupId g, boolean local) throws DbException, FormatException { diff --git a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImplTest.java index e49463a02..8c833a4d7 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxSettingsManagerImplTest.java @@ -29,6 +29,7 @@ import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTIN import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_UPLOADS_NAMESPACE; import static org.briarproject.bramble.test.TestUtils.getEvent; import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.briarproject.bramble.test.TestUtils.hasEvent; import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -52,6 +53,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { private final MailboxProperties properties = new MailboxProperties(onion, token, serverSupports); private final int[] serverSupportsInts = {1, 0, 1, 1}; + private final Settings pairedSettings; private final ContactId contactId1 = new ContactId(random.nextInt()); private final ContactId contactId2 = new ContactId(random.nextInt()); private final ContactId contactId3 = new ContactId(random.nextInt()); @@ -60,6 +62,14 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { private final long lastSuccess = now - 2345; private final int attempts = 123; + public MailboxSettingsManagerImplTest() { + pairedSettings = new Settings(); + pairedSettings.put(SETTINGS_KEY_ONION, onion); + pairedSettings.put(SETTINGS_KEY_TOKEN, token.toString()); + pairedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, + serverSupportsInts); + } + @Test public void testReturnsNullPropertiesIfSettingsAreEmpty() throws Exception { Transaction txn = new Transaction(null, true); @@ -76,14 +86,10 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { @Test public void testReturnsProperties() throws Exception { Transaction txn = new Transaction(null, true); - Settings settings = new Settings(); - settings.put(SETTINGS_KEY_ONION, onion); - settings.put(SETTINGS_KEY_TOKEN, token.toString()); - settings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, serverSupportsInts); context.checking(new Expectations() {{ oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE); - will(returnValue(settings)); + will(returnValue(pairedSettings)); }}); MailboxProperties properties = manager.getOwnMailboxProperties(txn); @@ -97,16 +103,11 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { @Test public void testStoresProperties() throws Exception { Transaction txn = new Transaction(null, false); - Settings expectedSettings = new Settings(); - expectedSettings.put(SETTINGS_KEY_ONION, onion); - expectedSettings.put(SETTINGS_KEY_TOKEN, token.toString()); - expectedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, - serverSupportsInts); manager.registerMailboxHook(hook); context.checking(new Expectations() {{ - oneOf(settingsManager).mergeSettings(txn, expectedSettings, + oneOf(settingsManager).mergeSettings(txn, pairedSettings, SETTINGS_NAMESPACE); oneOf(hook).mailboxPaired(txn, properties); }}); @@ -182,6 +183,8 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { serverSupportsInts); context.checking(new Expectations() {{ + oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE); + will(returnValue(pairedSettings)); oneOf(settingsManager).mergeSettings(txn, expectedSettings, SETTINGS_NAMESPACE); }}); @@ -196,22 +199,36 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { assertFalse(status.hasProblem(now)); } + @Test + public void testDoesNotRecordSuccessIfNotPaired() throws Exception { + Transaction txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE); + will(returnValue(new Settings())); + }}); + + manager.recordSuccessfulConnection(txn, now, serverSupports); + assertFalse(hasEvent(txn, OwnMailboxConnectionStatusEvent.class)); + } + @Test public void testRecordsFailureOnFirstAttempt() throws Exception { - testRecordsFailure(new Settings(), 0); + testRecordsFailure(pairedSettings, 0, 0); } @Test public void testRecordsFailureOnLaterAttempt() throws Exception { Settings oldSettings = new Settings(); + oldSettings.putAll(pairedSettings); oldSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, lastAttempt); oldSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, lastSuccess); oldSettings.putInt(SETTINGS_KEY_ATTEMPTS, attempts); - testRecordsFailure(oldSettings, attempts); + testRecordsFailure(oldSettings, attempts, lastSuccess); } - private void testRecordsFailure(Settings oldSettings, int oldAttempts) - throws Exception { + private void testRecordsFailure(Settings oldSettings, int oldAttempts, + long lastSuccess) throws Exception { Transaction txn = new Transaction(null, false); Settings expectedSettings = new Settings(); expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now); @@ -225,6 +242,25 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase { }}); manager.recordFailedConnectionAttempt(txn, now); + OwnMailboxConnectionStatusEvent e = + getEvent(txn, OwnMailboxConnectionStatusEvent.class); + MailboxStatus status = e.getStatus(); + assertEquals(now, status.getTimeOfLastAttempt()); + assertEquals(lastSuccess, status.getTimeOfLastSuccess()); + assertEquals(oldAttempts + 1, status.getAttemptsSinceSuccess()); + } + + @Test + public void testDoesNotRecordFailureIfNotPaired() throws Exception { + Transaction txn = new Transaction(null, false); + + context.checking(new Expectations() {{ + oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE); + will(returnValue(new Settings())); + }}); + + manager.recordFailedConnectionAttempt(txn, now); + assertFalse(hasEvent(txn, OwnMailboxConnectionStatusEvent.class)); } @Test 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 f176dc89b..8096b9220 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 @@ -38,8 +38,10 @@ import java.util.Random; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; 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.GROUP_KEY_SENT_SERVER_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; @@ -89,7 +91,10 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { private final BdfList someClientSupports; private final List newerClientSupportsList; private final BdfList newerClientSupports; + private final List someServerSupportsList; private final BdfList someServerSupports; + private final List newerServerSupportsList; + private final BdfList newerServerSupports; private final BdfList emptyServerSupports = new BdfList(); private final MailboxProperties updateProps; private final MailboxUpdateWithMailbox updateWithMailbox; @@ -110,11 +115,17 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { newerClientSupportsList.get(0).getMajor(), newerClientSupportsList.get(0).getMinor())); - List someServerSupportsList = - singletonList(new MailboxVersion(rnd.nextInt(), rnd.nextInt())); + someServerSupportsList = singletonList(new MailboxVersion( + rnd.nextInt(), rnd.nextInt())); someServerSupports = BdfList.of(BdfList.of( someServerSupportsList.get(0).getMajor(), someServerSupportsList.get(0).getMinor())); + newerServerSupportsList = singletonList(new MailboxVersion( + someServerSupportsList.get(0).getMajor(), + someServerSupportsList.get(0).getMinor() + 1)); + newerServerSupports = BdfList.of(BdfList.of( + newerServerSupportsList.get(0).getMajor(), + newerServerSupportsList.get(0).getMinor())); updateNoMailbox = new MailboxUpdate(someClientSupportsList); @@ -687,25 +698,33 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry(MSG_KEY_LOCAL, false) )); + BdfDictionary groupMetadata = new BdfDictionary(); + groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, someServerSupports); context.checking(new Expectations() {{ oneOf(db).getContacts(txn); will(returnValue(contacts)); + // Generate mailbox properties for contact oneOf(crypto).generateUniqueId(); will(returnValue(updateProps.getAuthToken())); oneOf(crypto).generateUniqueId(); will(returnValue(updateProps.getInboxId())); oneOf(crypto).generateUniqueId(); will(returnValue(updateProps.getOutboxId())); + // Find latest update oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroupId); will(returnValue(messageMetadata)); + // Replace latest update with new update expectStoreMessage(txn, contactGroupId, 2, someClientSupports, someServerSupports, propsDict); oneOf(db).removeMessage(txn, latestId); + // Store sent server-supported versions + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + groupMetadata); }}); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); @@ -741,19 +760,26 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { new BdfEntry(MSG_KEY_VERSION, 3), new BdfEntry(MSG_KEY_LOCAL, false) )); + BdfDictionary groupMetadata = new BdfDictionary(); + groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, NULL_VALUE); context.checking(new Expectations() {{ oneOf(db).getContacts(txn); will(returnValue(contacts)); + // Find latest update oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, MAJOR_VERSION, contact); will(returnValue(contactGroup)); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroupId); will(returnValue(messageMetadata)); + // Replace latest update with new update expectStoreMessage(txn, contactGroupId, 2, someClientSupports, emptyServerSupports, emptyPropsDict); oneOf(db).removeMessage(txn, latestId); + // Remove sent server-supported versions + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + groupMetadata); }}); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); @@ -768,6 +794,80 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase { assertFalse(hasEvent(txn, MailboxUpdateSentEvent.class)); } + @Test + public void testStoresLocalUpdateWhenServerSupportsChange() + throws Exception { + Transaction txn = new Transaction(null, false); + Map messageMetadata = new LinkedHashMap<>(); + MessageId latestId = new MessageId(getRandomId()); + messageMetadata.put(latestId, BdfDictionary.of( + new BdfEntry(MSG_KEY_VERSION, 1), + new BdfEntry(MSG_KEY_LOCAL, true) + )); + BdfList body = BdfList.of(1, someClientSupports, someServerSupports, + propsDict); + BdfDictionary oldGroupMetadata = new BdfDictionary(); + oldGroupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, + someServerSupports); + BdfDictionary newGroupMetadata = new BdfDictionary(); + newGroupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, + newerServerSupports); + + context.checking(new Expectations() {{ + // Load sent server-supported versions + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(oldGroupMetadata)); + oneOf(clientHelper).parseMailboxVersionList(someServerSupports); + will(returnValue(someServerSupportsList)); + // Update sent server-supported versions + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + newGroupMetadata); + oneOf(db).getContacts(txn); + will(returnValue(contacts)); + // Find latest update + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(clientHelper).getMessageMetadataAsDictionary(txn, + contactGroupId); + will(returnValue(messageMetadata)); + // Load and parse latest update + oneOf(clientHelper).getMessageAsList(txn, latestId); + will(returnValue(body)); + oneOf(clientHelper).parseAndValidateMailboxUpdate( + someClientSupports, someServerSupports, propsDict); + will(returnValue(updateWithMailbox)); + // Replace latest update with new update + expectStoreMessage(txn, contactGroupId, 2, someClientSupports, + newerServerSupports, propsDict); + oneOf(db).removeMessage(txn, latestId); + }}); + + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); + t.serverSupportedVersionsReceived(txn, newerServerSupportsList); + } + + @Test + public void testDoesNotStoreLocalUpdateWhenServerSupportsAreUnchanged() + throws Exception { + Transaction txn = new Transaction(null, false); + BdfDictionary groupMetadata = new BdfDictionary(); + groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, someServerSupports); + + context.checking(new Expectations() {{ + // Load sent server-supported versions + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(groupMetadata)); + oneOf(clientHelper).parseMailboxVersionList(someServerSupports); + will(returnValue(someServerSupportsList)); + }}); + + MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); + t.serverSupportedVersionsReceived(txn, someServerSupportsList); + } + @Test public void testGetRemoteUpdate() throws Exception { Transaction txn = new Transaction(null, false);