diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 158744bd1..c165fc3f9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -62,7 +62,7 @@ android test: when: on_failure rules: - if: '$CI_PIPELINE_SOURCE == "schedule"' - when: on_success + when: manual - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' changes: - briar-android/**/* @@ -85,35 +85,43 @@ test_reproducible: .optional_tests: stage: optional_tests - before_script: - - set -e - - export GRADLE_USER_HOME=$PWD/.gradle - - cache: - key: "$CI_COMMIT_REF_SLUG" - paths: - - .gradle/wrapper - - .gradle/caches - - script: - - OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest - - after_script: - # these file change every time but should not be cached - - rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock - - rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/ + extends: .base-test bridge test: extends: .optional_tests rules: - if: '$CI_PIPELINE_SOURCE == "schedule"' when: on_success - allow_failure: true + allow_failure: false - if: '$CI_COMMIT_TAG == null' when: manual allow_failure: true + script: + - OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest + +mailbox integration test: + extends: .optional_tests + rules: + - if: '$CI_PIPELINE_SOURCE == "schedule"' + when: on_success + allow_failure: false + - if: '$CI_COMMIT_TAG == null' + when: manual + allow_failure: false + script: + # start mailbox + - cd /opt && git clone --depth 1 https://code.briarproject.org/briar/briar-mailbox.git briar-mailbox + - cd briar-mailbox + - mkdir -p /root/.local/share # create directory that mailbox (currently) expects to exist + - ./gradlew run --args="--debug --setup-token 54686973206973206120736574757020746f6b656e20666f722042726961722e" & + # run mailbox integration test once mailbox has started + - cd "$CI_PROJECT_DIR" + - bramble-core/src/test/bash/wait-for-mailbox.sh + - OPTIONAL_TESTS=org.briarproject.bramble.mailbox.MailboxIntegrationTest ./gradlew --info bramble-core:test --tests MailboxIntegrationTest pre_release_tests: extends: .optional_tests + script: + - OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest only: - tags diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApi.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApi.java index 4040df48b..be08cc11a 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApi.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApi.java @@ -43,20 +43,20 @@ interface MailboxApi { /** * Deletes a contact from the mailbox. * This should get called after a contact was removed from Briar. - */ - void deleteContact(MailboxProperties properties, ContactId contactId) - throws IOException, ApiException; - - /** - * Gets a list of {@link ContactId}s from the mailbox. - * These are the contacts that the mailbox already knows about. * * @throws TolerableFailureException if response code is 404 * (contact probably was already deleted). */ - Collection getContacts(MailboxProperties properties) + void deleteContact(MailboxProperties properties, ContactId contactId) throws IOException, ApiException, TolerableFailureException; + /** + * Gets a list of {@link ContactId}s from the mailbox. + * These are the contacts that the mailbox already knows about. + */ + Collection getContacts(MailboxProperties properties) + throws IOException, ApiException; + @Immutable @JsonSerialize class MailboxContact { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApiImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApiImpl.java index 3a42d9e6f..ba24a852d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApiImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/mailbox/MailboxApiImpl.java @@ -113,7 +113,7 @@ class MailboxApiImpl implements MailboxApi { @Override public void deleteContact(MailboxProperties properties, ContactId contactId) - throws IOException, ApiException { + throws IOException, ApiException, TolerableFailureException { if (!properties.isOwner()) throw new IllegalArgumentException(); String url = properties.getOnionAddress() + "/contacts/" + contactId.getInt(); @@ -123,15 +123,15 @@ class MailboxApiImpl implements MailboxApi { .build(); OkHttpClient client = httpClientProvider.get(); Response response = client.newCall(request).execute(); + if (response.code() == 404) throw new TolerableFailureException(); if (response.code() != 200) throw new ApiException(); } @Override public Collection getContacts(MailboxProperties properties) - throws IOException, ApiException, TolerableFailureException { + throws IOException, ApiException { if (!properties.isOwner()) throw new IllegalArgumentException(); Response response = sendGetRequest(properties, "/contacts"); - if (response.code() == 404) throw new TolerableFailureException(); if (response.code() != 200) throw new ApiException(); ResponseBody body = response.body(); diff --git a/bramble-core/src/test/bash/wait-for-mailbox.sh b/bramble-core/src/test/bash/wait-for-mailbox.sh new file mode 100755 index 000000000..955a53d22 --- /dev/null +++ b/bramble-core/src/test/bash/wait-for-mailbox.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +URL="http://127.0.0.1:8000/status" +attempt_counter=0 +max_attempts=200 # 10min - CI for mailbox currently takes ~5min + +echo "Waiting for mailbox to come online at $URL" + +until [[ "$(curl -s -o /dev/null -w '%{http_code}' $URL)" == "401" ]]; do + if [ ${attempt_counter} -eq ${max_attempts} ]; then + echo "Timed out waiting for mailbox" + exit 1 + fi + + printf '.' + attempt_counter=$((attempt_counter + 1)) + sleep 3 +done + +echo "Mailbox started" diff --git a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxApiTest.java b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxApiTest.java index a62d012d6..c27e7ef42 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxApiTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxApiTest.java @@ -230,6 +230,7 @@ public class MailboxApiTest extends BrambleTestCase { server.enqueue(new MockResponse()); server.enqueue(new MockResponse().setResponseCode(205)); server.enqueue(new MockResponse().setResponseCode(401)); + server.enqueue(new MockResponse().setResponseCode(404)); server.start(); String baseUrl = getBaseUrl(server); MailboxProperties properties = @@ -257,6 +258,14 @@ public class MailboxApiTest extends BrambleTestCase { assertEquals("DELETE", request3.getMethod()); assertEquals("/contacts/" + contactId.getInt(), request3.getPath()); assertToken(request3, token); + + // tolerable 404 not found error + assertThrows(TolerableFailureException.class, + () -> api.deleteContact(properties, contactId)); + RecordedRequest request4 = server.takeRequest(); + assertEquals("/contacts/" + contactId.getInt(), request4.getPath()); + assertEquals("DELETE", request4.getMethod()); + assertToken(request4, token); } @Test @@ -286,7 +295,6 @@ public class MailboxApiTest extends BrambleTestCase { server.enqueue(new MockResponse().setBody(invalidResponse3)); server.enqueue(new MockResponse().setResponseCode(401)); server.enqueue(new MockResponse().setResponseCode(500)); - server.enqueue(new MockResponse().setResponseCode(404)); server.start(); String baseUrl = getBaseUrl(server); MailboxProperties properties = @@ -350,14 +358,6 @@ public class MailboxApiTest extends BrambleTestCase { assertEquals("/contacts", request8.getPath()); assertEquals("GET", request8.getMethod()); assertToken(request8, token); - - // tolerable 404 not found error - assertThrows(TolerableFailureException.class, - () -> api.getContacts(properties)); - RecordedRequest request9 = server.takeRequest(); - assertEquals("/contacts", request9.getPath()); - assertEquals("GET", request9.getMethod()); - assertToken(request9, token); } @Test diff --git a/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxIntegrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxIntegrationTest.java new file mode 100644 index 000000000..19fa9a64c --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/mailbox/MailboxIntegrationTest.java @@ -0,0 +1,109 @@ +package org.briarproject.bramble.mailbox; + +import org.briarproject.bramble.api.WeakSingletonProvider; +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.mailbox.MailboxProperties; +import org.briarproject.bramble.mailbox.MailboxApi.ApiException; +import org.briarproject.bramble.mailbox.MailboxApi.MailboxContact; +import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException; +import org.briarproject.bramble.test.BrambleTestCase; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Arrays; + +import javax.annotation.Nonnull; +import javax.net.SocketFactory; + +import okhttp3.OkHttpClient; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.briarproject.bramble.test.TestUtils.getMailboxSecret; +import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +public class MailboxIntegrationTest extends BrambleTestCase { + + private final static String URL_BASE = "http://127.0.0.1:8000"; + private final static String SETUP_TOKEN = + "54686973206973206120736574757020746f6b656e20666f722042726961722e"; + + private final OkHttpClient client = new OkHttpClient.Builder() + .socketFactory(SocketFactory.getDefault()) + .connectTimeout(60_000, MILLISECONDS) + .build(); + private final WeakSingletonProvider httpClientProvider = + new WeakSingletonProvider() { + @Override + @Nonnull + public OkHttpClient createInstance() { + return client; + } + }; + private final MailboxApiImpl api = new MailboxApiImpl(httpClientProvider); + // needs to be static to keep values across different tests + private static MailboxProperties ownerProperties; + + /** + * Called before each test to make sure the mailbox is setup once + * before starting with individual tests. + * {@link BeforeClass} needs to be static, so we can't use the API class. + */ + @Before + public void ensureSetup() throws IOException, ApiException { + // Skip this test unless it's explicitly enabled in the environment + assumeTrue(isOptionalTestEnabled(MailboxIntegrationTest.class)); + + if (ownerProperties != null) return; + MailboxProperties setupProperties = + new MailboxProperties(URL_BASE, SETUP_TOKEN, true); + String ownerToken = api.setup(setupProperties); + ownerProperties = new MailboxProperties(URL_BASE, ownerToken, true); + } + + @Test + public void testStatus() throws Exception { + assertTrue(api.checkStatus(ownerProperties)); + } + + @Test + public void testContactApi() throws Exception { + ContactId contactId1 = new ContactId(1); + ContactId contactId2 = new ContactId(2); + MailboxContact mailboxContact1 = getMailboxContact(contactId1); + MailboxContact mailboxContact2 = getMailboxContact(contactId2); + + // no contacts initially + assertEquals(emptyList(), api.getContacts(ownerProperties)); + // added contact gets returned + api.addContact(ownerProperties, mailboxContact1); + assertEquals(singletonList(contactId1), + api.getContacts(ownerProperties)); + // second contact also gets returned + api.addContact(ownerProperties, mailboxContact2); + assertEquals(Arrays.asList(contactId1, contactId2), + api.getContacts(ownerProperties)); + + // after both contacts get deleted, the list is empty again + api.deleteContact(ownerProperties, contactId1); + api.deleteContact(ownerProperties, contactId2); + assertEquals(emptyList(), api.getContacts(ownerProperties)); + + // deleting again is tolerable + assertThrows(TolerableFailureException.class, + () -> api.deleteContact(ownerProperties, contactId2)); + } + + private MailboxContact getMailboxContact(ContactId contactId) { + return new MailboxContact(contactId, getMailboxSecret(), + getMailboxSecret(), getMailboxSecret()); + } + +}