Implement auto-declining for self-destructed introductions

This commit is contained in:
Sebastian Kürten
2021-03-03 09:17:35 +01:00
committed by Torsten Grote
parent 49850e4198
commit 0bf10a827f
23 changed files with 1170 additions and 42 deletions

View File

@@ -102,7 +102,7 @@ public abstract class AbstractAutoDeleteTest extends
@FunctionalInterface
protected interface HeaderConsumer {
void accept(ConversationMessageHeader header);
void accept(ConversationMessageHeader header) throws DbException;
}
protected void forEachHeader(BriarIntegrationTestComponent component,

View File

@@ -0,0 +1,968 @@
package org.briarproject.briar.introduction;
import org.briarproject.bramble.api.FormatException;
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.db.DbException;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.system.TimeTravelModule;
import org.briarproject.bramble.test.TestDatabaseConfigModule;
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.briar.autodelete.AbstractAutoDeleteTest;
import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.junit.Before;
import org.junit.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.briarproject.bramble.api.cleanup.CleanupManager.BATCH_DELAY_MS;
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
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.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSES;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSE_A;
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSE_B;
import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.B_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.START;
import static org.briarproject.briar.test.TestEventListener.assertEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class AutoDeleteIntegrationTest extends AbstractAutoDeleteTest {
@Override
protected void createComponents() {
IntroductionIntegrationTestComponent component =
DaggerIntroductionIntegrationTestComponent.builder().build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(component);
component.inject(this);
IntroductionIntegrationTestComponent c0 =
DaggerIntroductionIntegrationTestComponent.builder()
.testDatabaseConfigModule(
new TestDatabaseConfigModule(t0Dir))
.timeTravelModule(new TimeTravelModule(true))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c0);
IntroductionIntegrationTestComponent c1 =
DaggerIntroductionIntegrationTestComponent.builder()
.testDatabaseConfigModule(
new TestDatabaseConfigModule(t1Dir))
.timeTravelModule(new TimeTravelModule(true))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c1);
IntroductionIntegrationTestComponent c2 =
DaggerIntroductionIntegrationTestComponent.builder()
.testDatabaseConfigModule(
new TestDatabaseConfigModule(t2Dir))
.timeTravelModule(new TimeTravelModule(true))
.build();
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c2);
this.c0 = c0;
this.c1 = c1;
this.c2 = c2;
// Use different times to avoid creating identical messages that are
// treated as redundant copies of the same message (#1907)
try {
c0.getTimeTravel().setCurrentTimeMillis(startTime);
c1.getTimeTravel().setCurrentTimeMillis(startTime + 1);
c2.getTimeTravel().setCurrentTimeMillis(startTime + 2);
} catch (InterruptedException e) {
fail();
}
}
@Before
@Override
public void setUp() throws Exception {
super.setUp();
addTransportProperties();
}
@Override
protected ConversationClient getConversationClient(
BriarIntegrationTestComponent component) {
return component.getIntroductionManager();
}
/*
* Basic tests.
* ASSERT timers are set on introduction messages
* ASSERT introduction messages self-destruct on all three sides
*/
@Test
public void testIntroductionMessagesHaveTimer() throws Exception {
makeIntroduction(true, true);
assertIntroductionsArrived();
assertMessagesAmong0And1HaveTimerSet(1, 1);
assertMessagesAmong0And2HaveTimerSet(1, 1);
}
@Test
public void testIntroductionAutoDeleteIntroducer() throws Exception {
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
makeIntroduction(true, true);
assertIntroductionsArrived();
assertMessagesAmong0And1HaveTimerSet(1, 1);
assertMessagesAmong0And2HaveTimerSet(1, 1);
// Sync the ack to 0 from 1 - this starts 0's timer
ack1To0(1);
waitForEvents(c0);
// Before 0's timer elapses, the introducer should still see the
// introduction sent to 1
timeTravel(c0, timerLatency - 1);
assertGroupCountAt0With1(1, 0);
assertGroupCountAt0With2(1, 0);
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
// After 0's timer elapses, the introducer should have deleted the
// introduction sent to 1
timeTravel(c0, 1);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(1, 0);
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
// Sync the ack to 0 from 2 - this starts 0's timer
ack2To0(1);
waitForEvents(c0);
// Before 0's timer elapses, the introducer should still see the
// introduction sent to 2
timeTravel(c0, timerLatency - 1);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(1, 0);
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
// After 0's timer elapses, the introducer should have deleted the
// introduction sent to 2
timeTravel(c0, 1);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(0, 0);
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
}
@Test
public void testIntroductionAutoDeleteIntroducee() throws Exception {
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
makeIntroduction(true, false);
markMessagesRead(c1, contact0From1);
assertGroupCountAt1With0(1, 0);
assertGroupCountAt2With0(1, 1);
// Travel in time at c1 unit 1ms before the deadline expires
timeTravel(c1, timerLatency - 1);
assertGroupCountAt0With1(1, 0);
assertGroupCountAt0With2(1, 0);
assertGroupCountAt2With0(1, 1);
// There is currently 1 message at 1 with 0, the introduction request
assertGroupCountAt1With0(1, 0);
forEachHeader(c1, contactId0From1, 1, h ->
assertTrue(h instanceof IntroductionRequest)
);
// After travelling in time one more 1ms, the introduction should be
// auto-declined. We should get an event signalling that the response
// has been sent.
IntroductionResponseReceivedEvent e = assertEvent(c1,
IntroductionResponseReceivedEvent.class, () ->
timeTravel(c1, 1)
);
// the event should have correct values
assertEquals(contactId0From1, e.getContactId());
IntroductionResponse response = e.getMessageHeader();
assertEquals(author2.getName(),
response.getIntroducedAuthor().getName());
assertTrue(response.isAutoDecline());
// these should not have changed
assertGroupCountAt0With1(1, 0);
assertGroupCountAt0With2(1, 0);
assertGroupCountAt2With0(1, 1);
// there is still 1 message at 1 with 0, but it should now be the new
// auto-decline message instead of the introduction
assertGroupCountAt1With0(1, 0);
forEachHeader(c1, contactId0From1, 1, h -> {
assertTrue(h instanceof IntroductionResponse);
IntroductionResponse r = (IntroductionResponse) h;
assertEquals(author2.getName(), r.getIntroducedAuthor().getName());
assertTrue(r.isAutoDecline());
});
// sync auto-decline to 0
sync1To0(1, true);
waitForEvents(c0);
// auto-decline arrived at 0
assertGroupCountAt0With1(2, 1);
// forward auto-decline to 2
sync0To2(1, true);
waitForEvents(c2);
// auto-decline arrived at 2
assertGroupCountAt2With0(2, 2);
}
/**
* Let one introducee accept, the other decline manually
* ASSERT accept and decline messages arrive and have timer on all sides
* ASSERT accept and decline get forwarded to the other introducee and that
* they all have timers set on all sides
* ASSERT that all messages self-destruct
*/
@Test
public void testIntroductionSessionManualDecline() throws Exception {
makeIntroduction(true, true);
assertIntroducerStatus(AWAIT_RESPONSES);
// mark messages as read on 1 and 2, this starts 1's and 2's timer for 0
markMessagesRead(c1, contact0From1);
markMessagesRead(c2, contact0From2);
assertGroupCountAt1With0(1, 0);
assertGroupCountAt2With0(1, 0);
respondToMostRecentIntroduction(c1, contactId0From1, true);
respondToMostRecentIntroduction(c2, contactId0From2, false);
sync1To0(1, true);
sync2To0(1, true);
waitForEvents(c0);
// added the own responses
assertGroupCountAt1With0(2, 0);
assertGroupCountAt2With0(2, 0);
// 0 has the sent introduction and the unread responses
assertGroupCountAt0With1(2, 1);
assertGroupCountAt0With2(2, 1);
assertIntroducerStatus(START);
markMessagesRead(c0, contact1From0);
markMessagesRead(c0, contact2From0);
assertGroupCountAt0With1(2, 0);
assertGroupCountAt0With2(2, 0);
// forward responses from 0 to introducees
sync0To1(1, true);
sync0To2(1, true);
waitForEvents(c1);
waitForEvents(c2);
// first contact receives the forwarded decline
assertGroupCountAt1With0(3, 1);
// second contact does not display an extra message because 2 declined
// themselves
assertGroupCountAt2With0(2, 0);
markMessagesRead(c1, contact0From1);
assertGroupCountAt1With0(3, 0);
assertMessagesAmong0And1HaveTimerSet(2, 3);
assertMessagesAmong0And2HaveTimerSet(2, 2);
// Travel in time on all devices
timeTravel(c0);
timeTravel(c1);
timeTravel(c2);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(0, 0);
assertGroupCountAt1With0(0, 0);
assertGroupCountAt2With0(0, 0);
}
/**
* Let introductions self-destruct at the introducer and one of the
* introducees
* ASSERT that auto-declines get sent and arrive
* ASSERT that auto-declines have the timer set
* ASSERT that declines get forwarded to other introducee
* ASSERT that forwarded declines have the timer set
* ASSERT that forwarded declines self-destruct
* ASSERT that a that a new introduction can succeed afterwards
*/
@Test
public void testIntroductionSessionAutoDecline() throws Exception {
makeIntroduction(true, false);
assertIntroducerStatus(AWAIT_RESPONSES);
// ack from 1 and 2 to 0. This starts 0's timer for 1
ack1To0(1);
ack2To0(1);
waitForEvents(c0);
// mark messages as read on 1 and 2, this starts 1's timer for 0
markMessagesRead(c1, contact0From1);
markMessagesRead(c2, contact0From2);
assertGroupCountAt1With0(1, 0);
assertGroupCountAt2With0(1, 0);
// Travel in time on all devices
timeTravel(c0);
timeTravel(c1);
timeTravel(c2);
// assert that introductions have been deleted between 0 and 1 but not
// between 0 and 2
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(1, 0);
// there is now 1 message, the auto-decline message
assertGroupCountAt1With0(1, 0);
assertGroupCountAt2With0(1, 0);
// sync the auto-decline message from 1 to 0
sync1To0(1, true);
waitForEvents(c0);
// the auto-decline from 1 has arrived at 0
assertGroupCountAt0With1(1, 1);
// the session status has moved to A_DECLINED or B_DECLINED
assertIntroducerStatusFirstDeclined();
// sync the auto-decline message from 0 to 2
sync0To2(1, true);
waitForEvents(c2);
// the auto-decline from 1 has arrived at 2
assertGroupCountAt2With0(2, 1);
// 0 and 1 still have the auto-decline message from 0
assertGroupCountAt0With1(1, 1);
assertGroupCountAt1With0(1, 0);
// make sure the auto-decline has the timer set at 0 and 1
assertMessagesAmong0And1HaveTimerSet(1, 1);
// ack message from 0 to 1 and make sure it self-destructs on 1
ack0To1(1);
timeTravel(c1);
assertGroupCountAt1With0(0, 0);
// mark message read on 0 and make sure it self-destructs on 0
markMessagesRead(c0, contact1From0);
timeTravel(c0);
assertGroupCountAt0With1(0, 0);
// assert that a that a new introduction can succeed afterwards:
// first decline from c2, assert we're in START state and then
// make the new introdution
respondToMostRecentIntroduction(c2, contactId0From2, false);
sync2To0(1, true);
waitForEvents(c0);
sync0To1(1, true);
waitForEvents(c1);
assertIntroducerStatus(START);
assertNewIntroductionSucceeds();
}
@Test
public void testIntroductionAcceptHasTimer() throws Exception {
testIntroductionResponseHasTimer(true);
}
@Test
public void testIntroductionDeclineHasTimer() throws Exception {
testIntroductionResponseHasTimer(false);
}
private void testIntroductionResponseHasTimer(boolean accept)
throws Exception {
makeIntroduction(true, false);
assertIntroductionsArrived();
// check that all messages have the timer set
assertMessagesAmong0And1HaveTimerSet(1, 1);
assertMessagesAmong0And2HaveTimerNotSet(1, 1);
respondToMostRecentIntroduction(c1, contactId0From1, accept);
sync1To0(1, true);
waitForEvents(c0);
// check that response has arrived
assertGroupCountAt0With1(2, 1);
assertGroupCountAt1With0(2, 1);
assertMessagesAmong0And1HaveTimerSet(2, 2);
}
@Test
public void testIntroductionAcceptSelfDestructs() throws Exception {
testIntroductionResponseSelfDestructs(true);
}
@Test
public void testIntroductionDeclineSelfDestructs() throws Exception {
testIntroductionResponseSelfDestructs(false);
}
private void testIntroductionResponseSelfDestructs(boolean accept)
throws Exception {
makeIntroduction(true, false);
assertIntroductionsArrived();
assertMessagesAmong0And1HaveTimerSet(1, 1);
assertMessagesAmong0And2HaveTimerNotSet(1, 1);
respondToMostRecentIntroduction(c1, contactId0From1, accept);
sync1To0(1, true);
waitForEvents(c0);
// Sync the ack to 1 - this starts 1's timer
ack0To1(1);
waitForEvents(c1);
// check that response has arrived
assertGroupCountAt0With1(2, 1);
assertGroupCountAt1With0(2, 1);
assertMessagesAmong0And1HaveTimerSet(2, 2);
// mark messages as read on 1, this starts 1's timer for 0
markMessagesRead(c1, contact0From1);
assertGroupCountAt1With0(2, 0);
// mark messages as read on 0, this starts 0's timer for 1
markMessagesRead(c0, contact1From0);
assertGroupCountAt0With1(2, 0);
// Travel in time on all devices
timeTravel(c0);
timeTravel(c1);
timeTravel(c2);
// assert that introductions and responses have been deleted between
// 0 and 1
assertGroupCountAt0With1(0, 0);
assertGroupCountAt1With0(0, 0);
}
/*
* Tests that checks whether an introduction can still succeed properly
* after the introduction self-destructed at the introducer.
*/
/**
* Let introductions self-destruct at the introducer only
* Let both introducees accept the introduction
* ASSERT that accept messages still get forwarded to the other introducer
* ASSERT that the introduction succeeds
* ASSERT all messages involved self-destruct eventually
*/
@Test
public void testSucceedAfterIntroducerSelfDestructed() throws Exception {
testSucceedAfterIntroducerSelfDestructed(false);
}
/**
* Variant of the above test that also auto-deletes the responses at each
* introducee received from the respective other introducee.
*/
@Test
public void testSucceedAfterIntroducerAndResponsesSelfDestructed()
throws Exception {
testSucceedAfterIntroducerSelfDestructed(true);
}
private void testSucceedAfterIntroducerSelfDestructed(
boolean autoDeleteResponsesBeforeSyncingAuthAndActivate)
throws Exception {
makeIntroduction(true, true);
// ack from 1 and 2 to 0. This starts 0's timer for 1 and 2
ack1To0(1);
ack2To0(1);
waitForEvents(c0);
// Travel in time at 0
timeTravel(c0);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(0, 0);
// -> introductions self-destructed at the introducer only
// introducees have got the introduction
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
respondToMostRecentIntroduction(c1, contactId0From1, true);
respondToMostRecentIntroduction(c2, contactId0From2, true);
sync1To0(1, true);
sync2To0(1, true);
waitForEvents(c0);
// introducees have got the own response
assertGroupCountAt1With0(2, 1);
assertGroupCountAt2With0(2, 1);
// sync forwarded ACCEPT messages to introducees
sync0To1(1, true);
sync0To2(1, true);
waitForEvents(c1);
waitForEvents(c2);
if (autoDeleteResponsesBeforeSyncingAuthAndActivate) {
assertGroupCountAt1With0(2, 1);
assertGroupCountAt2With0(2, 1);
markMessagesRead(c1, contact0From1);
markMessagesRead(c2, contact0From2);
assertGroupCountAt1With0(2, 0);
assertGroupCountAt2With0(2, 0);
// Travel in time at 1 and 2
timeTravel(c1);
timeTravel(c2);
assertGroupCountAt1With0(0, 0);
assertGroupCountAt2With0(0, 0);
}
syncAuthAndActivateMessages();
assertIntroductionSucceeded();
}
/*
* Group of three tests that check whether an introduction can still fail
* properly after the introduction self-destructed at the introducer.
* <p>
* Let introductions self-destruct at the introducer only
* Variant 1: Let both introducees decline the introduction
* Variant 2: Let first introducee accept, second decline the introduction
* Variant 3: Let first introducee decline, second accept the introduction
* ASSERT that accept/decline messages still get forwarded to the other introducer
* ASSERT that the introduction does not succeed
* ASSERT that abort messages do get sent
* ASSERT all messages involved self-destruct eventually
* ASSERT that a new introduction can succeed afterwards
*/
@Test
public void testFailAfterIntroducerSelfDestructedBothDecline()
throws Exception {
testFailAfterIntroducerSelfDestructed(false, false);
}
@Test
public void testFailAfterIntroducerSelfDestructedFirstAccept()
throws Exception {
testFailAfterIntroducerSelfDestructed(true, false);
}
@Test
public void testFailAfterIntroducerSelfDestructedSecondAccept()
throws Exception {
testFailAfterIntroducerSelfDestructed(false, true);
}
private void testFailAfterIntroducerSelfDestructed(boolean firstAccepts,
boolean secondAccepts) throws Exception {
assertFalse(firstAccepts && secondAccepts);
makeIntroduction(true, true);
// ack from 1 and 2 to 0. This starts 0's timer for 1 and 2
ack1To0(1);
ack2To0(1);
waitForEvents(c0);
// Travel in time at 0
timeTravel(c0);
assertGroupCountAt0With1(0, 0);
assertGroupCountAt0With2(0, 0);
// -> introductions self-destructed at the introducer only
assertIntroducerStatus(AWAIT_RESPONSES);
assertIntroduceeStatus(c1, IntroduceeState.AWAIT_RESPONSES);
assertIntroduceeStatus(c2, IntroduceeState.AWAIT_RESPONSES);
// introducees have got the introduction
assertGroupCountAt1With0(1, 1);
assertGroupCountAt2With0(1, 1);
// first contact reads the introduction and responds
markMessagesRead(c1, contact0From1);
respondToMostRecentIntroduction(c1, contactId0From1, firstAccepts);
sync1To0(1, true);
waitForEvents(c0);
if (firstAccepts) {
assertIntroducerStatusFirstAccepted();
} else {
assertIntroducerStatusFirstDeclined();
}
// second contact reads the introduction and responds
markMessagesRead(c2, contact0From2);
respondToMostRecentIntroduction(c2, contactId0From2, secondAccepts);
sync2To0(1, true);
waitForEvents(c0);
assertIntroducerStatus(START);
// introducees have got the own response
assertGroupCountAt1With0(2, 0);
assertGroupCountAt2With0(2, 0);
// sync forwarded ACCEPT/DECLINE messages to introducees
sync0To1(1, true);
waitForEvents(c1);
sync0To2(1, true);
waitForEvents(c2);
assertIntroductionFailed();
if (firstAccepts) {
// one additional message, the other introducee's response
assertGroupCountAt1With0(3, 1);
} else {
assertGroupCountAt1With0(2, 0);
}
if (secondAccepts) {
// one additional message, the other introducee's response
assertGroupCountAt2With0(3, 1);
} else {
assertGroupCountAt2With0(2, 0);
}
timeTravel(c1);
timeTravel(c2);
if (firstAccepts) {
assertGroupCountAt1With0(1, 1);
} else {
assertGroupCountAt1With0(0, 0);
}
if (secondAccepts) {
assertGroupCountAt2With0(1, 1);
} else {
assertGroupCountAt2With0(0, 0);
}
// -> if one of the introducees accepted, they still have got an unread
// decline from the other introducee
if (firstAccepts) {
markMessagesRead(c1, contact0From1);
timeTravel(c1);
assertGroupCountAt1With0(0, 0);
}
if (secondAccepts) {
markMessagesRead(c2, contact0From2);
timeTravel(c2);
assertGroupCountAt2With0(0, 0);
}
// make sure the introducees session status returned to START
assertIntroduceeStatus(c1, IntroduceeState.START);
assertIntroduceeStatus(c2, IntroduceeState.START);
assertNewIntroductionSucceeds();
}
private void makeIntroduction(boolean enableTimer1, boolean enableTimer2)
throws Exception {
if (enableTimer1) {
setAutoDeleteTimer(c0, contact1From0.getId(),
MIN_AUTO_DELETE_TIMER_MS);
}
if (enableTimer2) {
setAutoDeleteTimer(c0, contact2From0.getId(),
MIN_AUTO_DELETE_TIMER_MS);
}
// make introduction
c0.getIntroductionManager()
.makeIntroduction(contact1From0, contact2From0, "Hi!");
sync0To1(1, true);
sync0To2(1, true);
waitForEvents(c1);
waitForEvents(c2);
}
private void respondToMostRecentIntroduction(
BriarIntegrationTestComponent c, ContactId contactId,
boolean accept) throws Exception {
List<ConversationMessageHeader> headers =
getMessageHeaders(c, contactId);
Collections.reverse(headers);
for (ConversationMessageHeader h : headers) {
if (h instanceof IntroductionRequest) {
IntroductionRequest ir = (IntroductionRequest) h;
c.getIntroductionManager().respondToIntroduction(contactId,
ir.getSessionId(), accept);
return;
}
}
fail("no introduction found");
}
private void markMessagesRead(BriarIntegrationTestComponent c,
Contact contact) throws Exception {
for (ConversationMessageHeader h : getMessageHeaders(c,
contact.getId())) {
markMessageRead(c, contact, h.getId());
}
}
private void syncAuthAndActivateMessages() throws Exception {
// sync first AUTH and its forward
sync1To0(1, true);
sync0To2(1, true);
// sync second AUTH and its forward as well as the following ACTIVATE
sync2To0(2, true);
sync0To1(2, true);
// sync second ACTIVATE and its forward
sync1To0(1, true);
sync0To2(1, true);
}
private void timeTravel(BriarIntegrationTestComponent c) throws Exception {
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
timeTravel(c, timerLatency);
}
private void timeTravel(BriarIntegrationTestComponent c, long timerLatency)
throws Exception {
c.getTimeTravel().addCurrentTimeMillis(timerLatency);
waitForEvents(c);
}
private void assertIntroductionsArrived() throws DbException {
// check that introductions have arrived at the introducees
assertGroupCount(c0, contactId1From0, 1, 0);
assertGroupCount(c0, contactId2From0, 1, 0);
assertGroupCount(c1, contactId0From1, 1, 1);
assertGroupCount(c2, contactId0From2, 1, 1);
}
private void assertGroupCountAt0With1(int messageCount, int unreadCount)
throws Exception {
assertGroupCount(c0, contactId1From0, messageCount, unreadCount);
assertEquals(messageCount,
getMessageHeaders(c0, contactId1From0).size());
}
private void assertGroupCountAt0With2(int messageCount, int unreadCount)
throws Exception {
assertGroupCount(c0, contactId2From0, messageCount, unreadCount);
assertEquals(messageCount,
getMessageHeaders(c0, contactId2From0).size());
}
private void assertGroupCountAt1With0(int messageCount, int unreadCount)
throws Exception {
assertGroupCount(c1, contactId0From1, messageCount, unreadCount);
assertEquals(messageCount,
getMessageHeaders(c1, contactId0From1).size());
}
private void assertGroupCountAt2With0(int messageCount, int unreadCount)
throws Exception {
assertGroupCount(c2, contactId0From2, messageCount, unreadCount);
assertEquals(messageCount,
getMessageHeaders(c2, contactId0From2).size());
}
private void assertMessagesAmong0And1HaveTimerSet(int numC0, int numC1)
throws Exception {
forEachHeader(c0, contactId1From0, numC0, h ->
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer()));
forEachHeader(c1, contactId0From1, numC1, h ->
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer()));
}
private void assertMessagesAmong0And2HaveTimerSet(int numC0, int numC2)
throws Exception {
forEachHeader(c0, contactId2From0, numC0, h ->
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer()));
forEachHeader(c2, contactId0From2, numC2, h ->
assertEquals(MIN_AUTO_DELETE_TIMER_MS, h.getAutoDeleteTimer()));
}
private void assertMessagesAmong0And2HaveTimerNotSet(int numC0, int numC2)
throws Exception {
forEachHeader(c0, contactId2From0, numC0, h ->
assertEquals(NO_AUTO_DELETE_TIMER, h.getAutoDeleteTimer()));
forEachHeader(c2, contactId0From2, numC2, h ->
assertEquals(NO_AUTO_DELETE_TIMER, h.getAutoDeleteTimer()));
}
private void assertIntroducerStatus(IntroducerState state)
throws DbException, FormatException {
IntroducerSession introducerSession = getIntroducerSession();
assertEquals(state, introducerSession.getState());
}
private void assertIntroducerStatusFirstDeclined()
throws DbException, FormatException {
IntroductionCrypto introductionCrypto =
((IntroductionIntegrationTestComponent) c0)
.getIntroductionCrypto();
boolean alice =
introductionCrypto.isAlice(contact1From0.getAuthor().getId(),
contact2From0.getAuthor().getId());
IntroducerSession introducerSession = getIntroducerSession();
assertEquals(alice ? A_DECLINED : B_DECLINED,
introducerSession.getState());
}
private void assertIntroducerStatusFirstAccepted()
throws DbException, FormatException {
IntroductionCrypto introductionCrypto =
((IntroductionIntegrationTestComponent) c0)
.getIntroductionCrypto();
boolean alice =
introductionCrypto.isAlice(contact1From0.getAuthor().getId(),
contact2From0.getAuthor().getId());
IntroducerSession introducerSession = getIntroducerSession();
assertEquals(alice ? AWAIT_RESPONSE_B : AWAIT_RESPONSE_A,
introducerSession.getState());
}
private void assertIntroduceeStatus(BriarIntegrationTestComponent c,
IntroduceeState state)
throws DbException, FormatException {
IntroduceeSession introduceeSession = getIntroduceeSession(c);
assertEquals(state, introduceeSession.getState());
}
private void assertIntroductionSucceeded() throws DbException {
// make sure that introduced contacts have each other in their contact
// manager
assertTrue(contactManager1
.contactExists(author2.getId(), author1.getId()));
assertTrue(contactManager2
.contactExists(author1.getId(), author2.getId()));
// make sure that introduced contacts are not verified
for (Contact c : contactManager1.getContacts()) {
if (c.getAuthor().equals(author2)) {
assertFalse(c.isVerified());
}
}
for (Contact c : contactManager2.getContacts()) {
if (c.getAuthor().equals(author1)) {
assertFalse(c.isVerified());
}
}
}
private void assertIntroductionFailed() throws DbException {
// make sure that introduced contacts do not have each other in their
// contact manager
assertFalse(contactManager1
.contactExists(author2.getId(), author1.getId()));
assertFalse(contactManager2
.contactExists(author1.getId(), author2.getId()));
}
private void assertNewIntroductionSucceeds() throws Exception {
makeIntroduction(false, false);
respondToMostRecentIntroduction(c1, contactId0From1, true);
respondToMostRecentIntroduction(c2, contactId0From2, true);
sync1To0(1, true);
sync2To0(1, true);
waitForEvents(c0);
// forward responses from 0 to introducees
sync0To1(1, true);
sync0To2(1, true);
waitForEvents(c1);
waitForEvents(c2);
syncAuthAndActivateMessages();
assertIntroductionSucceeded();
}
private void addTransportProperties() throws Exception {
TransportPropertyManager tpm0 = c0.getTransportPropertyManager();
TransportPropertyManager tpm1 = c1.getTransportPropertyManager();
TransportPropertyManager tpm2 = c2.getTransportPropertyManager();
tpm0.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
getTransportProperties(2));
sync0To1(1, true);
sync0To2(1, true);
tpm1.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
getTransportProperties(2));
sync1To0(1, true);
tpm2.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
getTransportProperties(2));
sync2To0(1, true);
}
private IntroducerSession getIntroducerSession()
throws DbException, FormatException {
Map<MessageId, BdfDictionary> dicts = c0.getClientHelper()
.getMessageMetadataAsDictionary(getLocalGroup().getId());
assertEquals(1, dicts.size());
BdfDictionary d = dicts.values().iterator().next();
SessionParser sessionParser =
((IntroductionIntegrationTestComponent) c0).getSessionParser();
return sessionParser.parseIntroducerSession(d);
}
private IntroduceeSession getIntroduceeSession(
BriarIntegrationTestComponent c)
throws DbException, FormatException {
Map<MessageId, BdfDictionary> dicts = c.getClientHelper()
.getMessageMetadataAsDictionary(getLocalGroup().getId());
assertEquals(1, dicts.size());
BdfDictionary d = dicts.values().iterator().next();
Group introducerGroup =
c2.getIntroductionManager().getContactGroup(contact0From2);
SessionParser sessionParser =
((IntroductionIntegrationTestComponent) c).getSessionParser();
return sessionParser
.parseIntroduceeSession(introducerGroup.getId(), d);
}
private Group getLocalGroup() {
return contactGroupFactory
.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
}
}

View File

@@ -381,9 +381,6 @@ public class IntroductionIntegrationTest
listener2.getResponse().getIntroducedAuthor().getName());
assertFalse(listener2.getResponse().canSucceed());
// note how the introducer does not forward the second response,
// because after the first decline the protocol finished
assertFalse(listener1.succeeded);
assertFalse(listener2.succeeded);

View File

@@ -49,6 +49,8 @@ interface IntroductionIntegrationTestComponent
void inject(IntroductionCryptoIntegrationTest init);
void inject(AutoDeleteIntegrationTest init);
MessageEncoder getMessageEncoder();
MessageParser getMessageParser();

View File

@@ -640,8 +640,7 @@ public class IntroductionValidatorTest extends ValidatorTestCase {
private void expectEncodeMetadata(MessageType type, long autoDeleteTimer) {
context.checking(new Expectations() {{
oneOf(messageEncoder).encodeMetadata(type, sessionId,
message.getTimestamp(), false, false, false,
autoDeleteTimer);
message.getTimestamp(), autoDeleteTimer);
will(returnValue(meta));
}});
}

View File

@@ -107,7 +107,7 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
@Test
public void testMessageMetadata() throws FormatException {
BdfDictionary d = messageEncoder.encodeMetadata(ABORT, sessionId,
timestamp, false, true, false, MAX_AUTO_DELETE_TIMER_MS);
timestamp, false, true, false, MAX_AUTO_DELETE_TIMER_MS, false);
MessageMetadata meta = messageParser.parseMetadata(d);
assertEquals(ABORT, meta.getMessageType());