Mirror the remote auto-delete timer.

This commit is contained in:
akwizgran
2020-12-02 15:00:45 +00:00
committed by Torsten Grote
parent d7a2de5817
commit 27dbe23914
10 changed files with 331 additions and 40 deletions

View File

@@ -328,7 +328,8 @@ public class ConversationViewModel extends DbViewModel
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers);
} else {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, timer);
}

View File

@@ -173,7 +173,8 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
Contact contact = contactManager.getContact(txn, c);
long timestamp = conversationManager
.getTimestampForOutgoingMessage(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
contexts.add(new InvitationContext(contact, timestamp, timer));
} catch (NoSuchContactException e) {
// Continue

View File

@@ -24,8 +24,26 @@ public interface AutoDeleteManager {
*/
int MINOR_VERSION = 0;
long getAutoDeleteTimer(Transaction txn, ContactId c) throws DbException;
/**
* Returns the auto-delete timer duration for the given contact, for use in
* a message with the given timestamp. The timestamp is stored.
*/
long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException;
/**
* Sets the auto-delete timer duration for the given contact.
*/
void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException;
/**
* Receives an auto-delete timer duration from the given contact, carried
* in a message with the given timestamp. The local timer is set to the
* same duration unless it has been
* {@link #setAutoDeleteTimer(Transaction, ContactId, long) changed} more
* recently than the remote timer.
*/
void receiveAutoDeleteTimer(Transaction txn, ContactId c, long timer,
long timestamp) throws DbException;
}

View File

@@ -2,6 +2,28 @@ package org.briarproject.briar.autodelete;
interface AutoDeleteConstants {
// Group metadata key for storing the auto-delete timer
String GROUP_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer";
/**
* Group metadata key for storing the auto-delete timer duration.
*/
String GROUP_KEY_TIMER = "autoDeleteTimer";
/**
* Group metadata key for storing the timestamp of the latest incoming or
* outgoing message carrying an auto-delete timer (including a null timer).
*/
String GROUP_KEY_TIMESTAMP = "autoDeleteTimestamp";
/**
* Group metadata key for storing the previous auto-delete timer duration.
* This is used to decide whether a local change to the duration should be
* overwritten by a duration received from the contact.
*/
String GROUP_KEY_PREVIOUS_TIMER = "autoDeletePreviousTimer";
/**
* Special value for {@link #GROUP_KEY_PREVIOUS_TIMER} indicating that
* there are no local changes to the auto-delete timer duration that need
* to be compared with durations received from the contact.
*/
long NO_PREVIOUS_TIMER = 0;
}

View File

@@ -17,19 +17,29 @@ import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_PREVIOUS_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMESTAMP;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.NO_PREVIOUS_TIMER;
@Immutable
@NotNullByDefault
class AutoDeleteManagerImpl
implements AutoDeleteManager, OpenDatabaseHook, ContactHook {
private static final Logger LOG =
getLogger(AutoDeleteManagerImpl.class.getName());
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final GroupFactory groupFactory;
@@ -69,14 +79,22 @@ class AutoDeleteManagerImpl
}
@Override
public long getAutoDeleteTimer(Transaction txn, ContactId c)
public long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException {
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
return meta.getLong(GROUP_KEY_AUTO_DELETE_TIMER,
NO_AUTO_DELETE_TIMER);
long timer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
if (LOG.isLoggable(INFO)) {
LOG.info("Sending message with auto-delete timer " + timer);
}
// Update the timestamp and clear the previous timer, if any
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, timestamp),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
return timer;
} catch (FormatException e) {
throw new DbException(e);
}
@@ -85,18 +103,60 @@ class AutoDeleteManagerImpl
@Override
public void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException {
if (timer != NO_AUTO_DELETE_TIMER &&
(timer < MIN_AUTO_DELETE_TIMER_MS ||
timer > MAX_AUTO_DELETE_TIMER_MS)) {
throw new IllegalArgumentException();
}
validateTimer(timer);
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
if (timer == oldTimer) return;
if (LOG.isLoggable(INFO)) {
LOG.info("Setting auto-delete timer to " + timer);
}
// Store the new timer and the previous timer
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, timer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldTimer));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
throw new DbException(e);
}
}
@Override
public void receiveAutoDeleteTimer(Transaction txn, ContactId c,
long timer, long timestamp) throws DbException {
validateTimer(timer);
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimestamp = meta.getLong(GROUP_KEY_TIMESTAMP, 0L);
if (timestamp <= oldTimestamp) return;
long oldTimer =
meta.getLong(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
meta = new BdfDictionary();
if (oldTimer == NO_PREVIOUS_TIMER) {
// We don't have an unsent change. Mirror their timer
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer);
}
meta.put(GROUP_KEY_TIMER, timer);
} else if (timer != oldTimer) {
// Their sent change trumps our unsent change. Mirror their
// timer and clear the previous timer to drop our unsent change
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer
+ " and forgetting unsent change");
}
meta.put(GROUP_KEY_TIMER, timer);
meta.put(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
}
// Always update the timestamp
meta.put(GROUP_KEY_TIMESTAMP, timestamp);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new DbException(e);
}
}
@@ -104,4 +164,12 @@ class AutoDeleteManagerImpl
byte[] descriptor = c.getAuthor().getId().getBytes();
return groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION, descriptor);
}
private void validateTimer(long timer) {
if (timer != NO_AUTO_DELETE_TIMER &&
(timer < MIN_AUTO_DELETE_TIMER_MS ||
timer > MAX_AUTO_DELETE_TIMER_MS)) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -100,7 +100,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m;
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeRequestMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), author, text, timer);
sendMessage(txn, REQUEST, s.getSessionId(), m, true, timer);
@@ -120,7 +121,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m;
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(),
ephemeralPublicKey, acceptTimestamp, transportProperties,
@@ -141,7 +143,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m;
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(),
timer);

View File

@@ -152,9 +152,11 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation
long timer = visibleInUi
? autoDeleteManager.getAutoDeleteTimer(txn, c)
: NO_AUTO_DELETE_TIMER;
long timer = NO_AUTO_DELETE_TIMER;
if (visibleInUi) {
timer = autoDeleteManager
.getAutoDeleteTimer(txn, c, localTimestamp);
}
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer);
@@ -179,9 +181,11 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation
long timer = visibleInUi
? autoDeleteManager.getAutoDeleteTimer(txn, c)
: NO_AUTO_DELETE_TIMER;
long timer = NO_AUTO_DELETE_TIMER;
if (visibleInUi) {
timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
}
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer);

View File

@@ -142,7 +142,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
localTimestamp, s.getLastLocalMessageId(), descriptor,
text, timer);
@@ -209,7 +210,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer);
@@ -263,7 +265,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer);

View File

@@ -18,12 +18,18 @@ import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KE
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.autodelete.AutoDeleteManager.CLIENT_ID;
import static org.briarproject.briar.api.autodelete.AutoDeleteManager.MAJOR_VERSION;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_AUTO_DELETE_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_PREVIOUS_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMESTAMP;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.NO_PREVIOUS_TIMER;
import static org.junit.Assert.assertEquals;
// Thank you, I'm using them for readability
@SuppressWarnings("UnnecessaryLocalVariable")
public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
@@ -35,6 +41,7 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = getContact();
private final long now = System.currentTimeMillis();
private final AutoDeleteManagerImpl autoDeleteManager;
@@ -113,15 +120,44 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
@Test
public void testStoresTimer() throws Exception {
Transaction txn = new Transaction(null, false);
long timer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer));
long oldTimer = MIN_AUTO_DELETE_TIMER_MS;
long newTimer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, oldTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, newTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldTimer));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), meta);
contactGroup.getId(), newMeta);
}});
autoDeleteManager.setAutoDeleteTimer(txn, contact.getId(), newTimer);
}
@Test
public void testDoesNotStoreTimerIfUnchanged() throws Exception {
Transaction txn = new Transaction(null, false);
long timer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, timer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(meta));
}});
autoDeleteManager.setAutoDeleteTimer(txn, contact.getId(), timer);
@@ -133,7 +169,10 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
long timer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()),
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer));
new BdfEntry(GROUP_KEY_TIMER, timer));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, now),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn);
expectGetContactGroup();
@@ -141,10 +180,12 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(meta));
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
newMeta);
}});
assertEquals(timer,
autoDeleteManager.getAutoDeleteTimer(txn, contact.getId()));
assertEquals(timer, autoDeleteManager
.getAutoDeleteTimer(txn, contact.getId(), now));
}
@Test
@@ -152,6 +193,44 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, false);
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, now),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(meta));
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
newMeta);
}});
assertEquals(NO_AUTO_DELETE_TIMER, autoDeleteManager
.getAutoDeleteTimer(txn, contact.getId(), now));
}
@Test
public void testIgnoresReceivedTimerWithEarlierTimestamp()
throws Exception {
testIgnoresReceivedTimerWithTimestamp(now - 1);
}
@Test
public void testIgnoresReceivedTimerWithEqualTimestamp() throws Exception {
testIgnoresReceivedTimerWithTimestamp(now);
}
private void testIgnoresReceivedTimerWithTimestamp(long remoteTimestamp)
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
expectGetContact(txn);
expectGetContactGroup();
@@ -161,8 +240,99 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
will(returnValue(meta));
}});
assertEquals(NO_AUTO_DELETE_TIMER,
autoDeleteManager.getAutoDeleteTimer(txn, contact.getId()));
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
remoteTimer, remoteTimestamp);
}
@Test
public void testMirrorsRemoteTimestampIfNoUnsentChange() throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated and the timer should be mirrored
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp),
new BdfEntry(GROUP_KEY_TIMER, remoteTimer));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
remoteTimer, remoteTimestamp);
}
@Test
public void testDoesNotMirrorUnchangedRemoteTimestampIfUnsentChange()
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, remoteTimer),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated but the timer should not revert
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
remoteTimer, remoteTimestamp);
}
@Test
public void testMirrorsChangedRemoteTimestampIfUnsentChange()
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long oldRemoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long newRemoteTimer = MAX_AUTO_DELETE_TIMER_MS - 1;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldRemoteTimer),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated , the timer should be mirrored and
// the previous timer should be cleared
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp),
new BdfEntry(GROUP_KEY_TIMER, newRemoteTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
newRemoteTimer, remoteTimestamp);
}
private void expectGetContact(Transaction txn) throws Exception {

View File

@@ -256,7 +256,8 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
void expectGetAutoDeleteTimer() throws Exception {
context.checking(new Expectations() {{
oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId);
oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId,
localTimestamp);
will(returnValue(NO_AUTO_DELETE_TIMER));
}});
}