Convert more tests to kotlin.

Resolves #13825
This commit is contained in:
Jameson Williams 2024-11-30 20:33:57 -06:00 committed by Greyson Parrelli
parent 9fa6d4774d
commit 7b0df17d9a
22 changed files with 2598 additions and 2571 deletions

View file

@ -1,79 +0,0 @@
package org.thoughtcrime.securesms.database;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class CursorRecyclerViewAdapterTest {
private CursorRecyclerViewAdapter adapter;
private Context context;
private Cursor cursor;
@Before
public void setUp() {
context = mock(Context.class);
cursor = mock(Cursor.class);
when(cursor.getCount()).thenReturn(100);
when(cursor.moveToPosition(anyInt())).thenReturn(true);
adapter = new CursorRecyclerViewAdapter(context, cursor) {
@Override
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
}
};
}
@Test
public void testSanityCount() throws Exception {
assertEquals(adapter.getItemCount(), 100);
}
@Test
public void testHeaderCount() throws Exception {
adapter.setHeaderView(new View(context));
assertEquals(adapter.getItemCount(), 101);
assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE);
assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE);
assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.HEADER_TYPE);
}
@Test
public void testFooterCount() throws Exception {
adapter.setFooterView(new View(context));
assertEquals(adapter.getItemCount(), 101);
assertEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE);
assertNotEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.FOOTER_TYPE);
assertNotEquals(adapter.getItemViewType(99), CursorRecyclerViewAdapter.FOOTER_TYPE);
}
@Test
public void testHeaderFooterCount() throws Exception {
adapter.setHeaderView(new View(context));
adapter.setFooterView(new View(context));
assertEquals(adapter.getItemCount(), 102);
assertEquals(adapter.getItemViewType(101), CursorRecyclerViewAdapter.FOOTER_TYPE);
assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE);
assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE);
assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE);
}
}

View file

@ -0,0 +1,61 @@
package org.thoughtcrime.securesms.database
import android.content.Context
import android.database.Cursor
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Test
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter.FOOTER_TYPE
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter.HEADER_TYPE
class CursorRecyclerViewAdapterTest {
private val context: Context = mockk<Context>()
private val cursor: Cursor = mockk<Cursor>(relaxUnitFun = true) {
every { count } returns 100
every { moveToPosition(any()) } returns true
}
private val adapter = object : CursorRecyclerViewAdapter<RecyclerView.ViewHolder?>(context, cursor) {
override fun onCreateItemViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder? = null
override fun onBindItemViewHolder(viewHolder: RecyclerView.ViewHolder?, cursor: Cursor) = Unit
}
@Test
fun testSanityCount() {
assertEquals(100, adapter.itemCount)
}
@Test
fun testHeaderCount() {
adapter.headerView = View(context)
assertEquals(101, adapter.itemCount)
assertEquals(HEADER_TYPE, adapter.getItemViewType(0))
assertNotEquals(HEADER_TYPE, adapter.getItemViewType(1))
assertNotEquals(HEADER_TYPE, adapter.getItemViewType(100))
}
@Test
fun testFooterCount() {
adapter.setFooterView(View(context))
assertEquals(101, adapter.itemCount)
assertEquals(FOOTER_TYPE, adapter.getItemViewType(100))
assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(0))
assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(99))
}
@Test
fun testHeaderFooterCount() {
adapter.headerView = View(context)
adapter.setFooterView(View(context))
assertEquals(102, adapter.itemCount)
assertEquals(FOOTER_TYPE, adapter.getItemViewType(101))
assertEquals(HEADER_TYPE, adapter.getItemViewType(0))
assertNotEquals(HEADER_TYPE, adapter.getItemViewType(1))
assertNotEquals(FOOTER_TYPE, adapter.getItemViewType(100))
}
}

View file

@ -1,117 +0,0 @@
package org.thoughtcrime.securesms.jobmanager;
import androidx.annotation.NonNull;
import org.junit.BeforeClass;
import org.junit.Test;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec;
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import kotlin.jvm.functions.Function1;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class JobMigratorTest {
@BeforeClass
public static void init() {
Log.initialize(mock(Log.Logger.class));
}
@Test(expected = AssertionError.class)
public void JobMigrator_crashWhenTooFewMigrations() {
new JobMigrator(1, 2, Collections.emptyList());
}
@Test(expected = AssertionError.class)
public void JobMigrator_crashWhenTooManyMigrations() {
new JobMigrator(1, 2, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3)));
}
@Test(expected = AssertionError.class)
public void JobMigrator_crashWhenSkippingMigrations() {
new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(4)));
}
@Test
public void JobMigrator_properInitialization() {
new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3)));
}
@Test
public void migrate_callsAppropriateMigrations_fullSet() {
JobMigration migration1 = spy(new EmptyMigration(2));
JobMigration migration2 = spy(new EmptyMigration(3));
JobMigrator subject = new JobMigrator(1, 3, Arrays.asList(migration1, migration2));
int version = subject.migrate(simpleJobStorage());
assertEquals(3, version);
verify(migration1).migrate(any());
verify(migration2).migrate(any());
}
@Test
public void migrate_callsAppropriateMigrations_subset() {
JobMigration migration1 = spy(new EmptyMigration(2));
JobMigration migration2 = spy(new EmptyMigration(3));
JobMigrator subject = new JobMigrator(2, 3, Arrays.asList(migration1, migration2));
int version = subject.migrate(simpleJobStorage());
assertEquals(3, version);
verify(migration1, never()).migrate(any());
verify(migration2).migrate(any());
}
@Test
public void migrate_callsAppropriateMigrations_none() {
JobMigration migration1 = spy(new EmptyMigration(2));
JobMigration migration2 = spy(new EmptyMigration(3));
JobMigrator subject = new JobMigrator(3, 3, Arrays.asList(migration1, migration2));
int version = subject.migrate(simpleJobStorage());
assertEquals(3, version);
verify(migration1, never()).migrate(any());
verify(migration2, never()).migrate(any());
}
private static JobStorage simpleJobStorage() {
JobStorage jobStorage = mock(JobStorage.class);
JobSpec job = new JobSpec("1", "f1", null, 1, 1, 1, 1, 1, 1, null, null, false, false, 0, 0);
when(jobStorage.debugGetJobSpecs(anyInt())).thenReturn(new ArrayList<>(Collections.singletonList(job)));
doAnswer(invocation -> {
Function1<JobSpec, JobSpec> transformer = invocation.getArgument(0);
return transformer.invoke(job);
}).when(jobStorage).transformJobs(any());
return jobStorage;
}
private static class EmptyMigration extends JobMigration {
protected EmptyMigration(int endVersion) {
super(endVersion);
}
@Override
public @NonNull JobData migrate(@NonNull JobData jobData) {
return jobData;
}
}
}

View file

@ -0,0 +1,130 @@
package org.thoughtcrime.securesms.jobmanager
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.BeforeClass
import org.junit.Test
import org.signal.core.util.logging.Log.initialize
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage
import org.thoughtcrime.securesms.testutil.EmptyLogger
class JobMigratorTest {
@Test
fun test_JobMigrator_crashWhenTooFewMigrations() {
val error = assertThrows(AssertionError::class.java) {
JobMigrator(1, 2, emptyList())
}
assertEquals("You must have a migration for every version!", error.message)
}
@Test
fun test_JobMigrator_crashWhenTooManyMigrations() {
val error = assertThrows(AssertionError::class.java) {
JobMigrator(1, 2, listOf<JobMigration>(EmptyMigration(2), EmptyMigration(3)))
}
assertEquals("You must have a migration for every version!", error.message)
}
@Test
fun test_JobMigrator_crashWhenSkippingMigrations() {
val error = assertThrows(AssertionError::class.java) {
JobMigrator(1, 3, listOf<JobMigration>(EmptyMigration(2), EmptyMigration(4)))
}
assertEquals("Missing migration for version 3!", error.message)
}
@Test
fun test_JobMigrator_properInitialization() {
JobMigrator(1, 3, listOf<JobMigration>(EmptyMigration(2), EmptyMigration(3)))
}
@Test
fun migrate_callsAppropriateMigrations_fullSet() {
val migration1 = EmptyMigration(2)
val migration2 = EmptyMigration(3)
val subject = JobMigrator(1, 3, listOf(migration1, migration2))
val version = subject.migrate(simpleJobStorage())
assertEquals(3, version)
assertTrue(migration1.migrated)
assertTrue(migration2.migrated)
}
@Test
fun migrate_callsAppropriateMigrations_subset() {
val migration1 = EmptyMigration(2)
val migration2 = EmptyMigration(3)
val subject = JobMigrator(2, 3, listOf(migration1, migration2))
val version = subject.migrate(simpleJobStorage())
assertEquals(3, version)
assertFalse(migration1.migrated)
assertTrue(migration2.migrated)
}
@Test
fun migrate_callsAppropriateMigrations_none() {
val migration1 = EmptyMigration(2)
val migration2 = EmptyMigration(3)
val subject = JobMigrator(3, 3, listOf(migration1, migration2))
val version = subject.migrate(simpleJobStorage())
assertEquals(3, version)
assertFalse(migration1.migrated)
assertFalse(migration2.migrated)
}
private class EmptyMigration(endVersion: Int) : JobMigration(endVersion) {
private var _migrated: Boolean = false
val migrated: Boolean get() = _migrated
override fun migrate(jobData: JobData): JobData {
_migrated = true
return jobData
}
}
companion object {
@JvmStatic
@BeforeClass
fun init() {
initialize(EmptyLogger())
}
private fun simpleJobStorage(): JobStorage {
val job = JobSpec(
id = "1",
factoryKey = "f1",
queueKey = null,
createTime = 1,
lastRunAttemptTime = 1,
nextBackoffInterval = 1,
runAttempt = 1,
maxAttempts = 1,
lifespan = 1,
serializedData = null,
serializedInputData = null,
isRunning = false,
isMemoryOnly = false,
globalPriority = 0,
queuePriority = 0
)
return mockk<JobStorage> {
every { debugGetJobSpecs(any()) } returns listOf(job)
every { transformJobs(any()) } answers {
@Suppress("UNCHECKED_CAST")
val transformer = invocation.args.single() as Function1<JobSpec, JobSpec>
transformer(job)
}
}
}
}
}

View file

@ -1,63 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
import org.thoughtcrime.securesms.jobs.FailingJob;
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
public class RecipientIdFollowUpJobMigrationTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private MockedStatic<Recipient> recipientMockedStatic;
@Mock
private MockedStatic<Job.Parameters> jobParametersMockedStatic;
@Test
public void migrate_sendDeliveryReceiptJob_good() throws Exception {
JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("recipient", "1")
.putLong("message_id", 1)
.putLong("timestamp", 2)
.serialize());
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
JobData converted = subject.migrate(testData);
assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("1", data.getString("recipient"));
assertEquals(1, data.getLong("message_id"));
assertEquals(2, data.getLong("timestamp"));
new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_sendDeliveryReceiptJob_bad() throws Exception {
JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("recipient", "1")
.serialize());
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
JobData converted = subject.migrate(testData);
assertEquals("FailingJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
new FailingJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
}

View file

@ -0,0 +1,57 @@
package org.thoughtcrime.securesms.jobmanager.migrations
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.jobs.FailingJob
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob
class RecipientIdFollowUpJobMigrationTest {
@Test
fun migrate_sendDeliveryReceiptJob_good() {
val testData = JobData(
"SendDeliveryReceiptJob",
null,
-1,
-1,
JsonJobData.Builder().putString("recipient", "1")
.putLong("message_id", 1)
.putLong("timestamp", 2)
.serialize()
)
val subject = RecipientIdFollowUpJobMigration()
val converted = subject.migrate(testData)
assertEquals("SendDeliveryReceiptJob", converted.factoryKey)
assertNull(converted.queueKey)
val data = JsonJobData.deserialize(converted.data)
assertEquals("1", data.getString("recipient"))
assertEquals(1, data.getLong("message_id"))
assertEquals(2, data.getLong("timestamp"))
SendDeliveryReceiptJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_sendDeliveryReceiptJob_bad() {
val testData = JobData(
"SendDeliveryReceiptJob",
null,
-1,
-1,
JsonJobData.Builder().putString("recipient", "1")
.serialize()
)
val subject = RecipientIdFollowUpJobMigration()
val converted = subject.migrate(testData)
assertEquals("FailingJob", converted.factoryKey)
assertNull(converted.queueKey)
FailingJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
}

View file

@ -1,286 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import android.app.Application;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.NewSerializableSyncMessageId;
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.OldSerializableSyncMessageId;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
import org.thoughtcrime.securesms.jobs.IndividualSendJob;
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.JsonUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class RecipientIdJobMigrationTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private MockedStatic<Recipient> recipientMockedStatic;
@Mock
private MockedStatic<Job.Parameters> jobParametersMockStatic;
@Test
public void migrate_multiDeviceContactUpdateJob() throws Exception {
JobData testData = new JobData("MultiDeviceContactUpdateJob", "MultiDeviceContactUpdateJob", -1, -1, new JsonJobData.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("MultiDeviceContactUpdateJob", converted.getFactoryKey());
assertEquals("MultiDeviceContactUpdateJob", converted.getQueueKey());
assertFalse(data.getBoolean("force_sync"));
assertFalse(data.hasString("address"));
assertEquals("1", data.getString("recipient"));
new MultiDeviceContactUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_multiDeviceViewOnceOpenJob() throws Exception {
OldSerializableSyncMessageId oldId = new OldSerializableSyncMessageId("+16101234567", 1);
JobData testData = new JobData("MultiDeviceRevealUpdateJob", null, -1, -1, new JsonJobData.Builder().putString("message_id", JsonUtils.toJson(oldId)).serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("MultiDeviceRevealUpdateJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), data.getString("message_id"));
new MultiDeviceViewOnceOpenJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_sendDeliveryReceiptJob() throws Exception {
JobData testData = new JobData("SendDeliveryReceiptJob", null, -1, -1, new JsonJobData.Builder().putString("address", "+16101234567")
.putLong("message_id", 1)
.putLong("timestamp", 2)
.serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
assertEquals("1", data.getString("recipient"));
assertEquals(1, data.getLong("message_id"));
assertEquals(2, data.getLong("timestamp"));
new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_multiDeviceVerifiedUpdateJob() throws Exception {
JobData testData = new JobData("MultiDeviceVerifiedUpdateJob", "__MULTI_DEVICE_VERIFIED_UPDATE__", -1, -1, new JsonJobData.Builder().putString("destination", "+16101234567")
.putString("identity_key", "abcd")
.putInt("verified_status", 1)
.putLong("timestamp", 123)
.serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("MultiDeviceVerifiedUpdateJob", converted.getFactoryKey());
assertEquals("__MULTI_DEVICE_VERIFIED_UPDATE__", converted.getQueueKey());
assertEquals("abcd", data.getString("identity_key"));
assertEquals(1, data.getInt("verified_status"));
assertEquals(123, data.getLong("timestamp"));
assertEquals("1", data.getString("destination"));
new MultiDeviceVerifiedUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_pushGroupSendJob_null() throws Exception {
JobData testData = new JobData("PushGroupSendJob", "someGroupId", -1, -1, new JsonJobData.Builder().putString("filter_address", null)
.putLong("message_id", 123)
.serialize());
mockRecipientResolve("someGroupId", 5);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("PushGroupSendJob", converted.getFactoryKey());
assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey());
assertNull(data.getString("filter_recipient"));
assertFalse(data.hasString("filter_address"));
new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_pushGroupSendJob_nonNull() throws Exception {
JobData testData = new JobData("PushGroupSendJob", "someGroupId", -1, -1, new JsonJobData.Builder().putString("filter_address", "+16101234567")
.putLong("message_id", 123)
.serialize());
mockRecipientResolve("+16101234567", 1);
mockRecipientResolve("someGroupId", 5);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("PushGroupSendJob", converted.getFactoryKey());
assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey());
assertEquals("1", data.getString("filter_recipient"));
assertFalse(data.hasString("filter_address"));
new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_directoryRefreshJob_null() throws Exception {
JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", -1, -1, new JsonJobData.Builder().putString("address", null).putBoolean("notify_of_new_users", true).serialize());
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("DirectoryRefreshJob", converted.getFactoryKey());
assertEquals("DirectoryRefreshJob", converted.getQueueKey());
assertNull(data.getString("recipient"));
assertTrue(data.getBoolean("notify_of_new_users"));
assertFalse(data.hasString("address"));
new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_directoryRefreshJob_nonNull() throws Exception {
JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", -1, -1, new JsonJobData.Builder().putString("address", "+16101234567").putBoolean("notify_of_new_users", true).serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("DirectoryRefreshJob", converted.getFactoryKey());
assertEquals("DirectoryRefreshJob", converted.getQueueKey());
assertTrue(data.getBoolean("notify_of_new_users"));
assertEquals("1", data.getString("recipient"));
assertFalse(data.hasString("address"));
new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_retrieveProfileAvatarJob() throws Exception {
JobData testData = new JobData("RetrieveProfileAvatarJob", "RetrieveProfileAvatarJob+16101234567", -1, -1, new JsonJobData.Builder().putString("address", "+16101234567").putString("profile_avatar", "abc").serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("RetrieveProfileAvatarJob", converted.getFactoryKey());
assertEquals("RetrieveProfileAvatarJob::" + RecipientId.from(1).toQueueKey(), converted.getQueueKey());
assertEquals("1", data.getString("recipient"));
new RetrieveProfileAvatarJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_multiDeviceReadUpdateJob_empty() throws Exception {
JobData testData = new JobData("MultiDeviceReadUpdateJob", null, -1, -1, new JsonJobData.Builder().putStringArray("message_ids", new String[0]).serialize());
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
assertEquals(0, data.getStringArray("message_ids").length);
new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_multiDeviceReadUpdateJob_twoIds() throws Exception {
OldSerializableSyncMessageId id1 = new OldSerializableSyncMessageId("+16101234567", 1);
OldSerializableSyncMessageId id2 = new OldSerializableSyncMessageId("+16101112222", 2);
JobData testData = new JobData("MultiDeviceReadUpdateJob", null, -1, -1, new JsonJobData.Builder().putStringArray("message_ids", new String[]{ JsonUtils.toJson(id1), JsonUtils.toJson(id2) }).serialize());
mockRecipientResolve("+16101234567", 1);
mockRecipientResolve("+16101112222", 2);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey());
assertNull(converted.getQueueKey());
String[] updated = data.getStringArray("message_ids");
assertEquals(2, updated.length);
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), updated[0]);
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("2", 2)), updated[1]);
new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
@Test
public void migrate_pushMediaSendJob() throws Exception {
JobData testData = new JobData("PushMediaSendJob", "+16101234567", -1, -1, new JsonJobData.Builder().putLong("message_id", 1).serialize());
mockRecipientResolve("+16101234567", 1);
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
JobData converted = subject.migrate(testData);
JsonJobData data = JsonJobData.deserialize(converted.getData());
assertEquals("PushMediaSendJob", converted.getFactoryKey());
assertEquals(RecipientId.from(1).toQueueKey(), converted.getQueueKey());
assertEquals(1, data.getLong("message_id"));
new IndividualSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
private void mockRecipientResolve(String address, long recipientId) {
Recipient mockRecipient = mockRecipient(recipientId);
recipientMockedStatic.when(() -> Recipient.external(any(), eq(address))).thenReturn(mockRecipient);
}
private Recipient mockRecipient(long id) {
Recipient recipient = mock(Recipient.class);
when(recipient.getId()).thenReturn(RecipientId.from(id));
return recipient;
}
}

View file

@ -0,0 +1,365 @@
package org.thoughtcrime.securesms.jobmanager.migrations
import android.app.Application
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkAll
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.NewSerializableSyncMessageId
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.OldSerializableSyncMessageId
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob
import org.thoughtcrime.securesms.jobs.IndividualSendJob
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob
import org.thoughtcrime.securesms.jobs.PushGroupSendJob
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob
import org.thoughtcrime.securesms.recipients.LiveRecipient
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.JsonUtils
class RecipientIdJobMigrationTest {
@Before
fun setup() {
mockkObject(Recipient)
every { Recipient.live(any()) } returns mockk<LiveRecipient>(relaxed = true)
}
@After
fun cleanup() {
unmockkAll()
}
@Test
fun migrate_multiDeviceContactUpdateJob() {
val testData = JobData(
factoryKey = "MultiDeviceContactUpdateJob",
queueKey = "MultiDeviceContactUpdateJob",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("MultiDeviceContactUpdateJob", converted.factoryKey)
assertEquals("MultiDeviceContactUpdateJob", converted.queueKey)
assertFalse(data.getBoolean("force_sync"))
assertFalse(data.hasString("address"))
assertEquals("1", data.getString("recipient"))
MultiDeviceContactUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_multiDeviceViewOnceOpenJob() {
val oldId = OldSerializableSyncMessageId("+16101234567", 1)
val testData = JobData(
factoryKey = "MultiDeviceRevealUpdateJob",
queueKey = null,
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putString("message_id", JsonUtils.toJson(oldId)).serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("MultiDeviceRevealUpdateJob", converted.factoryKey)
assertNull(converted.queueKey)
assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("1", 1)), data.getString("message_id"))
MultiDeviceViewOnceOpenJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_sendDeliveryReceiptJob() {
val testData = JobData(
factoryKey = "SendDeliveryReceiptJob",
queueKey = null,
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putString("address", "+16101234567")
.putLong("message_id", 1)
.putLong("timestamp", 2)
.serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("SendDeliveryReceiptJob", converted.factoryKey)
assertNull(converted.queueKey)
assertEquals("1", data.getString("recipient"))
assertEquals(1, data.getLong("message_id"))
assertEquals(2, data.getLong("timestamp"))
SendDeliveryReceiptJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_multiDeviceVerifiedUpdateJob() {
val testData = JobData(
factoryKey = "MultiDeviceVerifiedUpdateJob",
queueKey = "__MULTI_DEVICE_VERIFIED_UPDATE__",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putString("destination", "+16101234567")
.putString("identity_key", "abcd")
.putInt("verified_status", 1)
.putLong("timestamp", 123)
.serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("MultiDeviceVerifiedUpdateJob", converted.factoryKey)
assertEquals("__MULTI_DEVICE_VERIFIED_UPDATE__", converted.queueKey)
assertEquals("abcd", data.getString("identity_key"))
assertEquals(1, data.getInt("verified_status"))
assertEquals(123, data.getLong("timestamp"))
assertEquals("1", data.getString("destination"))
MultiDeviceVerifiedUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_pushGroupSendJob_null() {
val testData = JobData(
factoryKey = "PushGroupSendJob",
queueKey = "someGroupId",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putString("filter_address", null)
.putLong("message_id", 123)
.serialize()
)
mockRecipientResolve("someGroupId", 5)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("PushGroupSendJob", converted.factoryKey)
assertEquals(RecipientId.from(5).toQueueKey(), converted.queueKey)
assertNull(data.getString("filter_recipient"))
assertFalse(data.hasString("filter_address"))
PushGroupSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_pushGroupSendJob_nonNull() {
val testData = JobData(
factoryKey = "PushGroupSendJob",
queueKey = "someGroupId",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putString("filter_address", "+16101234567")
.putLong("message_id", 123)
.serialize()
)
mockRecipientResolve("+16101234567", 1)
mockRecipientResolve("someGroupId", 5)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("PushGroupSendJob", converted.factoryKey)
assertEquals(RecipientId.from(5).toQueueKey(), converted.queueKey)
assertEquals("1", data.getString("filter_recipient"))
assertFalse(data.hasString("filter_address"))
PushGroupSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_directoryRefreshJob_null() {
val testData = JobData(
factoryKey = "DirectoryRefreshJob",
queueKey = "DirectoryRefreshJob",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("address", null)
.putBoolean("notify_of_new_users", true)
.serialize()
)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("DirectoryRefreshJob", converted.factoryKey)
assertEquals("DirectoryRefreshJob", converted.queueKey)
assertNull(data.getString("recipient"))
assertTrue(data.getBoolean("notify_of_new_users"))
assertFalse(data.hasString("address"))
DirectoryRefreshJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_directoryRefreshJob_nonNull() {
val testData = JobData(
factoryKey = "DirectoryRefreshJob",
queueKey = "DirectoryRefreshJob",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("address", "+16101234567")
.putBoolean("notify_of_new_users", true)
.serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("DirectoryRefreshJob", converted.factoryKey)
assertEquals("DirectoryRefreshJob", converted.queueKey)
assertTrue(data.getBoolean("notify_of_new_users"))
assertEquals("1", data.getString("recipient"))
assertFalse(data.hasString("address"))
DirectoryRefreshJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_retrieveProfileAvatarJob() {
val testData = JobData(
factoryKey = "RetrieveProfileAvatarJob",
queueKey = "RetrieveProfileAvatarJob+16101234567",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("address", "+16101234567")
.putString("profile_avatar", "abc")
.serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("RetrieveProfileAvatarJob", converted.factoryKey)
assertEquals("RetrieveProfileAvatarJob::" + RecipientId.from(1).toQueueKey(), converted.queueKey)
assertEquals("1", data.getString("recipient"))
RetrieveProfileAvatarJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_multiDeviceReadUpdateJob_empty() {
val testData = JobData(
factoryKey = "MultiDeviceReadUpdateJob",
queueKey = null,
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putStringArray("message_ids", arrayOfNulls(0))
.serialize()
)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("MultiDeviceReadUpdateJob", converted.factoryKey)
assertNull(converted.queueKey)
assertEquals(0, data.getStringArray("message_ids").size)
MultiDeviceReadUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_multiDeviceReadUpdateJob_twoIds() {
val id1 = OldSerializableSyncMessageId("+16101234567", 1)
val id2 = OldSerializableSyncMessageId("+16101112222", 2)
val testData = JobData(
factoryKey = "MultiDeviceReadUpdateJob",
queueKey = null,
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putStringArray("message_ids", arrayOf(JsonUtils.toJson(id1), JsonUtils.toJson(id2)))
.serialize()
)
mockRecipientResolve("+16101234567", 1)
mockRecipientResolve("+16101112222", 2)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("MultiDeviceReadUpdateJob", converted.factoryKey)
assertNull(converted.queueKey)
val updated = data.getStringArray("message_ids")
assertEquals(2, updated.size)
assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("1", 1)), updated[0])
assertEquals(JsonUtils.toJson(NewSerializableSyncMessageId("2", 2)), updated[1])
MultiDeviceReadUpdateJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
@Test
fun migrate_pushMediaSendJob() {
val testData = JobData(
factoryKey = "PushMediaSendJob",
queueKey = "+16101234567",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder().putLong("message_id", 1).serialize()
)
mockRecipientResolve("+16101234567", 1)
val subject = RecipientIdJobMigration(mockk<Application>())
val converted = subject.migrate(testData)
val data = JsonJobData.deserialize(converted.data)
assertEquals("PushMediaSendJob", converted.factoryKey)
assertEquals(RecipientId.from(1).toQueueKey(), converted.queueKey)
assertEquals(1, data.getLong("message_id"))
IndividualSendJob.Factory().create(Job.Parameters.Builder().build(), converted.data)
}
private fun mockRecipientResolve(address: String, recipientId: Long) {
every { Recipient.external(any(), address) } returns mockRecipient(recipientId)
}
private fun mockRecipient(id: Long): Recipient {
return mockk<Recipient> {
every { this@mockk.id } returns RecipientId.from(id)
}
}
}

View file

@ -1,107 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Test;
import org.thoughtcrime.securesms.database.MessageTable;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class SendReadReceiptsJobMigrationTest {
private final MessageTable mockDatabase = mock(MessageTable.class);
private final SendReadReceiptsJobMigration testSubject = new SendReadReceiptsJobMigration(mockDatabase);
@Test
public void givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdFound_whenIMigrate_thenIInsertThreadId() {
// GIVEN
SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>());
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(),
"asdf",
-1,
-1,
new JsonJobData.Builder()
.putString("recipient", RecipientId.from(2).serialize())
.putLongArray("message_ids", new long[]{1, 2, 3, 4, 5})
.putLong("timestamp", 292837649).serialize());
when(mockDatabase.getThreadIdForMessage(anyLong())).thenReturn(1234L);
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
JsonJobData data = JsonJobData.deserialize(result.getData());
// THEN
assertEquals(1234L, data.getLong("thread"));
assertEquals(RecipientId.from(2).serialize(), data.getString("recipient"));
assertTrue(data.hasLongArray("message_ids"));
assertTrue(data.hasLong("timestamp"));
}
@Test
public void givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdNotFound_whenIMigrate_thenIGetAFailingJob() {
// GIVEN
SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>());
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(),
"asdf",
-1,
-1,
new JsonJobData.Builder()
.putString("recipient", RecipientId.from(2).serialize())
.putLongArray("message_ids", new long[]{})
.putLong("timestamp", 292837649).serialize());
when(mockDatabase.getThreadIdForMessage(anyLong())).thenReturn(-1L);
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals("FailingJob", result.getFactoryKey());
}
@Test
public void givenSendReadReceiptJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>(), new ArrayList<>());
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(), "asdf", -1, -1, job.serialize());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(jobData, result);
}
@Test
public void givenSomeOtherJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData("SomeOtherJob", "asdf", -1, -1, new JsonJobData.Builder().putLong("thread", 1).serialize());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(jobData, result);
}
@Test
public void givenSomeOtherJobDataWithoutThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData("SomeOtherJob", "asdf", -1, -1, new JsonJobData.Builder().serialize());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(jobData, result);
}
}

View file

@ -0,0 +1,104 @@
package org.thoughtcrime.securesms.jobmanager.migrations
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.thoughtcrime.securesms.database.MessageTable
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.jobs.SendReadReceiptJob
import org.thoughtcrime.securesms.recipients.RecipientId
class SendReadReceiptsJobMigrationTest {
private val mockDatabase = mockk<MessageTable>()
private val testSubject = SendReadReceiptsJobMigration(mockDatabase)
@Test
fun givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdFound_whenIMigrate_thenIInsertThreadId() {
// GIVEN
val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList())
val jobData = JobData(
factoryKey = job.factoryKey,
queueKey = "asdf",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("recipient", RecipientId.from(2).serialize())
.putLongArray("message_ids", longArrayOf(1, 2, 3, 4, 5))
.putLong("timestamp", 292837649).serialize()
)
every { mockDatabase.getThreadIdForMessage(any()) } returns 1234L
// WHEN
val result = testSubject.migrate(jobData)
val data = JsonJobData.deserialize(result.data)
// THEN
assertEquals(1234L, data.getLong("thread"))
assertEquals(RecipientId.from(2).serialize(), data.getString("recipient"))
assertTrue(data.hasLongArray("message_ids"))
assertTrue(data.hasLong("timestamp"))
}
@Test
fun givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdNotFound_whenIMigrate_thenIGetAFailingJob() {
// GIVEN
val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList())
val jobData = JobData(
factoryKey = job.factoryKey,
queueKey = "asdf",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("recipient", RecipientId.from(2).serialize())
.putLongArray("message_ids", longArrayOf())
.putLong("timestamp", 292837649).serialize()
)
every { mockDatabase.getThreadIdForMessage(any()) } returns -1L
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals("FailingJob", result.factoryKey)
}
@Test
fun givenSendReadReceiptJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
val job = SendReadReceiptJob(1, RecipientId.from(2), ArrayList(), ArrayList())
val jobData = JobData(job.factoryKey, "asdf", -1, -1, job.serialize())
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals(jobData, result)
}
@Test
fun givenSomeOtherJobDataWithThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
val jobData = JobData("SomeOtherJob", "asdf", -1, -1, JsonJobData.Builder().putLong("thread", 1).serialize())
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals(jobData, result)
}
@Test
fun givenSomeOtherJobDataWithoutThreadId_whenIMigrate_thenIDoNotReplace() {
// GIVEN
val jobData = JobData("SomeOtherJob", "asdf", -1, -1, JsonJobData.Builder().serialize())
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals(jobData, result)
}
}

View file

@ -1,89 +0,0 @@
package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Test;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
import org.thoughtcrime.securesms.jobs.FailingJob;
import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Util;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class SenderKeyDistributionSendJobRecipientMigrationTest {
private final GroupTable mockDatabase = mock(GroupTable.class);
private final SenderKeyDistributionSendJobRecipientMigration testSubject = new SenderKeyDistributionSendJobRecipientMigration(mockDatabase);
private static final GroupId GROUP_ID = GroupId.pushOrThrow(Util.getSecretBytes(32));
@Test
public void normalMigration() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
-1,
-1,
new JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.serialize());
GroupRecord mockGroup = mock(GroupRecord.class);
when(mockGroup.getRecipientId()).thenReturn(RecipientId.from(2));
when(mockDatabase.getGroup(GROUP_ID)).thenReturn(Optional.of(mockGroup));
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
JsonJobData data = JsonJobData.deserialize(result.getData());
// THEN
assertEquals(RecipientId.from(1).serialize(), data.getString("recipient_id"));
assertEquals(RecipientId.from(2).serialize(), data.getString("thread_recipient_id"));
}
@Test
public void cannotFindGroup() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
-1,
-1,
new JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.serialize());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(FailingJob.KEY, result.getFactoryKey());
}
@Test
public void missingGroupId() {
// GIVEN
JobMigration.JobData jobData = new JobMigration.JobData(SenderKeyDistributionSendJob.KEY,
"asdf",
-1,
-1,
new JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.serialize());
// WHEN
JobMigration.JobData result = testSubject.migrate(jobData);
// THEN
assertEquals(FailingJob.KEY, result.getFactoryKey());
}
}

View file

@ -0,0 +1,94 @@
package org.thoughtcrime.securesms.jobmanager.migrations
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Test
import org.thoughtcrime.securesms.database.GroupTable
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData
import org.thoughtcrime.securesms.jobmanager.JsonJobData
import org.thoughtcrime.securesms.jobs.FailingJob
import org.thoughtcrime.securesms.jobs.SenderKeyDistributionSendJob
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.Util
import java.util.Optional
class SenderKeyDistributionSendJobRecipientMigrationTest {
private val mockDatabase = mockk<GroupTable>(relaxed = true)
private val testSubject = SenderKeyDistributionSendJobRecipientMigration(mockDatabase)
@Test
fun normalMigration() {
// GIVEN
val jobData = JobData(
factoryKey = SenderKeyDistributionSendJob.KEY,
queueKey = "asdf",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.decodedId)
.serialize()
)
val mockGroup = mockk<GroupRecord> {
every { recipientId } returns RecipientId.from(2)
}
every { mockDatabase.getGroup(GROUP_ID) } returns Optional.of(mockGroup)
// WHEN
val result = testSubject.migrate(jobData)
val data = JsonJobData.deserialize(result.data)
// THEN
assertEquals(RecipientId.from(1).serialize(), data.getString("recipient_id"))
assertEquals(RecipientId.from(2).serialize(), data.getString("thread_recipient_id"))
}
@Test
fun cannotFindGroup() {
// GIVEN
val jobData = JobData(
factoryKey = SenderKeyDistributionSendJob.KEY,
queueKey = "asdf",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.putBlobAsString("group_id", GROUP_ID.decodedId)
.serialize()
)
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals(FailingJob.KEY, result.factoryKey)
}
@Test
fun missingGroupId() {
// GIVEN
val jobData = JobData(
factoryKey = SenderKeyDistributionSendJob.KEY,
queueKey = "asdf",
maxAttempts = -1,
lifespan = -1,
data = JsonJobData.Builder()
.putString("recipient_id", RecipientId.from(1).serialize())
.serialize()
)
// WHEN
val result = testSubject.migrate(jobData)
// THEN
assertEquals(FailingJob.KEY, result.factoryKey)
}
companion object {
private val GROUP_ID: GroupId = GroupId.pushOrThrow(Util.getSecretBytes(32))
}
}

View file

@ -1,47 +0,0 @@
package org.thoughtcrime.securesms.payments;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.thoughtcrime.securesms.util.RemoteConfig;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
public final class GeographicalRestrictionsTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private MockedStatic<RemoteConfig> remoteConfigMockedStatic;
@Test
public void e164Allowed_general() {
when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("");
assertTrue(GeographicalRestrictions.e164Allowed("+15551234567"));
when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1");
assertFalse(GeographicalRestrictions.e164Allowed("+15551234567"));
when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1,44");
assertFalse(GeographicalRestrictions.e164Allowed("+15551234567"));
assertFalse(GeographicalRestrictions.e164Allowed("+445551234567"));
assertTrue(GeographicalRestrictions.e164Allowed("+525551234567"));
when(RemoteConfig.paymentsCountryBlocklist()).thenReturn("1 234,44");
assertFalse(GeographicalRestrictions.e164Allowed("+12341234567"));
assertTrue(GeographicalRestrictions.e164Allowed("+15551234567"));
assertTrue(GeographicalRestrictions.e164Allowed("+525551234567"));
assertTrue(GeographicalRestrictions.e164Allowed("+2345551234567"));
}
@Test
public void e164Allowed_nullNotAllowed() {
assertFalse(GeographicalRestrictions.e164Allowed(null));
}
}

View file

@ -0,0 +1,37 @@
package org.thoughtcrime.securesms.payments
import io.mockk.every
import io.mockk.mockkStatic
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.thoughtcrime.securesms.util.RemoteConfig
class GeographicalRestrictionsTest {
@Test
fun e164Allowed_general() {
mockkStatic(RemoteConfig::class) {
every { RemoteConfig.paymentsCountryBlocklist } returns ""
assertTrue(GeographicalRestrictions.e164Allowed("+15551234567"))
every { RemoteConfig.paymentsCountryBlocklist } returns "1"
assertFalse(GeographicalRestrictions.e164Allowed("+15551234567"))
every { RemoteConfig.paymentsCountryBlocklist } returns "1,44"
assertFalse(GeographicalRestrictions.e164Allowed("+15551234567"))
assertFalse(GeographicalRestrictions.e164Allowed("+445551234567"))
assertTrue(GeographicalRestrictions.e164Allowed("+525551234567"))
every { RemoteConfig.paymentsCountryBlocklist } returns "1 234,44"
assertFalse(GeographicalRestrictions.e164Allowed("+12341234567"))
assertTrue(GeographicalRestrictions.e164Allowed("+15551234567"))
assertTrue(GeographicalRestrictions.e164Allowed("+525551234567"))
assertTrue(GeographicalRestrictions.e164Allowed("+2345551234567"))
}
}
@Test
fun e164Allowed_nullNotAllowed() {
assertFalse(GeographicalRestrictions.e164Allowed(null))
}
}

View file

@ -1,109 +0,0 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.fcm;
import android.app.Application;
import android.os.AsyncTask;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import java.io.IOException;
import java.util.Optional;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, application = Application.class)
public final class PushChallengeRequestTest {
@Test
public void getPushChallengeBlocking_returns_absent_if_times_out() {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 50L);
assertFalse(challenge.isPresent());
}
@Test
public void getPushChallengeBlocking_waits_for_specified_period() {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
long startTime = System.currentTimeMillis();
PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 250L);
long duration = System.currentTimeMillis() - startTime;
assertThat(duration, greaterThanOrEqualTo(250L));
}
@Test
public void getPushChallengeBlocking_completes_fast_if_posted_to_event_bus() throws IOException {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
doAnswer(invocation -> {
AsyncTask.execute(() -> PushChallengeRequest.postChallengeResponse("CHALLENGE"));
return null;
}).when(signal).requestRegistrationPushChallenge("session ID", "token");
long startTime = System.currentTimeMillis();
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L);
long duration = System.currentTimeMillis() - startTime;
assertThat(duration, lessThan(500L));
verify(signal).requestRegistrationPushChallenge("session ID", "token");
verifyNoMoreInteractions(signal);
assertTrue(challenge.isPresent());
assertEquals("CHALLENGE", challenge.get());
}
@Test
public void getPushChallengeBlocking_returns_fast_if_no_fcm_token_supplied() {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
long startTime = System.currentTimeMillis();
PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L);
long duration = System.currentTimeMillis() - startTime;
assertThat(duration, lessThan(500L));
}
@Test
public void getPushChallengeBlocking_returns_absent_if_no_fcm_token_supplied() {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L);
verifyNoInteractions(signal);
assertFalse(challenge.isPresent());
}
@Test
public void getPushChallengeBlocking_returns_absent_if_any_IOException_is_thrown() throws IOException {
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
doThrow(new IOException()).when(signal).requestRegistrationPushChallenge(any(), any());
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L);
assertFalse(challenge.isPresent());
}
}

View file

@ -0,0 +1,104 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.registration.fcm
import android.app.Application
import android.os.AsyncTask
import io.mockk.called
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.greaterThanOrEqualTo
import org.hamcrest.Matchers.lessThan
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import java.io.IOException
import java.util.Optional
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
class PushChallengeRequestTest {
@Test
fun pushChallengeBlocking_returns_absent_if_times_out() {
val signal = mockk<SignalServiceAccountManager>(relaxUnitFun = true)
val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 50L)
assertFalse(challenge.isPresent)
}
@Test
fun pushChallengeBlocking_waits_for_specified_period() {
val signal = mockk<SignalServiceAccountManager>(relaxUnitFun = true)
val startTime = System.currentTimeMillis()
PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 250L)
val duration = System.currentTimeMillis() - startTime
assertThat(duration, greaterThanOrEqualTo(250L))
}
@Test
fun pushChallengeBlocking_completes_fast_if_posted_to_event_bus() {
val signal = mockk<SignalServiceAccountManager> {
every {
requestRegistrationPushChallenge("session ID", "token")
} answers {
AsyncTask.execute { PushChallengeRequest.postChallengeResponse("CHALLENGE") }
}
}
val startTime = System.currentTimeMillis()
val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L)
val duration = System.currentTimeMillis() - startTime
assertThat(duration, lessThan(500L))
verify { signal.requestRegistrationPushChallenge("session ID", "token") }
confirmVerified(signal)
assertTrue(challenge.isPresent)
assertEquals("CHALLENGE", challenge.get())
}
@Test
fun pushChallengeBlocking_returns_fast_if_no_fcm_token_supplied() {
val signal = mockk<SignalServiceAccountManager>()
val startTime = System.currentTimeMillis()
PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L)
val duration = System.currentTimeMillis() - startTime
assertThat(duration, lessThan(500L))
}
@Test
fun pushChallengeBlocking_returns_absent_if_no_fcm_token_supplied() {
val signal = mockk<SignalServiceAccountManager>()
val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.empty(), 500L)
verify { signal wasNot called }
assertFalse(challenge.isPresent)
}
@Test
fun pushChallengeBlocking_returns_absent_if_any_IOException_is_thrown() {
val signal = mockk<SignalServiceAccountManager> {
every { requestRegistrationPushChallenge(any(), any()) } throws IOException()
}
val challenge = PushChallengeRequest.getPushChallengeBlocking(signal, "session ID", Optional.of("token"), 500L)
assertFalse(challenge.isPresent)
}
}

View file

@ -1,77 +0,0 @@
package org.thoughtcrime.securesms.registration.v2;
import org.junit.Test;
import org.signal.core.util.StreamUtil;
import org.signal.libsignal.svr2.PinHash;
import org.thoughtcrime.securesms.registration.testdata.KbsTestVector;
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
import org.whispersystems.signalservice.api.kbs.KbsData;
import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
import org.whispersystems.signalservice.internal.util.JsonUtil;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.thoughtcrime.securesms.testutil.SecureRandomTestUtil.mockRandom;
public final class PinHashKbsDataTest {
@Test
public void vectors_createNewKbsData() throws IOException {
for (KbsTestVector vector : getKbsTestVectorList()) {
PinHash pinHash = fromArgon2Hash(vector.getArgon2Hash());
KbsData kbsData = PinHashUtil.createNewKbsData(pinHash, MasterKey.createNew(mockRandom(vector.getMasterKey())));
assertArrayEquals(vector.getMasterKey(), kbsData.getMasterKey().serialize());
assertArrayEquals(vector.getIvAndCipher(), kbsData.getCipherText());
assertArrayEquals(vector.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertEquals(vector.getRegistrationLock(), kbsData.getMasterKey().deriveRegistrationLock());
}
}
@Test
public void vectors_decryptKbsDataIVCipherText() throws IOException, InvalidCiphertextException {
for (KbsTestVector vector : getKbsTestVectorList()) {
PinHash hashedPin = fromArgon2Hash(vector.getArgon2Hash());
KbsData kbsData = PinHashUtil.decryptSvrDataIVCipherText(hashedPin, vector.getIvAndCipher());
assertArrayEquals(vector.getMasterKey(), kbsData.getMasterKey().serialize());
assertArrayEquals(vector.getIvAndCipher(), kbsData.getCipherText());
assertArrayEquals(vector.getKbsAccessKey(), kbsData.getKbsAccessKey());
assertEquals(vector.getRegistrationLock(), kbsData.getMasterKey().deriveRegistrationLock());
}
}
private static KbsTestVector[] getKbsTestVectorList() throws IOException {
try (InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("data/kbs_vectors.json")) {
KbsTestVector[] data = JsonUtil.fromJson(StreamUtil.readFullyAsString(resourceAsStream), KbsTestVector[].class);
assertTrue(data.length > 0);
return data;
}
}
public static PinHash fromArgon2Hash(byte[] argon2Hash64) {
if (argon2Hash64.length != 64) throw new AssertionError();
byte[] K = Arrays.copyOfRange(argon2Hash64, 0, 32);
byte[] kbsAccessKey = Arrays.copyOfRange(argon2Hash64, 32, 64);
PinHash mocked = mock(PinHash.class);
when(mocked.encryptionKey()).thenReturn(K);
when(mocked.accessKey()).thenReturn(kbsAccessKey);
return mocked;
}
}

View file

@ -0,0 +1,72 @@
package org.thoughtcrime.securesms.registration.v2
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.signal.core.util.StreamUtil
import org.signal.libsignal.svr2.PinHash
import org.thoughtcrime.securesms.registration.v2.testdata.KbsTestVector
import org.thoughtcrime.securesms.testutil.SecureRandomTestUtil
import org.whispersystems.signalservice.api.kbs.MasterKey
import org.whispersystems.signalservice.api.kbs.PinHashUtil.createNewKbsData
import org.whispersystems.signalservice.api.kbs.PinHashUtil.decryptSvrDataIVCipherText
import org.whispersystems.signalservice.internal.util.JsonUtil
class PinHashKbsDataTest {
@Test
fun vectors_createNewKbsData() {
for (vector in kbsTestVectorList) {
val pinHash = fromArgon2Hash(vector.argon2Hash)
val kbsData = createNewKbsData(pinHash, MasterKey.createNew(SecureRandomTestUtil.mockRandom(vector.masterKey)))
assertArrayEquals(vector.masterKey, kbsData.masterKey.serialize())
assertArrayEquals(vector.ivAndCipher, kbsData.cipherText)
assertArrayEquals(vector.kbsAccessKey, kbsData.kbsAccessKey)
assertEquals(vector.registrationLock, kbsData.masterKey.deriveRegistrationLock())
}
}
@Test
fun vectors_decryptKbsDataIVCipherText() {
for (vector in kbsTestVectorList) {
val hashedPin = fromArgon2Hash(vector.argon2Hash)
val kbsData = decryptSvrDataIVCipherText(hashedPin, vector.ivAndCipher)
assertArrayEquals(vector.masterKey, kbsData.masterKey.serialize())
assertArrayEquals(vector.ivAndCipher, kbsData.cipherText)
assertArrayEquals(vector.kbsAccessKey, kbsData.kbsAccessKey)
assertEquals(vector.registrationLock, kbsData.masterKey.deriveRegistrationLock())
}
}
companion object {
private val kbsTestVectorList: Array<KbsTestVector>
get() {
ClassLoader.getSystemClassLoader().getResourceAsStream("data/kbs_vectors.json").use { resourceAsStream ->
val data: Array<KbsTestVector> = JsonUtil.fromJson(
StreamUtil.readFullyAsString(resourceAsStream),
Array<KbsTestVector>::class.java
)
assertTrue(data.isNotEmpty())
return data
}
}
fun fromArgon2Hash(argon2Hash64: ByteArray): PinHash {
if (argon2Hash64.size != 64) throw AssertionError()
val k = argon2Hash64.copyOfRange(0, 32)
val kbsAccessKey = argon2Hash64.copyOfRange(32, 64)
return mockk<PinHash> {
every { encryptionKey() } returns k
every { accessKey() } returns kbsAccessKey
}
}
}
}

View file

@ -1,63 +0,0 @@
package org.thoughtcrime.securesms.registration.testdata;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.thoughtcrime.securesms.testutil.HexDeserializer;
public final class KbsTestVector {
@JsonProperty("backup_id")
@JsonDeserialize(using = HexDeserializer.class)
private byte[] backupId;
@JsonProperty("argon2_hash")
@JsonDeserialize(using = HexDeserializer.class)
private byte[] argon2Hash;
@JsonProperty("pin")
private String pin;
@JsonProperty("registration_lock")
private String registrationLock;
@JsonProperty("master_key")
@JsonDeserialize(using = HexDeserializer.class)
private byte[] masterKey;
@JsonProperty("kbs_access_key")
@JsonDeserialize(using = HexDeserializer.class)
private byte[] kbsAccessKey;
@JsonProperty("iv_and_cipher")
@JsonDeserialize(using = HexDeserializer.class)
private byte[] ivAndCipher;
public byte[] getBackupId() {
return backupId;
}
public byte[] getArgon2Hash() {
return argon2Hash;
}
public String getPin() {
return pin;
}
public String getRegistrationLock() {
return registrationLock;
}
public byte[] getMasterKey() {
return masterKey;
}
public byte[] getKbsAccessKey() {
return kbsAccessKey;
}
public byte[] getIvAndCipher() {
return ivAndCipher;
}
}

View file

@ -0,0 +1,62 @@
package org.thoughtcrime.securesms.registration.v2.testdata
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import org.thoughtcrime.securesms.testutil.HexDeserializer
data class KbsTestVector(
@JsonProperty("backup_id")
@JsonDeserialize(using = HexDeserializer::class)
val backupId: ByteArray,
@JsonProperty("argon2_hash")
@JsonDeserialize(using = HexDeserializer::class)
val argon2Hash: ByteArray,
@JsonProperty("pin")
val pin: String? = null,
@JsonProperty("registration_lock")
val registrationLock: String? = null,
@JsonProperty("master_key")
@JsonDeserialize(using = HexDeserializer::class)
val masterKey: ByteArray,
@JsonProperty("kbs_access_key")
@JsonDeserialize(using = HexDeserializer::class)
val kbsAccessKey: ByteArray,
@JsonProperty("iv_and_cipher")
@JsonDeserialize(using = HexDeserializer::class)
val ivAndCipher: ByteArray
) {
// equals() and hashCode() are still recommended on data class because of ByteArray usage
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as KbsTestVector
if (!backupId.contentEquals(other.backupId)) return false
if (!argon2Hash.contentEquals(other.argon2Hash)) return false
if (pin != other.pin) return false
if (registrationLock != other.registrationLock) return false
if (!masterKey.contentEquals(other.masterKey)) return false
if (!kbsAccessKey.contentEquals(other.kbsAccessKey)) return false
if (!ivAndCipher.contentEquals(other.ivAndCipher)) return false
return true
}
override fun hashCode(): Int {
var result = backupId.contentHashCode()
result = 31 * result + argon2Hash.contentHashCode()
result = 31 * result + (pin?.hashCode() ?: 0)
result = 31 * result + (registrationLock?.hashCode() ?: 0)
result = 31 * result + masterKey.contentHashCode()
result = 31 * result + kbsAccessKey.contentHashCode()
result = 31 * result + ivAndCipher.contentHashCode()
return result
}
}