Commit 64ae99bb authored by akwizgran's avatar akwizgran

Handle corner cases such as removal during rendezvous.

parent ed1cefa1
Pipeline #3474 passed with stage
in 8 minutes and 4 seconds
...@@ -102,6 +102,7 @@ class ContactManagerImpl implements ContactManager, EventListener { ...@@ -102,6 +102,7 @@ class ContactManagerImpl implements ContactManager, EventListener {
throws DbException, GeneralSecurityException { throws DbException, GeneralSecurityException {
PendingContact pendingContact = db.getPendingContact(txn, p); PendingContact pendingContact = db.getPendingContact(txn, p);
db.removePendingContact(txn, p); db.removePendingContact(txn, p);
states.remove(p);
PublicKey theirPublicKey = pendingContact.getPublicKey(); PublicKey theirPublicKey = pendingContact.getPublicKey();
ContactId c = ContactId c =
db.addContact(txn, remote, local, theirPublicKey, verified); db.addContact(txn, remote, local, theirPublicKey, verified);
...@@ -288,17 +289,13 @@ class ContactManagerImpl implements ContactManager, EventListener { ...@@ -288,17 +289,13 @@ class ContactManagerImpl implements ContactManager, EventListener {
if (e instanceof RendezvousConnectionOpenedEvent) { if (e instanceof RendezvousConnectionOpenedEvent) {
RendezvousConnectionOpenedEvent r = RendezvousConnectionOpenedEvent r =
(RendezvousConnectionOpenedEvent) e; (RendezvousConnectionOpenedEvent) e;
PendingContactId p = r.getPendingContactId(); setStateConnected(r.getPendingContactId());
setState(p, WAITING_FOR_CONNECTION, ADDING_CONTACT);
} else if (e instanceof RendezvousConnectionClosedEvent) { } else if (e instanceof RendezvousConnectionClosedEvent) {
RendezvousConnectionClosedEvent r = RendezvousConnectionClosedEvent r =
(RendezvousConnectionClosedEvent) e; (RendezvousConnectionClosedEvent) e;
// We're only interested in failures - if the rendezvous succeeds // We're only interested in failures - if the rendezvous succeeds
// the pending contact will be removed // the pending contact will be removed
if (!r.isSuccess()) { if (!r.isSuccess()) setStateDisconnected(r.getPendingContactId());
PendingContactId p = r.getPendingContactId();
setState(p, ADDING_CONTACT, WAITING_FOR_CONNECTION);
}
} else if (e instanceof RendezvousFailedEvent) { } else if (e instanceof RendezvousFailedEvent) {
RendezvousFailedEvent r = (RendezvousFailedEvent) e; RendezvousFailedEvent r = (RendezvousFailedEvent) e;
setState(r.getPendingContactId(), FAILED); setState(r.getPendingContactId(), FAILED);
...@@ -313,14 +310,22 @@ class ContactManagerImpl implements ContactManager, EventListener { ...@@ -313,14 +310,22 @@ class ContactManagerImpl implements ContactManager, EventListener {
eventBus.broadcast(new PendingContactStateChangedEvent(p, state)); eventBus.broadcast(new PendingContactStateChangedEvent(p, state));
} }
/* private void setStateConnected(PendingContactId p) {
* Sets the state of the given pending contact and broadcasts an event if // Set the state to ADDING_CONTACT if there's no current state or the
* there is no current state or the current state equals {@code expected}. // current state is WAITING_FOR_CONNECTION
*/ if (states.putIfAbsent(p, ADDING_CONTACT) == null ||
private void setState(PendingContactId p, PendingContactState expected, states.replace(p, WAITING_FOR_CONNECTION, ADDING_CONTACT)) {
PendingContactState state) { eventBus.broadcast(new PendingContactStateChangedEvent(p,
PendingContactState old = states.putIfAbsent(p, state); ADDING_CONTACT));
if (old == null || states.replace(p, expected, state)) }
eventBus.broadcast(new PendingContactStateChangedEvent(p, state)); }
private void setStateDisconnected(PendingContactId p) {
// Set the state to WAITING_FOR_CONNECTION if the current state is
// ADDING_CONTACT
if (states.replace(p, ADDING_CONTACT, WAITING_FOR_CONNECTION)) {
eventBus.broadcast(new PendingContactStateChangedEvent(p,
WAITING_FOR_CONNECTION));
}
} }
} }
...@@ -76,6 +76,9 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -76,6 +76,9 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final KeyPair handshakeKeyPair = private final KeyPair handshakeKeyPair =
new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey()); new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey());
private final PendingContact pendingContact = getPendingContact(); private final PendingContact pendingContact = getPendingContact();
private final SecretKey rootKey = getSecretKey();
private final long timestamp = System.currentTimeMillis();
private final boolean alice = new Random().nextBoolean();
private ContactManagerImpl contactManager; private ContactManagerImpl contactManager;
...@@ -87,9 +90,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -87,9 +90,6 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey rootKey = getSecretKey();
long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean();
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
...@@ -329,7 +329,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -329,7 +329,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testPendingContactFailsBeforeConnection() { public void testPendingContactExpiresBeforeConnection() {
// The pending contact expires - the FAILED state is broadcast // The pending contact expires - the FAILED state is broadcast
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(new PredicateMatcher<>( oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
...@@ -338,6 +338,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -338,6 +338,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
}}); }});
contactManager.eventOccurred(new RendezvousFailedEvent( contactManager.eventOccurred(new RendezvousFailedEvent(
pendingContact.getId())); pendingContact.getId()));
context.assertIsSatisfied();
// A rendezvous connection is opened - no state is broadcast // A rendezvous connection is opened - no state is broadcast
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent( contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
...@@ -347,11 +348,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -347,11 +348,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
// The rendezvous connection fails - no state is broadcast // The rendezvous connection fails - no state is broadcast
contactManager.eventOccurred(new RendezvousConnectionClosedEvent( contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
pendingContact.getId(), false)); pendingContact.getId(), false));
context.assertIsSatisfied();
} }
@Test @Test
public void testPendingContactFailsDuringConnection() { public void testPendingContactExpiresDuringFailedConnection() {
// A rendezvous connection is opened - the ADDING_CONTACT state is // A rendezvous connection is opened - the ADDING_CONTACT state is
// broadcast // broadcast
context.checking(new Expectations() {{ context.checking(new Expectations() {{
...@@ -370,12 +370,101 @@ public class ContactManagerImplTest extends BrambleMockTestCase { ...@@ -370,12 +370,101 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
PendingContactStateChangedEvent.class, e -> PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == FAILED))); e.getPendingContactState() == FAILED)));
}}); }});
contactManager.eventOccurred(new RendezvousFailedEvent( contactManager.eventOccurred(new RendezvousFailedEvent(
pendingContact.getId())); pendingContact.getId()));
context.assertIsSatisfied();
// The rendezvous connection fails - no state is broadcast // The rendezvous connection fails - no state is broadcast
contactManager.eventOccurred(new RendezvousConnectionClosedEvent( contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
pendingContact.getId(), false)); pendingContact.getId(), false));
}
@Test
public void testPendingContactExpiresDuringSuccessfulConnection()
throws Exception {
Transaction txn = new Transaction(null, false);
// A rendezvous connection is opened - the ADDING_CONTACT state is
// broadcast
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == ADDING_CONTACT)));
}});
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
pendingContact.getId()));
context.assertIsSatisfied();
// The pending contact expires - the FAILED state is broadcast
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == FAILED)));
}});
contactManager.eventOccurred(new RendezvousFailedEvent(
pendingContact.getId()));
context.assertIsSatisfied();
// The pending contact is converted to a contact - no state is broadcast
context.checking(new DbExpectations() {{
oneOf(db).getPendingContact(txn, pendingContact.getId());
will(returnValue(pendingContact));
oneOf(db).removePendingContact(txn, pendingContact.getId());
oneOf(db).addContact(txn, remote, local,
pendingContact.getPublicKey(), verified);
will(returnValue(contactId));
oneOf(db).setContactAlias(txn, contactId,
pendingContact.getAlias());
oneOf(identityManager).getHandshakeKeys(txn);
will(returnValue(handshakeKeyPair));
oneOf(keyManager).addContact(txn, contactId,
pendingContact.getPublicKey(), handshakeKeyPair);
oneOf(keyManager).addRotationKeys(txn, contactId, rootKey,
timestamp, alice, active);
oneOf(db).getContact(txn, contactId);
will(returnValue(contact));
}});
contactManager.addContact(txn, pendingContact.getId(), remote,
local, rootKey, timestamp, alice, verified, active);
context.assertIsSatisfied(); context.assertIsSatisfied();
// The rendezvous connection succeeds - no state is broadcast
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
pendingContact.getId(), true));
}
@Test
public void testPendingContactRemovedDuringFailedConnection()
throws Exception {
Transaction txn = new Transaction(null, false);
// A rendezvous connection is opened - the ADDING_CONTACT state is
// broadcast
context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(new PredicateMatcher<>(
PendingContactStateChangedEvent.class, e ->
e.getPendingContactState() == ADDING_CONTACT)));
}});
contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
pendingContact.getId()));
context.assertIsSatisfied();
// The pending contact is removed - no state is broadcast
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(db).removePendingContact(txn, pendingContact.getId());
}});
contactManager.removePendingContact(pendingContact.getId());
context.assertIsSatisfied();
// The rendezvous connection fails - no state is broadcast
contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
pendingContact.getId(), false));
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment