mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-27 13:13:43 +00:00
Improve message requests, add megaphone.
This commit is contained in:
committed by
Greyson Parrelli
parent
dc689d325b
commit
9e5f64c431
@@ -0,0 +1,102 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
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.Matchers.anyLong;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class SendReadReceiptsJobMigrationTest {
|
||||
|
||||
private final MmsSmsDatabase mockDatabase = mock(MmsSmsDatabase.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<>());
|
||||
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(),
|
||||
"asdf",
|
||||
new Data.Builder()
|
||||
.putString("recipient", RecipientId.from(2).serialize())
|
||||
.putLongArray("message_ids", new long[]{1, 2, 3, 4, 5})
|
||||
.putLong("timestamp", 292837649).build());
|
||||
when(mockDatabase.getThreadForMessageId(anyLong())).thenReturn(1234L);
|
||||
|
||||
// WHEN
|
||||
JobMigration.JobData result = testSubject.migrate(jobData);
|
||||
|
||||
// THEN
|
||||
assertEquals(1234L, result.getData().getLong("thread"));
|
||||
assertEquals(RecipientId.from(2).serialize(), result.getData().getString("recipient"));
|
||||
assertTrue(result.getData().hasLongArray("message_ids"));
|
||||
assertTrue(result.getData().hasLong("timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSendReadReceiptJobDataWithoutThreadIdAndThreadIdNotFound_whenIMigrate_thenIGetAFailingJob() {
|
||||
// GIVEN
|
||||
SendReadReceiptJob job = new SendReadReceiptJob(1, RecipientId.from(2), new ArrayList<>());
|
||||
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(),
|
||||
"asdf",
|
||||
new Data.Builder()
|
||||
.putString("recipient", RecipientId.from(2).serialize())
|
||||
.putLongArray("message_ids", new long[]{})
|
||||
.putLong("timestamp", 292837649).build());
|
||||
when(mockDatabase.getThreadForMessageId(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<>());
|
||||
JobMigration.JobData jobData = new JobMigration.JobData(job.getFactoryKey(), "asdf", 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", new Data.Builder().putLong("thread", 1).build());
|
||||
|
||||
// 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", new Data.Builder().build());
|
||||
|
||||
// WHEN
|
||||
JobMigration.JobData result = testSubject.migrate(jobData);
|
||||
|
||||
// THEN
|
||||
assertEquals(jobData, result);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.thoughtcrime.securesms.notifications;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.powermock.api.mockito.PowerMockito.doAnswer;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest(ApplicationDependencies.class)
|
||||
public class MarkReadReceiverTest {
|
||||
|
||||
private final Context mockContext = mock(Context.class);
|
||||
private final JobManager mockJobManager = mock(JobManager.class);
|
||||
private final List<Job> jobs = new LinkedList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mockStatic(ApplicationDependencies.class);
|
||||
when(ApplicationDependencies.getJobManager()).thenReturn(mockJobManager);
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
jobs.add((Job) invocation.getArguments()[0]);
|
||||
return null;
|
||||
}).when(mockJobManager).add(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMultipleThreadsWithMultipleMessagesEach_whenIProcess_thenIProperlyGroupByThreadAndRecipient() {
|
||||
// GIVEN
|
||||
List<RecipientId> recipients = Stream.range(1L, 4L).map(RecipientId::from).toList();
|
||||
List<Long> threads = Stream.range(4L, 7L).toList();
|
||||
int expected = recipients.size() * threads.size() + 1;
|
||||
|
||||
List<MessagingDatabase.MarkedMessageInfo> infoList = Stream.of(threads)
|
||||
.flatMap(threadId -> Stream.of(recipients)
|
||||
.map(recipientId -> createMarkedMessageInfo(threadId, recipientId)))
|
||||
.toList();
|
||||
|
||||
List<MessagingDatabase.MarkedMessageInfo> duplicatedList = Util.concatenatedList(infoList, infoList);
|
||||
|
||||
// WHEN
|
||||
MarkReadReceiver.process(mockContext, duplicatedList);
|
||||
|
||||
// THEN
|
||||
assertEquals("Should have 10 total jobs, including MultiDeviceReadUpdateJob", expected, jobs.size());
|
||||
|
||||
Set<Pair<Long, String>> threadRecipientPairs = new HashSet<>();
|
||||
Stream.of(jobs).forEach(job -> {
|
||||
if (job instanceof MultiDeviceReadUpdateJob) {
|
||||
return;
|
||||
}
|
||||
|
||||
Data data = job.serialize();
|
||||
|
||||
long threadId = data.getLong("thread");
|
||||
String recipientId = data.getString("recipient");
|
||||
long[] messageIds = data.getLongArray("message_ids");
|
||||
|
||||
assertEquals("Each job should contain two messages.", 2, messageIds.length);
|
||||
assertTrue("Each thread recipient pair should only exist once.", threadRecipientPairs.add(new Pair<>(threadId, recipientId)));
|
||||
});
|
||||
|
||||
assertEquals("Should have 9 total combinations.", 9, threadRecipientPairs.size());
|
||||
}
|
||||
|
||||
private MessagingDatabase.MarkedMessageInfo createMarkedMessageInfo(long threadId, @NonNull RecipientId recipientId) {
|
||||
return new MessagingDatabase.MarkedMessageInfo(threadId,
|
||||
new MessagingDatabase.SyncMessageId(recipientId, 0),
|
||||
new MessagingDatabase.ExpirationInfo(0, 0, 0, false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({DatabaseFactory.class, FeatureFlags.class})
|
||||
public class RecipientUtilTest {
|
||||
|
||||
private Context context = mock(Context.class);
|
||||
private Recipient recipient = mock(Recipient.class);
|
||||
private ThreadDatabase mockThreadDatabase = mock(ThreadDatabase.class);
|
||||
private MmsSmsDatabase mockMmsSmsDatabase = mock(MmsSmsDatabase.class);
|
||||
private RecipientDatabase mockRecipientDatabase = mock(RecipientDatabase.class);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mockStatic(DatabaseFactory.class);
|
||||
when(DatabaseFactory.getThreadDatabase(any())).thenReturn(mockThreadDatabase);
|
||||
when(DatabaseFactory.getMmsSmsDatabase(any())).thenReturn(mockMmsSmsDatabase);
|
||||
when(DatabaseFactory.getRecipientDatabase(any())).thenReturn(mockRecipientDatabase);
|
||||
|
||||
mockStatic(FeatureFlags.class);
|
||||
when(FeatureFlags.messageRequests()).thenReturn(true);
|
||||
|
||||
when(recipient.getId()).thenReturn(RecipientId.from(5));
|
||||
when(recipient.resolve()).thenReturn(recipient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMessageRequestsFlagDisabled_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(FeatureFlags.messageRequests()).thenReturn(false);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenThreadIsNegativeOne_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, -1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRecipientIsNullForThreadId_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIHaveSentASecureMessageInThisThread_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(mockThreadDatabase.getRecipientForThreadId(anyLong())).thenReturn(recipient);
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(1L)).thenReturn(5);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIHaveNotSentASecureMessageInThisThreadAndIAmProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(recipient.isProfileSharing()).thenReturn(true);
|
||||
when(mockThreadDatabase.getRecipientForThreadId(anyLong())).thenReturn(recipient);
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(1L)).thenReturn(0);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIHaveNotSentASecureMessageInThisThreadAndRecipientIsSystemContact_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(recipient.isSystemContact()).thenReturn(true);
|
||||
when(mockThreadDatabase.getRecipientForThreadId(anyLong())).thenReturn(recipient);
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(1L)).thenReturn(0);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIHaveReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectFalse() {
|
||||
// GIVEN
|
||||
when(mockThreadDatabase.getRecipientForThreadId(anyLong())).thenReturn(recipient);
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(1L)).thenReturn(0);
|
||||
when(mockMmsSmsDatabase.getSecureConversationCount(1L)).thenReturn(5);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIHaveNotReceivedASecureMessageIHaveNotSentASecureMessageAndRecipientIsNotSystemContactAndNotProfileSharing_whenIsThreadMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(mockThreadDatabase.getRecipientForThreadId(anyLong())).thenReturn(recipient);
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(1L)).thenReturn(0);
|
||||
when(mockMmsSmsDatabase.getSecureConversationCount(1L)).thenReturn(0);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isThreadMessageRequestAccepted(context, 1L);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRecipientIsNull_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, null);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenMessageRequestsFlagIsOff_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(FeatureFlags.messageRequests()).thenReturn(false);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNonZeroOutgoingSecureMessageCount_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(anyLong())).thenReturn(1);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenIAmProfileSharing_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(recipient.isProfileSharing()).thenReturn(true);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenRecipientIsASystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(recipient.isSystemContact()).thenReturn(true);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNoSecureMessagesSentSomeSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectFalse() {
|
||||
// GIVEN
|
||||
when(mockMmsSmsDatabase.getSecureConversationCount(anyLong())).thenReturn(5);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNoSecureMessagesSentNoSecureMessagesReceivedNotSharingAndNotSystemContact_whenIsRecipientMessageRequestAccepted_thenIExpectTrue() {
|
||||
// GIVEN
|
||||
when(mockMmsSmsDatabase.getSecureConversationCount(anyLong())).thenReturn(0);
|
||||
|
||||
// WHEN
|
||||
boolean result = RecipientUtil.isRecipientMessageRequestAccepted(context, recipient);
|
||||
|
||||
// THEN
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenNoSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() {
|
||||
// GIVEN
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(anyLong())).thenReturn(0);
|
||||
|
||||
// WHEN
|
||||
RecipientUtil.shareProfileIfFirstSecureMessage(context, recipient);
|
||||
|
||||
// THEN
|
||||
verify(mockRecipientDatabase).setProfileSharing(recipient.getId(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenSecureMessagesSent_whenIShareProfileIfFirstSecureMessage_thenIShareProfile() {
|
||||
// GIVEN
|
||||
when(mockMmsSmsDatabase.getOutgoingSecureConversationCount(anyLong())).thenReturn(5);
|
||||
|
||||
// WHEN
|
||||
RecipientUtil.shareProfileIfFirstSecureMessage(context, recipient);
|
||||
|
||||
// THEN
|
||||
verify(mockRecipientDatabase, never()).setProfileSharing(recipient.getId(), true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user