From 1aa579a44f7962874906d1c835e59618a4c51b33 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Wed, 5 Jun 2019 12:23:15 +0100
Subject: [PATCH] Add unit tests for pending contact state.

---
 .../contact/ContactManagerImplTest.java       | 108 ++++++++++++++++--
 1 file changed, 99 insertions(+), 9 deletions(-)

diff --git a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
index a96f730fc6..543822a3ea 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/contact/ContactManagerImplTest.java
@@ -1,11 +1,12 @@
 package org.briarproject.bramble.contact;
 
+import org.briarproject.bramble.api.Pair;
 import org.briarproject.bramble.api.contact.Contact;
 import org.briarproject.bramble.api.contact.ContactId;
-import org.briarproject.bramble.api.contact.ContactManager;
+import org.briarproject.bramble.api.contact.PendingContact;
+import org.briarproject.bramble.api.contact.PendingContactState;
+import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
 import org.briarproject.bramble.api.crypto.KeyPair;
-import org.briarproject.bramble.api.crypto.PrivateKey;
-import org.briarproject.bramble.api.crypto.PublicKey;
 import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseComponent;
 import org.briarproject.bramble.api.db.DbException;
@@ -17,8 +18,12 @@ import org.briarproject.bramble.api.identity.AuthorId;
 import org.briarproject.bramble.api.identity.AuthorInfo;
 import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
+import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
+import org.briarproject.bramble.api.rendezvous.event.RendezvousFailedEvent;
 import org.briarproject.bramble.api.transport.KeyManager;
 import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.briarproject.bramble.test.CaptureArgumentAction;
 import org.briarproject.bramble.test.DbExpectations;
 import org.jmock.Expectations;
 import org.junit.Before;
@@ -26,10 +31,14 @@ import org.junit.Test;
 
 import java.util.Collection;
 import java.util.Random;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
+import static org.briarproject.bramble.api.contact.PendingContactState.ADDING_CONTACT;
+import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
+import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
 import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
 import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
@@ -40,6 +49,7 @@ import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
 import static org.briarproject.bramble.test.TestUtils.getAuthor;
 import static org.briarproject.bramble.test.TestUtils.getContact;
 import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
+import static org.briarproject.bramble.test.TestUtils.getPendingContact;
 import static org.briarproject.bramble.test.TestUtils.getRandomId;
 import static org.briarproject.bramble.test.TestUtils.getSecretKey;
 import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
@@ -64,8 +74,11 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	private final boolean verified = false, active = true;
 	private final Contact contact = getContact(remote, local, verified);
 	private final ContactId contactId = contact.getId();
+	private final KeyPair handshakeKeyPair =
+			new KeyPair(getAgreementPublicKey(), getAgreementPrivateKey());
+	private final PendingContact pendingContact = getPendingContact();
 
-	private ContactManager contactManager;
+	private ContactManagerImpl contactManager;
 
 	@Before
 	public void setUp() {
@@ -97,6 +110,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	@Test
 	public void testGetContact() throws Exception {
 		Transaction txn = new Transaction(null, true);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).getContact(txn, contactId);
@@ -110,6 +124,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	public void testGetContactByAuthor() throws Exception {
 		Transaction txn = new Transaction(null, true);
 		Collection<Contact> contacts = singletonList(contact);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).getContactsByAuthorId(txn, remote.getId());
@@ -122,6 +137,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	@Test(expected = NoSuchContactException.class)
 	public void testGetContactByUnknownAuthor() throws Exception {
 		Transaction txn = new Transaction(null, true);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).getContactsByAuthorId(txn, remote.getId());
@@ -135,6 +151,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	public void testGetContactByUnknownLocalAuthor() throws Exception {
 		Transaction txn = new Transaction(null, true);
 		Collection<Contact> contacts = singletonList(contact);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).getContactsByAuthorId(txn, remote.getId());
@@ -148,6 +165,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	public void testGetContacts() throws Exception {
 		Collection<Contact> contacts = singletonList(contact);
 		Transaction txn = new Transaction(null, true);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).getContacts(txn);
@@ -160,6 +178,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	@Test
 	public void testRemoveContact() throws Exception {
 		Transaction txn = new Transaction(null, false);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transaction(with(false), withDbRunnable(txn));
 			oneOf(db).getContact(txn, contactId);
@@ -193,6 +212,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	@Test
 	public void testContactExists() throws Exception {
 		Transaction txn = new Transaction(null, true);
+
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(db).containsContact(txn, remote.getId(), local);
@@ -212,6 +232,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 			oneOf(db).getContactsByAuthorId(txn, remote.getId());
 			will(returnValue(singletonList(contact)));
 		}});
+
 		AuthorInfo authorInfo =
 				contactManager.getAuthorInfo(txn, remote.getId());
 		assertEquals(UNVERIFIED, authorInfo.getStatus());
@@ -229,6 +250,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 			oneOf(db).getContactsByAuthorId(txn, remote.getId());
 			will(returnValue(emptyList()));
 		}});
+
 		AuthorInfo authorInfo =
 				contactManager.getAuthorInfo(txn, remote.getId());
 		assertEquals(UNKNOWN, authorInfo.getStatus());
@@ -253,6 +275,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 			will(returnValue(localAuthor));
 			never(db).getContactsByAuthorId(txn, remote.getId());
 		}});
+
 		authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
 		assertEquals(OURSELVES, authorInfo.getStatus());
 		assertNull(authorInfo.getAlias());
@@ -271,19 +294,86 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
 	@Test
 	public void testGetHandshakeLink() throws Exception {
 		Transaction txn = new Transaction(null, true);
-		PublicKey publicKey = getAgreementPublicKey();
-		PrivateKey privateKey = getAgreementPrivateKey();
-		KeyPair keyPair = new KeyPair(publicKey, privateKey);
 		String link = "briar://" + getRandomBase32String(BASE32_LINK_BYTES);
 
 		context.checking(new DbExpectations() {{
 			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
 			oneOf(identityManager).getHandshakeKeys(txn);
-			will(returnValue(keyPair));
-			oneOf(pendingContactFactory).createHandshakeLink(publicKey);
+			will(returnValue(handshakeKeyPair));
+			oneOf(pendingContactFactory).createHandshakeLink(
+					handshakeKeyPair.getPublic());
 			will(returnValue(link));
 		}});
 
 		assertEquals(link, contactManager.getHandshakeLink());
 	}
+
+	@Test
+	public void testDefaultPendingContactState() throws Exception {
+		Transaction txn = new Transaction(null, true);
+
+		context.checking(new DbExpectations() {{
+			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
+			oneOf(db).getPendingContacts(txn);
+			will(returnValue(singletonList(pendingContact)));
+		}});
+
+		// No events have happened for this pending contact, so the state
+		// should be WAITING_FOR_CONNECTION
+		Collection<Pair<PendingContact, PendingContactState>> pairs =
+				contactManager.getPendingContacts();
+		assertEquals(1, pairs.size());
+		Pair<PendingContact, PendingContactState> pair =
+				pairs.iterator().next();
+		assertEquals(pendingContact, pair.getFirst());
+		assertEquals(WAITING_FOR_CONNECTION, pair.getSecond());
+	}
+
+	@Test
+	public void testFailedStateIsNotReplaced() throws Exception {
+		Transaction txn = new Transaction(null, true);
+		AtomicReference<PendingContactStateChangedEvent> captureFirstEvent =
+				new AtomicReference<>();
+		AtomicReference<PendingContactStateChangedEvent> captureSecondEvent =
+				new AtomicReference<>();
+
+		context.checking(new Expectations() {{
+			oneOf(eventBus).broadcast(with(any(
+					PendingContactStateChangedEvent.class)));
+			will(new CaptureArgumentAction<>(captureFirstEvent,
+					PendingContactStateChangedEvent.class, 0));
+			oneOf(eventBus).broadcast(with(any(
+					PendingContactStateChangedEvent.class)));
+			will(new CaptureArgumentAction<>(captureSecondEvent,
+					PendingContactStateChangedEvent.class, 0));
+		}});
+
+		// A rendezvous connection is opened, then the pending contact expires,
+		// then the rendezvous connection is closed
+		contactManager.eventOccurred(new RendezvousConnectionOpenedEvent(
+				pendingContact.getId()));
+		contactManager.eventOccurred(new RendezvousFailedEvent(
+				pendingContact.getId()));
+		contactManager.eventOccurred(new RendezvousConnectionClosedEvent(
+				pendingContact.getId(), false));
+
+		assertEquals(ADDING_CONTACT,
+				captureFirstEvent.get().getPendingContactState());
+		assertEquals(FAILED, captureSecondEvent.get().getPendingContactState());
+
+		context.checking(new DbExpectations() {{
+			oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
+			oneOf(db).getPendingContacts(txn);
+			will(returnValue(singletonList(pendingContact)));
+		}});
+
+		// The FAILED state should not have been overwritten
+		Collection<Pair<PendingContact, PendingContactState>> pairs =
+				contactManager.getPendingContacts();
+		assertEquals(1, pairs.size());
+		Pair<PendingContact, PendingContactState> pair =
+				pairs.iterator().next();
+		assertEquals(pendingContact, pair.getFirst());
+		assertEquals(FAILED, pair.getSecond());
+	}
 }
-- 
GitLab