diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 670f4527fc7..768f450c038 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -590,7 +590,7 @@ dependencies { } testImplementation(testLibs.junit.junit) - testImplementation(testLibs.assertj.core) + testImplementation(testLibs.assertk) testImplementation(testLibs.mockito.core) testImplementation(testLibs.mockito.kotlin) testImplementation(testLibs.androidx.test.core) diff --git a/app/proguard/proguard-automation.pro b/app/proguard/proguard-automation.pro index 43561f7b2e1..59ee5d47ba6 100644 --- a/app/proguard/proguard-automation.pro +++ b/app/proguard/proguard-automation.pro @@ -5,9 +5,9 @@ -dontwarn com.android.support.test.** -dontwarn sun.reflect.** -dontwarn sun.misc.** --dontwarn org.assertj.** +-dontwarn assertk.** -dontwarn org.hamcrest.** -dontwarn org.mockito.** -dontwarn com.squareup.** --dontobfuscate \ No newline at end of file +-dontobfuscate diff --git a/app/src/main/res/raw/third_party_licenses b/app/src/main/res/raw/third_party_licenses index 4c395c33e89..7b6c0fee6b9 100644 --- a/app/src/main/res/raw/third_party_licenses +++ b/app/src/main/res/raw/third_party_licenses @@ -25,7 +25,6 @@ The following dependencies are licensed under Apache License, Version 2.0: * Android Tracing (https://developer.android.com/jetpack/androidx/releases/tracing#1.0.0) * AndroidX Futures (https://developer.android.com/topic/libraries/architecture/index.html) * AndroidX Test Library (https://developer.android.com/testing) -* AssertJ fluent assertions * AutoValue Annotations (https://github.com/google/auto/tree/master/value) * Byte Buddy (without dependencies) * Byte Buddy agent @@ -336,6 +335,7 @@ The following dependencies are licensed under The MIT License: * annotations (http://robolectric.org) * framework (http://robolectric.org) * junit (http://robolectric.org) +* assertk (https://github.com/willowtreeapps/assertk) * ktlint (https://github.com/pinterest/ktlint) * ktlint-cli-reporter (https://github.com/pinterest/ktlint) * ktlint-cli-ruleset-core (https://github.com/pinterest/ktlint) diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.java b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.java deleted file mode 100644 index c647af55cdf..00000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.thoughtcrime.securesms.groups.v2; - -import androidx.annotation.NonNull; - -import org.junit.Test; -import org.signal.core.util.Base64; -import org.signal.libsignal.zkgroup.InvalidInputException; -import org.signal.storageservice.protos.groups.GroupInviteLink; -import org.thoughtcrime.securesms.util.Util; - -import java.io.IOException; - -import okio.ByteString; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertNull; - -public final class GroupInviteLinkUrl_InvalidGroupLinkException_Test { - - @Test - public void empty_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException { - assertNull(GroupInviteLinkUrl.fromUri("")); - } - - @Test - public void not_a_url_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException { - assertNull(GroupInviteLinkUrl.fromUri("abc")); - } - - @Test - public void wrong_host() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException { - assertNull(GroupInviteLinkUrl.fromUri("https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o")); - } - - @Test - public void wrong_scheme() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException { - assertNull(GroupInviteLinkUrl.fromUri("http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o")); - } - - @Test - public void has_path() { - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o")) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasMessage("No path was expected in uri"); - } - - @Test - public void missing_ref() { - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/")) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasMessage("No reference was in the uri"); - } - - @Test - public void empty_ref() { - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#")) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasMessage("No reference was in the uri"); - } - - @Test - public void bad_base64() { - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY")) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasCauseExactlyInstanceOf(IOException.class); - } - - @Test - public void bad_protobuf() { - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY")) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasCauseExactlyInstanceOf(IllegalStateException.class); - } - - @Test - public void version_999_url() { - String url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3"; - - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url)) - .isInstanceOf(GroupInviteLinkUrl.UnknownGroupLinkVersionException.class) - .hasMessage("Url contains no known group link content"); - } - - @Test - public void bad_master_key_length() { - byte[] masterKeyBytes = Util.getSecretBytes(33); - GroupLinkPassword password = GroupLinkPassword.createNew(); - - String encoding = createEncodedProtobuf(masterKeyBytes, password.serialize()); - - String url = "https://signal.group/#" + encoding; - - assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url)) - .isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class) - .hasCauseExactlyInstanceOf(InvalidInputException.class); - } - - private static String createEncodedProtobuf(@NonNull byte[] groupMasterKey, - @NonNull byte[] passwordBytes) - { - return Base64.encodeUrlSafeWithoutPadding(new GroupInviteLink.Builder() - .v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder() - .groupMasterKey(ByteString.of(groupMasterKey)) - .inviteLinkPassword(ByteString.of(passwordBytes)) - .build()) - .build() - .encode()); - } - -} \ No newline at end of file diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.kt new file mode 100644 index 00000000000..9089193c680 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/GroupInviteLinkUrl_InvalidGroupLinkException_Test.kt @@ -0,0 +1,130 @@ +package org.thoughtcrime.securesms.groups.v2 + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.hasMessage +import assertk.assertions.isInstanceOf +import assertk.assertions.isNull +import assertk.assertions.messageContains +import assertk.assertions.rootCause +import okio.ByteString +import org.junit.Test +import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding +import org.signal.libsignal.zkgroup.InvalidInputException +import org.signal.storageservice.protos.groups.GroupInviteLink +import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.InvalidGroupLinkException +import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.UnknownGroupLinkVersionException +import org.thoughtcrime.securesms.util.Util +import java.io.IOException + +@Suppress("ClassName") +class GroupInviteLinkUrl_InvalidGroupLinkException_Test { + @Test + fun empty_string() { + val uri = "" + assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull() + } + + @Test + fun not_a_url_string() { + val uri = "abc" + assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull() + } + + @Test + fun wrong_host() { + val uri = "https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o" + assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull() + } + + @Test + fun wrong_scheme() { + val uri = "http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o" + assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull() + } + + @Test + fun has_path() { + val uri = "https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o" + assertFailure { GroupInviteLinkUrl.fromUri(uri) } + .isInstanceOf() + .hasMessage("No path was expected in uri") + } + + @Test + fun missing_ref() { + val uri = "https://signal.group/" + assertFailure { GroupInviteLinkUrl.fromUri(uri) } + .isInstanceOf() + .hasMessage("No reference was in the uri") + } + + @Test + fun empty_ref() { + val uri = "https://signal.group/#" + assertFailure { GroupInviteLinkUrl.fromUri(uri) } + .isInstanceOf() + .hasMessage("No reference was in the uri") + } + + @Test + fun bad_base64() { + val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY" + assertFailure { GroupInviteLinkUrl.fromUri(uri) } + .isInstanceOf() + .rootCause() + .isInstanceOf() + } + + @Test + fun bad_protobuf() { + val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY" + assertFailure { + GroupInviteLinkUrl.fromUri(uri) + }.isInstanceOf() + .rootCause() + .isInstanceOf() + } + + @Test + fun version_999_url() { + val url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3" + assertFailure { GroupInviteLinkUrl.fromUri(url) } + .isInstanceOf() + .messageContains("Url contains no known group link content") + } + + @Test + fun bad_master_key_length() { + val masterKeyBytes = Util.getSecretBytes(33) + val password = GroupLinkPassword.createNew() + + val encoding = createEncodedProtobuf(masterKeyBytes, password.serialize()) + + val url = "https://signal.group/#$encoding" + + assertFailure { GroupInviteLinkUrl.fromUri(url) } + .isInstanceOf() + .rootCause() + .isInstanceOf() + } + + companion object { + private fun createEncodedProtobuf( + groupMasterKey: ByteArray, + passwordBytes: ByteArray + ): String { + return encodeUrlSafeWithoutPadding( + GroupInviteLink.Builder() + .v1Contents( + GroupInviteLink.GroupInviteLinkContentsV1.Builder() + .groupMasterKey(ByteString.of(*groupMasterKey)) + .inviteLinkPassword(ByteString.of(*passwordBytes)) + .build() + ) + .build() + .encode() + ) + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.java b/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.java deleted file mode 100644 index 7c6fe5c526a..00000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.thoughtcrime.securesms.payments; - -import org.junit.Before; -import org.junit.Test; -import org.signal.libsignal.protocol.IdentityKey; -import org.signal.libsignal.protocol.IdentityKeyPair; -import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.signalservice.internal.push.PaymentAddress; - -import okio.ByteString; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.Assert.assertArrayEquals; -import static org.whispersystems.signalservice.test.LibSignalLibraryUtil.assumeLibSignalSupportedOnOS; - -public final class MobileCoinPublicAddressProfileUtilTest { - - @Before - public void ensureNativeSupported() { - assumeLibSignalSupportedOnOS(); - } - - @Test - public void can_verify_an_address() throws PaymentsAddressException { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - byte[] address = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair); - - byte[] paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.getPublicKey()); - - assertArrayEquals(address, paymentsAddress); - } - - @Test - public void can_not_verify_an_address_with_the_wrong_key() { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - IdentityKey wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().getPublicKey(); - byte[] address = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair); - - assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey)) - .isInstanceOf(PaymentsAddressException.class) - .hasMessage("Invalid MobileCoin address signature on payments address proto"); - } - - @Test - public void can_not_verify_a_tampered_signature() { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - byte[] address = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair); - - byte[] signature = signedPaymentAddress.mobileCoinAddress.signature.toByteArray(); - signature[0] = (byte) (signature[0] ^ 0x01); - PaymentAddress tamperedSignature = signedPaymentAddress.newBuilder() - .mobileCoinAddress(signedPaymentAddress.mobileCoinAddress - .newBuilder() - .signature(ByteString.of(signature)) - .build()) - .build(); - - assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.getPublicKey())) - .isInstanceOf(PaymentsAddressException.class) - .hasMessage("Invalid MobileCoin address signature on payments address proto"); - } - - @Test - public void can_not_verify_a_tampered_address() { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - byte[] addressBytes = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair); - - byte[] address = signedPaymentAddress.mobileCoinAddress.address.toByteArray(); - address[0] = (byte) (address[0] ^ 0x01); - PaymentAddress tamperedAddress = signedPaymentAddress.newBuilder() - .mobileCoinAddress(signedPaymentAddress.mobileCoinAddress - .newBuilder() - .address(ByteString.of(address)) - .build()) - .build(); - - assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.getPublicKey())) - .isInstanceOf(PaymentsAddressException.class) - .hasMessage("Invalid MobileCoin address signature on payments address proto"); - } - - @Test - public void can_not_verify_a_missing_signature() { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - byte[] address = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair); - - PaymentAddress removedSignature = signedPaymentAddress.newBuilder() - .mobileCoinAddress(signedPaymentAddress.mobileCoinAddress - .newBuilder() - .signature(null) - .build()) - .build(); - - assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.getPublicKey())) - .isInstanceOf(PaymentsAddressException.class) - .hasMessage("Invalid MobileCoin address signature on payments address proto"); - } - - @Test - public void can_not_verify_a_missing_address() { - IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair(); - byte[] address = Util.getSecretBytes(100); - PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair); - - PaymentAddress removedAddress = signedPaymentAddress.newBuilder() - .mobileCoinAddress(signedPaymentAddress.mobileCoinAddress - .newBuilder() - .address(null) - .build()) - .build(); - - assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.getPublicKey())) - .isInstanceOf(PaymentsAddressException.class) - .hasMessage("Invalid MobileCoin address signature on payments address proto"); - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.kt b/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.kt new file mode 100644 index 00000000000..f201c1e5954 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/payments/MobileCoinPublicAddressProfileUtilTest.kt @@ -0,0 +1,129 @@ +package org.thoughtcrime.securesms.payments + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.hasMessage +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import okio.ByteString +import org.junit.Before +import org.junit.Test +import org.thoughtcrime.securesms.crypto.IdentityKeyUtil +import org.thoughtcrime.securesms.util.Util +import org.whispersystems.signalservice.test.LibSignalLibraryUtil + +class MobileCoinPublicAddressProfileUtilTest { + @Before + fun ensureNativeSupported() { + LibSignalLibraryUtil.assumeLibSignalSupportedOnOS() + } + + @Test + fun can_verify_an_address() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val address = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair) + + val paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.publicKey) + + assertThat(paymentsAddress).isEqualTo(address) + } + + @Test + fun can_not_verify_an_address_with_the_wrong_key() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().publicKey + val address = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair) + + assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey) } + .isInstanceOf() + .hasMessage("Invalid MobileCoin address signature on payments address proto") + } + + @Test + fun can_not_verify_a_tampered_signature() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val address = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair) + val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!! + + val signature = mobileCoinAddress.signature!!.toByteArray() + signature[0] = (signature[0].toInt() xor 0x01).toByte() + val tamperedSignature = signedPaymentAddress.newBuilder() + .mobileCoinAddress( + mobileCoinAddress + .newBuilder() + .signature(ByteString.of(*signature)) + .build() + ) + .build() + + assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.publicKey) } + .isInstanceOf() + .hasMessage("Invalid MobileCoin address signature on payments address proto") + } + + @Test + fun can_not_verify_a_tampered_address() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val addressBytes = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair) + val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!! + + val address = mobileCoinAddress.address!!.toByteArray() + address[0] = (address[0].toInt() xor 0x01).toByte() + val tamperedAddress = signedPaymentAddress.newBuilder() + .mobileCoinAddress( + mobileCoinAddress + .newBuilder() + .address(ByteString.of(*address)) + .build() + ) + .build() + + assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.publicKey) } + .isInstanceOf() + .hasMessage("Invalid MobileCoin address signature on payments address proto") + } + + @Test + fun can_not_verify_a_missing_signature() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val address = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair) + + val removedSignature = signedPaymentAddress.newBuilder() + .mobileCoinAddress( + signedPaymentAddress.mobileCoinAddress!! + .newBuilder() + .signature(null) + .build() + ) + .build() + + assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.publicKey) } + .isInstanceOf() + .hasMessage("Invalid MobileCoin address signature on payments address proto") + } + + @Test + fun can_not_verify_a_missing_address() { + val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair() + val address = Util.getSecretBytes(100) + val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair) + + val removedAddress = signedPaymentAddress.newBuilder() + .mobileCoinAddress( + signedPaymentAddress.mobileCoinAddress!! + .newBuilder() + .address(null) + .build() + ) + .build() + + assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.publicKey) } + .isInstanceOf() + .hasMessage("Invalid MobileCoin address signature on payments address proto") + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.java b/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.java deleted file mode 100644 index 25574f54291..00000000000 --- a/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.thoughtcrime.securesms.util.livedata; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; - -import org.assertj.core.api.Assertions; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; - -import static org.junit.Assert.assertEquals; -import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.assertNoValue; -import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.observeAndGetOneValue; - -public final class LiveDataUtilTest_skip { - - @Rule - public TestRule rule = new LiveDataRule(); - - @Test - public void skip_no_value() { - MutableLiveData liveData = new MutableLiveData<>(); - - LiveData skipped = LiveDataUtil.skip(liveData, 0); - - assertNoValue(skipped); - } - - @Test - public void skip_same_value_with_zero_skip() { - MutableLiveData liveData = new MutableLiveData<>(); - - LiveData skipped = LiveDataUtil.skip(liveData, 0); - liveData.setValue("A"); - - assertEquals("A", observeAndGetOneValue(skipped)); - } - - @Test - public void skip_second_value_with_skip_one() { - MutableLiveData liveData = new MutableLiveData<>(); - TestObserver testObserver = new TestObserver<>(); - - LiveData skipped = LiveDataUtil.skip(liveData, 1); - - skipped.observeForever(testObserver); - liveData.setValue("A"); - liveData.setValue("B"); - skipped.removeObserver(testObserver); - - Assertions.assertThat(testObserver.getValues()) - .containsExactly("B"); - } - - @Test - public void skip_no_value_with_skip() { - MutableLiveData liveData = new MutableLiveData<>(); - - LiveData skipped = LiveDataUtil.skip(liveData, 1); - liveData.setValue("A"); - - assertNoValue(skipped); - } - - @Test - public void skip_third_and_fourth_value_with_skip_two() { - MutableLiveData liveData = new MutableLiveData<>(); - TestObserver testObserver = new TestObserver<>(); - - LiveData skipped = LiveDataUtil.skip(liveData, 2); - - skipped.observeForever(testObserver); - liveData.setValue("A"); - liveData.setValue("B"); - liveData.setValue("C"); - liveData.setValue("D"); - skipped.removeObserver(testObserver); - - Assertions.assertThat(testObserver.getValues()) - .containsExactly("C", "D"); - } - - @Test - public void skip_set_one_before_then_skip() { - MutableLiveData liveData = new MutableLiveData<>(); - TestObserver testObserver = new TestObserver<>(); - - liveData.setValue("A"); - - LiveData skipped = LiveDataUtil.skip(liveData, 2); - - skipped.observeForever(testObserver); - liveData.setValue("B"); - liveData.setValue("C"); - liveData.setValue("D"); - skipped.removeObserver(testObserver); - - Assertions.assertThat(testObserver.getValues()) - .containsExactly("C", "D"); - } - - @Test - public void skip_set_two_before_then_skip() { - MutableLiveData liveData = new MutableLiveData<>(); - TestObserver testObserver = new TestObserver<>(); - - liveData.setValue("A"); - liveData.setValue("B"); - - LiveData skipped = LiveDataUtil.skip(liveData, 2); - - skipped.observeForever(testObserver); - liveData.setValue("C"); - liveData.setValue("D"); - skipped.removeObserver(testObserver); - - Assertions.assertThat(testObserver.getValues()) - .containsExactly("D"); - } -} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.kt b/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.kt new file mode 100644 index 00000000000..c36f15bd0fc --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/util/livedata/LiveDataUtilTest_skip.kt @@ -0,0 +1,112 @@ +package org.thoughtcrime.securesms.util.livedata + +import androidx.lifecycle.MutableLiveData +import assertk.assertThat +import assertk.assertions.containsExactlyInAnyOrder +import assertk.assertions.isEqualTo +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule + +@Suppress("ClassName") +class LiveDataUtilTest_skip { + @get:Rule + val rule: TestRule = LiveDataRule() + + @Test + fun skip_no_value() { + val liveData = MutableLiveData() + + val skipped = LiveDataUtil.skip(liveData, 0) + + LiveDataTestUtil.assertNoValue(skipped) + } + + @Test + fun skip_same_value_with_zero_skip() { + val liveData = MutableLiveData() + + val skipped = LiveDataUtil.skip(liveData, 0) + liveData.value = "A" + + assertThat(LiveDataTestUtil.observeAndGetOneValue(skipped)).isEqualTo("A") + } + + @Test + fun skip_second_value_with_skip_one() { + val liveData = MutableLiveData() + val testObserver = TestObserver() + + val skipped = LiveDataUtil.skip(liveData, 1) + + skipped.observeForever(testObserver) + liveData.value = "A" + liveData.value = "B" + skipped.removeObserver(testObserver) + + assertThat(testObserver.values).containsExactlyInAnyOrder("B") + } + + @Test + fun skip_no_value_with_skip() { + val liveData = MutableLiveData() + + val skipped = LiveDataUtil.skip(liveData, 1) + liveData.value = "A" + + LiveDataTestUtil.assertNoValue(skipped) + } + + @Test + fun skip_third_and_fourth_value_with_skip_two() { + val liveData = MutableLiveData() + val testObserver = TestObserver() + + val skipped = LiveDataUtil.skip(liveData, 2) + + skipped.observeForever(testObserver) + liveData.value = "A" + liveData.value = "B" + liveData.value = "C" + liveData.value = "D" + skipped.removeObserver(testObserver) + + assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D") + } + + @Test + fun skip_set_one_before_then_skip() { + val liveData = MutableLiveData() + val testObserver = TestObserver() + + liveData.value = "A" + + val skipped = LiveDataUtil.skip(liveData, 2) + + skipped.observeForever(testObserver) + liveData.value = "B" + liveData.value = "C" + liveData.value = "D" + skipped.removeObserver(testObserver) + + assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D") + } + + @Test + fun skip_set_two_before_then_skip() { + val liveData = MutableLiveData() + val testObserver = TestObserver() + + liveData.value = "A" + liveData.value = "B" + + val skipped = LiveDataUtil.skip(liveData, 2) + + skipped.observeForever(testObserver) + liveData.value = "C" + liveData.value = "D" + skipped.removeObserver(testObserver) + + assertThat(testObserver.values).containsExactlyInAnyOrder("D") + } +} diff --git a/core-util-jvm/build.gradle.kts b/core-util-jvm/build.gradle.kts index 36ab387f21e..3dea6dbaa0a 100644 --- a/core-util-jvm/build.gradle.kts +++ b/core-util-jvm/build.gradle.kts @@ -55,7 +55,6 @@ dependencies { implementation(libs.kotlinx.coroutines.core.jvm) testImplementation(testLibs.junit.junit) - testImplementation(testLibs.assertj.core) - testImplementation(testLibs.junit.junit) + testImplementation(testLibs.assertk) testImplementation(testLibs.kotlinx.coroutines.test) } diff --git a/gradle/test-libs.versions.toml b/gradle/test-libs.versions.toml index cd962e55272..0a96d20482f 100644 --- a/gradle/test-libs.versions.toml +++ b/gradle/test-libs.versions.toml @@ -23,7 +23,7 @@ robolectric-robolectric = { module = "org.robolectric:robolectric", version.ref bouncycastle-bcprov-jdk15on = "org.bouncycastle:bcprov-jdk15on:1.70" bouncycastle-bcpkix-jdk15on = "org.bouncycastle:bcpkix-jdk15on:1.70" hamcrest-hamcrest = "org.hamcrest:hamcrest:2.2" -assertj-core = "org.assertj:assertj-core:3.11.1" +assertk = "com.willowtreeapps.assertk:assertk:0.28.1" square-okhttp-mockserver = "com.squareup.okhttp3:mockwebserver:4.12.0" mockk = "io.mockk:mockk:1.13.2" mockk-android = "io.mockk:mockk-android:1.13.2" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index dcb14cf613a..53ffac82d5b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2879,9 +2879,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - @@ -3937,11 +3934,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4531,6 +4523,38 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -4950,11 +4974,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5010,11 +5029,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5449,6 +5463,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -5506,6 +5528,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + @@ -5937,11 +5964,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -6070,6 +6092,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + diff --git a/libsignal-service/build.gradle.kts b/libsignal-service/build.gradle.kts index eb8a1c64175..48b365745f8 100644 --- a/libsignal-service/build.gradle.kts +++ b/libsignal-service/build.gradle.kts @@ -101,7 +101,7 @@ dependencies { implementation(project(":core-util-jvm")) testImplementation(testLibs.junit.junit) - testImplementation(testLibs.assertj.core) + testImplementation(testLibs.assertk) testImplementation(testLibs.conscrypt.openjdk.uber) testImplementation(testLibs.mockito.core) testImplementation(testLibs.mockk)