Introduce common push notification interfaces/pathways

This commit is contained in:
Jon Chambers
2022-08-03 10:07:53 -04:00
committed by GitHub
parent 0d24828539
commit 6f0faae4ce
23 changed files with 843 additions and 1011 deletions

View File

@@ -0,0 +1,210 @@
/*
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.push;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.eatthepath.pushy.apns.ApnsClient;
import com.eatthepath.pushy.apns.ApnsPushNotification;
import com.eatthepath.pushy.apns.DeliveryPriority;
import com.eatthepath.pushy.apns.PushNotificationResponse;
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
import com.eatthepath.pushy.apns.util.concurrent.PushNotificationFuture;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
class APNSenderTest {
private static final String DESTINATION_APN_ID = "foo";
private Account destinationAccount;
private Device destinationDevice;
private ApnsClient apnsClient;
@BeforeEach
void setup() {
destinationAccount = mock(Account.class);
destinationDevice = mock(Device.class);
apnsClient = mock(ApnsClient.class);
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID);
}
@Test
void testSendVoip() {
PushNotificationResponse<SimpleApnsPushNotification> response = mock(PushNotificationResponse.class);
when(response.isAccepted()).thenReturn(true);
when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class)))
.thenAnswer((Answer) invocationOnMock -> new MockPushNotificationFuture<>(invocationOnMock.getArgument(0), response));
RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient);
PushNotification pushNotification = new PushNotification(DESTINATION_APN_ID, PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, destinationAccount, destinationDevice);
APNSender apnSender = new APNSender(new SynchronousExecutorService(), retryingApnsClient, "foo", false);
final SendPushNotificationResult result = apnSender.sendNotification(pushNotification).join();
ArgumentCaptor<SimpleApnsPushNotification> notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class);
verify(apnsClient).sendNotification(notification.capture());
assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID);
assertThat(notification.getValue().getExpiration()).isEqualTo(APNSender.MAX_EXPIRATION);
assertThat(notification.getValue().getPayload()).isEqualTo(APNSender.APN_VOIP_NOTIFICATION_PAYLOAD);
assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE);
assertThat(notification.getValue().getTopic()).isEqualTo("foo.voip");
assertThat(result.accepted()).isTrue();
assertThat(result.errorCode()).isNull();
assertThat(result.unregistered()).isFalse();
verifyNoMoreInteractions(apnsClient);
}
@Test
void testSendApns() {
PushNotificationResponse<SimpleApnsPushNotification> response = mock(PushNotificationResponse.class);
when(response.isAccepted()).thenReturn(true);
when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class)))
.thenAnswer((Answer) invocationOnMock -> new MockPushNotificationFuture<>(invocationOnMock.getArgument(0), response));
RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient);
PushNotification pushNotification = new PushNotification(DESTINATION_APN_ID, PushNotification.TokenType.APN, PushNotification.NotificationType.NOTIFICATION, null, destinationAccount, destinationDevice);
APNSender apnSender = new APNSender(new SynchronousExecutorService(), retryingApnsClient, "foo", false);
final SendPushNotificationResult result = apnSender.sendNotification(pushNotification).join();
ArgumentCaptor<SimpleApnsPushNotification> notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class);
verify(apnsClient).sendNotification(notification.capture());
assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID);
assertThat(notification.getValue().getExpiration()).isEqualTo(APNSender.MAX_EXPIRATION);
assertThat(notification.getValue().getPayload()).isEqualTo(APNSender.APN_NSE_NOTIFICATION_PAYLOAD);
assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE);
assertThat(notification.getValue().getTopic()).isEqualTo("foo");
assertThat(result.accepted()).isTrue();
assertThat(result.errorCode()).isNull();
assertThat(result.unregistered()).isFalse();
verifyNoMoreInteractions(apnsClient);
}
@Test
void testUnregisteredUser() throws Exception {
PushNotificationResponse<SimpleApnsPushNotification> response = mock(PushNotificationResponse.class);
when(response.isAccepted()).thenReturn(false);
when(response.getRejectionReason()).thenReturn(Optional.of("Unregistered"));
when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class)))
.thenAnswer((Answer) invocationOnMock -> new MockPushNotificationFuture<>(invocationOnMock.getArgument(0), response));
RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient);
PushNotification pushNotification = new PushNotification(DESTINATION_APN_ID, PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, destinationAccount, destinationDevice);
APNSender apnSender = new APNSender(new SynchronousExecutorService(), retryingApnsClient, "foo", false);
when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID);
when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(11));
final SendPushNotificationResult result = apnSender.sendNotification(pushNotification).join();
ArgumentCaptor<SimpleApnsPushNotification> notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class);
verify(apnsClient).sendNotification(notification.capture());
assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID);
assertThat(notification.getValue().getExpiration()).isEqualTo(APNSender.MAX_EXPIRATION);
assertThat(notification.getValue().getPayload()).isEqualTo(APNSender.APN_VOIP_NOTIFICATION_PAYLOAD);
assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE);
assertThat(result.accepted()).isFalse();
assertThat(result.errorCode()).isEqualTo("Unregistered");
assertThat(result.unregistered()).isTrue();
}
@Test
void testGenericFailure() {
ApnsClient apnsClient = mock(ApnsClient.class);
PushNotificationResponse<SimpleApnsPushNotification> response = mock(PushNotificationResponse.class);
when(response.isAccepted()).thenReturn(false);
when(response.getRejectionReason()).thenReturn(Optional.of("BadTopic"));
when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class)))
.thenAnswer((Answer) invocationOnMock -> new MockPushNotificationFuture<>(invocationOnMock.getArgument(0), response));
RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient);
PushNotification pushNotification = new PushNotification(DESTINATION_APN_ID, PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, destinationAccount, destinationDevice);
APNSender apnSender = new APNSender(new SynchronousExecutorService(), retryingApnsClient, "foo", false);
final SendPushNotificationResult result = apnSender.sendNotification(pushNotification).join();
ArgumentCaptor<SimpleApnsPushNotification> notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class);
verify(apnsClient).sendNotification(notification.capture());
assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID);
assertThat(notification.getValue().getExpiration()).isEqualTo(APNSender.MAX_EXPIRATION);
assertThat(notification.getValue().getPayload()).isEqualTo(APNSender.APN_VOIP_NOTIFICATION_PAYLOAD);
assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE);
assertThat(result.accepted()).isFalse();
assertThat(result.errorCode()).isEqualTo("BadTopic");
assertThat(result.unregistered()).isFalse();
}
@Test
void testFailure() {
PushNotificationResponse<SimpleApnsPushNotification> response = mock(PushNotificationResponse.class);
when(response.isAccepted()).thenReturn(true);
when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class)))
.thenAnswer((Answer) invocationOnMock -> new MockPushNotificationFuture<>(invocationOnMock.getArgument(0), new IOException("lost connection")));
RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient);
PushNotification pushNotification = new PushNotification(DESTINATION_APN_ID, PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, destinationAccount, destinationDevice);
APNSender apnSender = new APNSender(new SynchronousExecutorService(), retryingApnsClient, "foo", false);
assertThatThrownBy(() -> apnSender.sendNotification(pushNotification).join())
.isInstanceOf(CompletionException.class)
.hasCauseInstanceOf(IOException.class);
verify(apnsClient).sendNotification(any());
verifyNoMoreInteractions(apnsClient);
}
private static class MockPushNotificationFuture <P extends ApnsPushNotification, V> extends PushNotificationFuture<P, V> {
MockPushNotificationFuture(final P pushNotification, final V response) {
super(pushNotification);
complete(response);
}
MockPushNotificationFuture(final P pushNotification, final Exception exception) {
super(pushNotification);
completeExceptionally(exception);
}
}
}

View File

@@ -100,14 +100,14 @@ class ApnFallbackManagerTest {
assertEquals(1, worker.processNextSlot());
final ArgumentCaptor<ApnMessage> messageCaptor = ArgumentCaptor.forClass(ApnMessage.class);
verify(apnSender).sendMessage(messageCaptor.capture());
final ArgumentCaptor<PushNotification> notificationCaptor = ArgumentCaptor.forClass(PushNotification.class);
verify(apnSender).sendNotification(notificationCaptor.capture());
final ApnMessage message = messageCaptor.getValue();
final PushNotification pushNotification = notificationCaptor.getValue();
assertEquals(VOIP_APN_ID, message.getApnId());
assertEquals(Optional.of(ACCOUNT_UUID), message.getUuid());
assertEquals(DEVICE_ID, message.getDeviceId());
assertEquals(VOIP_APN_ID, pushNotification.deviceToken());
assertEquals(account, pushNotification.destination());
assertEquals(device, pushNotification.destinationDevice());
assertEquals(0, worker.processNextSlot());
}

View File

@@ -5,8 +5,12 @@
package org.whispersystems.textsecuregcm.push;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -16,24 +20,18 @@ import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.MessagingErrorCode;
import java.util.Optional;
import java.util.UUID;
import java.io.IOException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
import org.whispersystems.textsecuregcm.util.Util;
class FcmSenderTest {
private ExecutorService executorService;
private AccountsManager accountsManager;
private FirebaseMessaging firebaseMessaging;
private FcmSender fcmSender;
@@ -41,10 +39,9 @@ class FcmSenderTest {
@BeforeEach
void setUp() {
executorService = new SynchronousExecutorService();
accountsManager = mock(AccountsManager.class);
firebaseMessaging = mock(FirebaseMessaging.class);
fcmSender = new FcmSender(executorService, accountsManager, firebaseMessaging);
fcmSender = new FcmSender(executorService, firebaseMessaging);
}
@AfterEach
@@ -57,35 +54,44 @@ class FcmSenderTest {
@Test
void testSendMessage() {
AccountsHelper.setupMockUpdate(accountsManager);
final GcmMessage message = new GcmMessage("foo", UUID.randomUUID(), 1, GcmMessage.Type.NOTIFICATION, Optional.empty());
final PushNotification pushNotification = new PushNotification("foo", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, null, null);
final SettableApiFuture<String> sendFuture = SettableApiFuture.create();
sendFuture.set("message-id");
when(firebaseMessaging.sendAsync(any())).thenReturn(sendFuture);
fcmSender.sendMessage(message);
final SendPushNotificationResult result = fcmSender.sendNotification(pushNotification).join();
verify(firebaseMessaging).sendAsync(any(Message.class));
assertTrue(result.accepted());
assertNull(result.errorCode());
assertFalse(result.unregistered());
}
@Test
void testSendUninstalled() {
final UUID destinationUuid = UUID.randomUUID();
final String gcmId = "foo";
void testSendMessageRejected() {
final PushNotification pushNotification = new PushNotification("foo", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, null, null);
final Account destinationAccount = mock(Account.class);
final Device destinationDevice = mock(Device.class );
final FirebaseMessagingException invalidArgumentException = mock(FirebaseMessagingException.class);
when(invalidArgumentException.getMessagingErrorCode()).thenReturn(MessagingErrorCode.INVALID_ARGUMENT);
AccountsHelper.setupMockUpdate(accountsManager);
final SettableApiFuture<String> sendFuture = SettableApiFuture.create();
sendFuture.setException(invalidArgumentException);
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
when(accountsManager.getByAccountIdentifier(destinationUuid)).thenReturn(Optional.of(destinationAccount));
when(destinationDevice.getGcmId()).thenReturn(gcmId);
when(firebaseMessaging.sendAsync(any())).thenReturn(sendFuture);
final GcmMessage message = new GcmMessage(gcmId, destinationUuid, 1, GcmMessage.Type.NOTIFICATION, Optional.empty());
final SendPushNotificationResult result = fcmSender.sendNotification(pushNotification).join();
verify(firebaseMessaging).sendAsync(any(Message.class));
assertFalse(result.accepted());
assertEquals("INVALID_ARGUMENT", result.errorCode());
assertFalse(result.unregistered());
}
@Test
void testSendMessageUnregistered() {
final PushNotification pushNotification = new PushNotification("foo", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, null, null);
final FirebaseMessagingException unregisteredException = mock(FirebaseMessagingException.class);
when(unregisteredException.getMessagingErrorCode()).thenReturn(MessagingErrorCode.UNREGISTERED);
@@ -95,11 +101,27 @@ class FcmSenderTest {
when(firebaseMessaging.sendAsync(any())).thenReturn(sendFuture);
fcmSender.sendMessage(message);
final SendPushNotificationResult result = fcmSender.sendNotification(pushNotification).join();
verify(firebaseMessaging).sendAsync(any(Message.class));
verify(accountsManager).getByAccountIdentifier(destinationUuid);
verify(accountsManager).updateDevice(eq(destinationAccount), eq(1L), any());
verify(destinationDevice).setUninstalledFeedbackTimestamp(Util.todayInMillis());
assertFalse(result.accepted());
assertEquals("UNREGISTERED", result.errorCode());
assertTrue(result.unregistered());
}
@Test
void testSendMessageException() {
final PushNotification pushNotification = new PushNotification("foo", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, null, null);
final SettableApiFuture<String> sendFuture = SettableApiFuture.create();
sendFuture.setException(new IOException());
when(firebaseMessaging.sendAsync(any())).thenReturn(sendFuture);
final CompletionException completionException =
assertThrows(CompletionException.class, () -> fcmSender.sendNotification(pushNotification).join());
verify(firebaseMessaging).sendAsync(any(Message.class));
assertTrue(completionException.getCause() instanceof IOException);
}
}

View File

@@ -5,12 +5,14 @@
package org.whispersystems.textsecuregcm.push;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -37,8 +39,7 @@ class MessageSenderTest {
private ClientPresenceManager clientPresenceManager;
private MessagesManager messagesManager;
private FcmSender fcmSender;
private APNSender apnSender;
private PushNotificationManager pushNotificationManager;
private MessageSender messageSender;
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
@@ -53,13 +54,10 @@ class MessageSenderTest {
clientPresenceManager = mock(ClientPresenceManager.class);
messagesManager = mock(MessagesManager.class);
fcmSender = mock(FcmSender.class);
apnSender = mock(APNSender.class);
messageSender = new MessageSender(mock(ApnFallbackManager.class),
clientPresenceManager,
pushNotificationManager = mock(PushNotificationManager.class);
messageSender = new MessageSender(clientPresenceManager,
messagesManager,
fcmSender,
apnSender,
pushNotificationManager,
mock(PushLatencyManager.class));
when(account.getUuid()).thenReturn(ACCOUNT_UUID);
@@ -80,8 +78,7 @@ class MessageSenderTest {
assertTrue(envelopeArgumentCaptor.getValue().getEphemeral());
verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender);
verifyNoInteractions(pushNotificationManager);
}
@Test
@@ -92,8 +89,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, true);
verify(messagesManager, never()).insert(any(), anyLong(), any());
verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender);
verifyNoInteractions(pushNotificationManager);
}
@Test
@@ -110,8 +106,7 @@ class MessageSenderTest {
assertFalse(envelopeArgumentCaptor.getValue().getEphemeral());
assertEquals(message, envelopeArgumentCaptor.getValue());
verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender);
verifyNoInteractions(pushNotificationManager);
}
@Test
@@ -122,8 +117,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, false);
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verify(fcmSender).sendMessage(any());
verifyNoInteractions(apnSender);
verify(pushNotificationManager).sendNewMessageNotification(account, device.getId());
}
@Test
@@ -134,8 +128,7 @@ class MessageSenderTest {
messageSender.sendMessage(account, device, message, false);
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verifyNoInteractions(fcmSender);
verify(apnSender).sendMessage(any());
verify(pushNotificationManager).sendNewMessageNotification(account, device.getId());
}
@Test
@@ -143,11 +136,11 @@ class MessageSenderTest {
when(clientPresenceManager.isPresent(ACCOUNT_UUID, DEVICE_ID)).thenReturn(false);
when(device.getFetchesMessages()).thenReturn(true);
messageSender.sendMessage(account, device, message, false);
doThrow(NotPushRegisteredException.class)
.when(pushNotificationManager).sendNewMessageNotification(account, DEVICE_ID);
assertDoesNotThrow(() -> messageSender.sendMessage(account, device, message, false));
verify(messagesManager).insert(ACCOUNT_UUID, DEVICE_ID, message);
verifyNoInteractions(fcmSender);
verifyNoInteractions(apnSender);
}
private MessageProtos.Envelope generateRandomMessage() {

View File

@@ -0,0 +1,186 @@
/*
* Copyright 2013-2022 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.push;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.util.Util;
class PushNotificationManagerTest {
private AccountsManager accountsManager;
private APNSender apnSender;
private FcmSender fcmSender;
private ApnFallbackManager apnFallbackManager;
private PushNotificationManager pushNotificationManager;
@BeforeEach
void setUp() {
accountsManager = mock(AccountsManager.class);
apnSender = mock(APNSender.class);
fcmSender = mock(FcmSender.class);
apnFallbackManager = mock(ApnFallbackManager.class);
AccountsHelper.setupMockUpdate(accountsManager);
pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender, apnFallbackManager);
}
@Test
void sendNewMessageNotification() throws NotPushRegisteredException {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
final String deviceToken = "token";
when(device.getId()).thenReturn(Device.MASTER_ID);
when(device.getGcmId()).thenReturn(deviceToken);
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
when(fcmSender.sendNotification(any()))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, null, false)));
pushNotificationManager.sendNewMessageNotification(account, Device.MASTER_ID);
verify(fcmSender).sendNotification(new PushNotification(deviceToken, PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, account, device));
}
@Test
void sendRegistrationChallengeNotification() {
final String deviceToken = "token";
final String challengeToken = "challenge";
when(apnSender.sendNotification(any()))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, null, false)));
pushNotificationManager.sendRegistrationChallengeNotification(deviceToken, PushNotification.TokenType.APN_VOIP, challengeToken);
verify(apnSender).sendNotification(new PushNotification(deviceToken, PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.CHALLENGE, challengeToken, null, null));
}
@Test
void sendRateLimitChallengeNotification() throws NotPushRegisteredException {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
final String deviceToken = "token";
final String challengeToken = "challenge";
when(device.getId()).thenReturn(Device.MASTER_ID);
when(device.getApnId()).thenReturn(deviceToken);
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
when(apnSender.sendNotification(any()))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, null, false)));
pushNotificationManager.sendRateLimitChallengeNotification(account, challengeToken);
verify(apnSender).sendNotification(new PushNotification(deviceToken, PushNotification.TokenType.APN, PushNotification.NotificationType.RATE_LIMIT_CHALLENGE, challengeToken, account, device));
}
@Test
void testSendNotification() {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.MASTER_ID);
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
final PushNotification pushNotification = new PushNotification(
"token", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, account, device);
when(fcmSender.sendNotification(pushNotification))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, null, false)));
pushNotificationManager.sendNotification(pushNotification);
verify(fcmSender).sendNotification(pushNotification);
verifyNoInteractions(apnSender);
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
verifyNoInteractions(apnFallbackManager);
}
@Test
void testSendNotificationApnVoip() {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.MASTER_ID);
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
final PushNotification pushNotification = new PushNotification(
"token", PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, account, device);
when(apnSender.sendNotification(pushNotification))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(true, null, false)));
pushNotificationManager.sendNotification(pushNotification);
verify(apnSender).sendNotification(pushNotification);
verifyNoInteractions(fcmSender);
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
verify(apnFallbackManager).schedule(account, device);
}
@Test
void testSendNotificationUnregisteredFcm() {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.MASTER_ID);
when(device.getGcmId()).thenReturn("token");
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
final PushNotification pushNotification = new PushNotification(
"token", PushNotification.TokenType.FCM, PushNotification.NotificationType.NOTIFICATION, null, account, device);
when(fcmSender.sendNotification(pushNotification))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(false, null, true)));
pushNotificationManager.sendNotification(pushNotification);
verify(accountsManager).updateDevice(eq(account), eq(Device.MASTER_ID), any());
verify(device).setUninstalledFeedbackTimestamp(Util.todayInMillis());
verifyNoInteractions(apnSender);
verifyNoInteractions(apnFallbackManager);
}
@Test
void testSendNotificationUnregisteredApn() {
final Account account = mock(Account.class);
final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.MASTER_ID);
when(account.getDevice(Device.MASTER_ID)).thenReturn(Optional.of(device));
final PushNotification pushNotification = new PushNotification(
"token", PushNotification.TokenType.APN_VOIP, PushNotification.NotificationType.NOTIFICATION, null, account, device);
when(apnSender.sendNotification(pushNotification))
.thenReturn(CompletableFuture.completedFuture(new SendPushNotificationResult(false, null, true)));
pushNotificationManager.sendNotification(pushNotification);
verifyNoInteractions(fcmSender);
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
verify(apnFallbackManager).cancel(account, device);
}
}