mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 09:10:35 +01:00
Refactor scheduled APNs notifications in preparation for future development
This commit is contained in:
@@ -26,7 +26,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
class ApnFallbackManagerTest {
|
||||
class ApnPushNotificationSchedulerTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||
@@ -36,7 +36,7 @@ class ApnFallbackManagerTest {
|
||||
|
||||
private APNSender apnSender;
|
||||
|
||||
private ApnFallbackManager apnFallbackManager;
|
||||
private ApnPushNotificationScheduler apnPushNotificationScheduler;
|
||||
|
||||
private static final UUID ACCOUNT_UUID = UUID.randomUUID();
|
||||
private static final String ACCOUNT_NUMBER = "+18005551234";
|
||||
@@ -62,41 +62,43 @@ class ApnFallbackManagerTest {
|
||||
|
||||
apnSender = mock(APNSender.class);
|
||||
|
||||
apnFallbackManager = new ApnFallbackManager(REDIS_CLUSTER_EXTENSION.getRedisCluster(), apnSender, accountsManager);
|
||||
apnPushNotificationScheduler = new ApnPushNotificationScheduler(REDIS_CLUSTER_EXTENSION.getRedisCluster(), apnSender, accountsManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClusterInsert() {
|
||||
final String endpoint = apnFallbackManager.getEndpointKey(account, device);
|
||||
final String endpoint = apnPushNotificationScheduler.getEndpointKey(account, device);
|
||||
|
||||
assertTrue(apnFallbackManager.getPendingDestinations(SlotHash.getSlot(endpoint), 1).isEmpty());
|
||||
assertTrue(
|
||||
apnPushNotificationScheduler.getPendingDestinationsForRecurringVoipNotifications(SlotHash.getSlot(endpoint), 1).isEmpty());
|
||||
|
||||
apnFallbackManager.schedule(account, device, System.currentTimeMillis() - 30_000);
|
||||
apnPushNotificationScheduler.scheduleRecurringVoipNotification(account, device, System.currentTimeMillis() - 30_000);
|
||||
|
||||
final List<String> pendingDestinations = apnFallbackManager.getPendingDestinations(SlotHash.getSlot(endpoint), 2);
|
||||
final List<String> pendingDestinations = apnPushNotificationScheduler.getPendingDestinationsForRecurringVoipNotifications(SlotHash.getSlot(endpoint), 2);
|
||||
assertEquals(1, pendingDestinations.size());
|
||||
|
||||
final Optional<Pair<String, Long>> maybeUuidAndDeviceId = ApnFallbackManager.getSeparated(
|
||||
final Optional<Pair<String, Long>> maybeUuidAndDeviceId = ApnPushNotificationScheduler.getSeparated(
|
||||
pendingDestinations.get(0));
|
||||
|
||||
assertTrue(maybeUuidAndDeviceId.isPresent());
|
||||
assertEquals(ACCOUNT_UUID.toString(), maybeUuidAndDeviceId.get().first());
|
||||
assertEquals(DEVICE_ID, (long) maybeUuidAndDeviceId.get().second());
|
||||
|
||||
assertTrue(apnFallbackManager.getPendingDestinations(SlotHash.getSlot(endpoint), 1).isEmpty());
|
||||
assertTrue(
|
||||
apnPushNotificationScheduler.getPendingDestinationsForRecurringVoipNotifications(SlotHash.getSlot(endpoint), 1).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessNextSlot() {
|
||||
final ApnFallbackManager.NotificationWorker worker = apnFallbackManager.new NotificationWorker();
|
||||
final ApnPushNotificationScheduler.NotificationWorker worker = apnPushNotificationScheduler.new NotificationWorker();
|
||||
|
||||
apnFallbackManager.schedule(account, device, System.currentTimeMillis() - 30_000);
|
||||
apnPushNotificationScheduler.scheduleRecurringVoipNotification(account, device, System.currentTimeMillis() - 30_000);
|
||||
|
||||
final int slot = SlotHash.getSlot(apnFallbackManager.getEndpointKey(account, device));
|
||||
final int slot = SlotHash.getSlot(apnPushNotificationScheduler.getEndpointKey(account, device));
|
||||
final int previousSlot = (slot + SlotHash.SLOT_COUNT - 1) % SlotHash.SLOT_COUNT;
|
||||
|
||||
REDIS_CLUSTER_EXTENSION.getRedisCluster().withCluster(connection -> connection.sync()
|
||||
.set(ApnFallbackManager.NEXT_SLOT_TO_PERSIST_KEY, String.valueOf(previousSlot)));
|
||||
.set(ApnPushNotificationScheduler.NEXT_SLOT_TO_PERSIST_KEY, String.valueOf(previousSlot)));
|
||||
|
||||
assertEquals(1, worker.processNextSlot());
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos;
|
||||
import org.whispersystems.textsecuregcm.metrics.PushLatencyManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.MessagesManager;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2013-2022 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
|
||||
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicPushLatencyConfiguration;
|
||||
import org.whispersystems.textsecuregcm.push.PushLatencyManager;
|
||||
import org.whispersystems.textsecuregcm.push.PushLatencyManager.PushRecord;
|
||||
import org.whispersystems.textsecuregcm.push.PushLatencyManager.PushType;
|
||||
import org.whispersystems.textsecuregcm.redis.RedisClusterExtension;
|
||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||
|
||||
class PushLatencyManagerTest {
|
||||
|
||||
@RegisterExtension
|
||||
static final RedisClusterExtension REDIS_CLUSTER_EXTENSION = RedisClusterExtension.builder().build();
|
||||
|
||||
private DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
//noinspection unchecked
|
||||
dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
|
||||
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
|
||||
final DynamicPushLatencyConfiguration dynamicPushLatencyConfiguration = mock(DynamicPushLatencyConfiguration.class);
|
||||
|
||||
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
|
||||
when(dynamicConfiguration.getPushLatencyConfiguration()).thenReturn(dynamicPushLatencyConfiguration);
|
||||
when(dynamicPushLatencyConfiguration.getInstrumentedVersions()).thenReturn(Collections.emptyMap());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, false})
|
||||
void testTakeRecord(final boolean isVoip) throws ExecutionException, InterruptedException {
|
||||
final UUID accountUuid = UUID.randomUUID();
|
||||
final long deviceId = 1;
|
||||
|
||||
final Instant pushTimestamp = Instant.now();
|
||||
|
||||
final PushLatencyManager pushLatencyManager = new PushLatencyManager(REDIS_CLUSTER_EXTENSION.getRedisCluster(),
|
||||
dynamicConfigurationManager, Clock.fixed(pushTimestamp, ZoneId.systemDefault()));
|
||||
|
||||
assertNull(pushLatencyManager.takePushRecord(accountUuid, deviceId).get());
|
||||
|
||||
pushLatencyManager.recordPushSent(accountUuid, deviceId, isVoip);
|
||||
|
||||
final PushRecord pushRecord = pushLatencyManager.takePushRecord(accountUuid, deviceId).get();
|
||||
|
||||
assertNotNull(pushRecord);
|
||||
assertEquals(pushTimestamp, pushRecord.getTimestamp());
|
||||
assertEquals(isVoip ? PushType.VOIP : PushType.STANDARD, pushRecord.getPushType());
|
||||
|
||||
assertNull(pushLatencyManager.takePushRecord(accountUuid, deviceId).get());
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -28,7 +29,8 @@ class PushNotificationManagerTest {
|
||||
private AccountsManager accountsManager;
|
||||
private APNSender apnSender;
|
||||
private FcmSender fcmSender;
|
||||
private ApnFallbackManager apnFallbackManager;
|
||||
private ApnPushNotificationScheduler apnPushNotificationScheduler;
|
||||
private PushLatencyManager pushLatencyManager;
|
||||
|
||||
private PushNotificationManager pushNotificationManager;
|
||||
|
||||
@@ -37,11 +39,13 @@ class PushNotificationManagerTest {
|
||||
accountsManager = mock(AccountsManager.class);
|
||||
apnSender = mock(APNSender.class);
|
||||
fcmSender = mock(FcmSender.class);
|
||||
apnFallbackManager = mock(ApnFallbackManager.class);
|
||||
apnPushNotificationScheduler = mock(ApnPushNotificationScheduler.class);
|
||||
pushLatencyManager = mock(PushLatencyManager.class);
|
||||
|
||||
AccountsHelper.setupMockUpdate(accountsManager);
|
||||
|
||||
pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender, apnFallbackManager);
|
||||
pushNotificationManager = new PushNotificationManager(accountsManager, apnSender, fcmSender,
|
||||
apnPushNotificationScheduler, pushLatencyManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,7 +117,7 @@ class PushNotificationManagerTest {
|
||||
verifyNoInteractions(apnSender);
|
||||
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
|
||||
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
|
||||
verifyNoInteractions(apnFallbackManager);
|
||||
verifyNoInteractions(apnPushNotificationScheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,7 +140,7 @@ class PushNotificationManagerTest {
|
||||
verifyNoInteractions(fcmSender);
|
||||
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
|
||||
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
|
||||
verify(apnFallbackManager).schedule(account, device);
|
||||
verify(apnPushNotificationScheduler).scheduleRecurringVoipNotification(account, device);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -159,7 +163,7 @@ class PushNotificationManagerTest {
|
||||
verify(accountsManager).updateDevice(eq(account), eq(Device.MASTER_ID), any());
|
||||
verify(device).setUninstalledFeedbackTimestamp(Util.todayInMillis());
|
||||
verifyNoInteractions(apnSender);
|
||||
verifyNoInteractions(apnFallbackManager);
|
||||
verifyNoInteractions(apnPushNotificationScheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,6 +185,22 @@ class PushNotificationManagerTest {
|
||||
verifyNoInteractions(fcmSender);
|
||||
verify(accountsManager, never()).updateDevice(eq(account), eq(Device.MASTER_ID), any());
|
||||
verify(device, never()).setUninstalledFeedbackTimestamp(Util.todayInMillis());
|
||||
verify(apnFallbackManager).cancel(account, device);
|
||||
verify(apnPushNotificationScheduler).cancelRecurringVoipNotification(account, device);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMessagesRetrieved() {
|
||||
final UUID accountIdentifier = UUID.randomUUID();
|
||||
final Account account = mock(Account.class);
|
||||
final Device device = mock(Device.class);
|
||||
final String userAgent = "User-Agent";
|
||||
|
||||
when(account.getUuid()).thenReturn(accountIdentifier);
|
||||
when(device.getId()).thenReturn(Device.MASTER_ID);
|
||||
|
||||
pushNotificationManager.handleMessagesRetrieved(account, device, userAgent);
|
||||
|
||||
verify(pushLatencyManager).recordQueueRead(accountIdentifier, Device.MASTER_ID, userAgent);
|
||||
verify(apnPushNotificationScheduler).cancelRecurringVoipNotification(account, device);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user