mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-26 18:23:16 +01:00
Pull GCM/APN senders into service
// FREEBIE
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
package org.whispersystems.textsecuregcm.tests.push;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.notnoop.apns.ApnsService;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.push.APNSender;
|
||||
import org.whispersystems.textsecuregcm.push.ApnMessage;
|
||||
import org.whispersystems.textsecuregcm.push.TransientPushFailureException;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.*;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
public class APNSenderTest {
|
||||
|
||||
private static final String DESTINATION_NUMBER = "+14151231234";
|
||||
private static final String DESTINATION_APN_ID = "foo";
|
||||
|
||||
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
private final JedisPool jedisPool = mock(JedisPool.class);
|
||||
private final Jedis jedis = mock(Jedis.class);
|
||||
private final ApnsService voipService = mock(ApnsService.class);
|
||||
private final ApnsService apnsService = mock(ApnsService.class);
|
||||
|
||||
private final Account destinationAccount = mock(Account.class);
|
||||
private final Device destinationDevice = mock(Device.class );
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
|
||||
when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID);
|
||||
when(accountsManager.get(DESTINATION_NUMBER)).thenReturn(Optional.of(destinationAccount));
|
||||
|
||||
when(jedisPool.getResource()).thenReturn(jedis);
|
||||
when(jedis.get("APN-" + DESTINATION_APN_ID)).thenReturn(DESTINATION_NUMBER + "." + 1);
|
||||
|
||||
when(voipService.getInactiveDevices()).thenReturn(new HashMap<String, Date>() {{
|
||||
put(DESTINATION_APN_ID, new Date(System.currentTimeMillis()));
|
||||
}});
|
||||
when(apnsService.getInactiveDevices()).thenReturn(new HashMap<String, Date>());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendVoip() throws TransientPushFailureException {
|
||||
APNSender apnSender = new APNSender(accountsManager, jedisPool, apnsService, voipService, false, false);
|
||||
|
||||
ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30);
|
||||
apnSender.sendMessage(message);
|
||||
|
||||
|
||||
verify(jedis, times(1)).set(eq("APN-" + DESTINATION_APN_ID.toLowerCase()), eq(DESTINATION_NUMBER + "." + 1));
|
||||
verify(voipService, times(1)).push(eq(DESTINATION_APN_ID), eq(message.getMessage()), eq(new Date(30)));
|
||||
verifyNoMoreInteractions(apnsService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendApns() throws TransientPushFailureException {
|
||||
APNSender apnSender = new APNSender(accountsManager, jedisPool, apnsService, voipService, false, false);
|
||||
|
||||
ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", false, 30);
|
||||
apnSender.sendMessage(message);
|
||||
|
||||
verify(jedis, times(1)).set(eq("APN-" + DESTINATION_APN_ID.toLowerCase()), eq(DESTINATION_NUMBER + "." + 1));
|
||||
verify(apnsService, times(1)).push(eq(DESTINATION_APN_ID), eq(message.getMessage()), eq(new Date(30)));
|
||||
verifyNoMoreInteractions(voipService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeedbackUnregistered() {
|
||||
APNSender apnSender = new APNSender(accountsManager, jedisPool, apnsService, voipService, false, false);
|
||||
apnSender.checkFeedback();
|
||||
|
||||
verify(jedis, times(1)).get(eq("APN-" +DESTINATION_APN_ID));
|
||||
verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER));
|
||||
verify(destinationDevice, times(1)).setApnId(eq((String)null));
|
||||
verify(accountsManager, times(1)).update(eq(destinationAccount));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,10 +2,10 @@ package org.whispersystems.textsecuregcm.tests.push;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.whispersystems.textsecuregcm.entities.ApnMessage;
|
||||
import org.whispersystems.textsecuregcm.push.APNSender;
|
||||
import org.whispersystems.textsecuregcm.push.ApnFallbackManager;
|
||||
import org.whispersystems.textsecuregcm.push.ApnFallbackManager.ApnFallbackTask;
|
||||
import org.whispersystems.textsecuregcm.push.PushServiceClient;
|
||||
import org.whispersystems.textsecuregcm.push.ApnMessage;
|
||||
import org.whispersystems.textsecuregcm.storage.PubSubManager;
|
||||
import org.whispersystems.textsecuregcm.storage.PubSubProtos;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
@@ -14,23 +14,21 @@ import org.whispersystems.textsecuregcm.websocket.WebsocketAddress;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class ApnFallbackManagerTest {
|
||||
|
||||
@Test
|
||||
public void testFullFallback() throws Exception {
|
||||
PushServiceClient pushServiceClient = mock(PushServiceClient.class);
|
||||
PubSubManager pubSubManager = mock(PubSubManager.class);
|
||||
WebsocketAddress address = new WebsocketAddress("+14152222223", 1L);
|
||||
WebSocketConnectionInfo info = new WebSocketConnectionInfo(address);
|
||||
ApnMessage message = new ApnMessage("bar", "123", 1, "hmm", true, 1111);
|
||||
ApnFallbackTask task = new ApnFallbackTask("foo", "voipfoo", message, 500, 0);
|
||||
APNSender apnSender = mock(APNSender.class );
|
||||
PubSubManager pubSubManager = mock(PubSubManager.class);
|
||||
WebsocketAddress address = new WebsocketAddress("+14152222223", 1L);
|
||||
WebSocketConnectionInfo info = new WebSocketConnectionInfo(address);
|
||||
ApnMessage message = new ApnMessage("bar", "123", 1, "hmm", true, 1111);
|
||||
ApnFallbackTask task = new ApnFallbackTask("foo", "voipfoo", message, 500, 0);
|
||||
|
||||
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushServiceClient, pubSubManager);
|
||||
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(apnSender, pubSubManager);
|
||||
apnFallbackManager.start();
|
||||
|
||||
apnFallbackManager.schedule(address, task);
|
||||
@@ -38,7 +36,7 @@ public class ApnFallbackManagerTest {
|
||||
Util.sleep(1100);
|
||||
|
||||
ArgumentCaptor<ApnMessage> captor = ArgumentCaptor.forClass(ApnMessage.class);
|
||||
verify(pushServiceClient, times(2)).send(captor.capture());
|
||||
verify(apnSender, times(2)).sendMessage(captor.capture());
|
||||
verify(pubSubManager).unsubscribe(eq(info), eq(apnFallbackManager));
|
||||
|
||||
List<ApnMessage> arguments = captor.getAllValues();
|
||||
@@ -56,7 +54,7 @@ public class ApnFallbackManagerTest {
|
||||
|
||||
@Test
|
||||
public void testNoFallback() throws Exception {
|
||||
PushServiceClient pushServiceClient = mock(PushServiceClient.class);
|
||||
APNSender pushServiceClient = mock(APNSender.class);
|
||||
PubSubManager pubSubManager = mock(PubSubManager.class);
|
||||
WebsocketAddress address = new WebsocketAddress("+14152222222", 1);
|
||||
WebSocketConnectionInfo info = new WebSocketConnectionInfo(address);
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package org.whispersystems.textsecuregcm.tests.push;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Matchers;
|
||||
import org.whispersystems.gcm.server.Message;
|
||||
import org.whispersystems.gcm.server.Result;
|
||||
import org.whispersystems.gcm.server.Sender;
|
||||
import org.whispersystems.textsecuregcm.push.GCMSender;
|
||||
import org.whispersystems.textsecuregcm.push.GcmMessage;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.tests.util.SynchronousExecutorService;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class GCMSenderTest {
|
||||
|
||||
@Test
|
||||
public void testSendMessage() {
|
||||
AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
Sender sender = mock(Sender.class );
|
||||
Result successResult = mock(Result.class );
|
||||
SynchronousExecutorService executorService = new SynchronousExecutorService();
|
||||
|
||||
when(successResult.isInvalidRegistrationId()).thenReturn(false);
|
||||
when(successResult.isUnregistered()).thenReturn(false);
|
||||
when(successResult.hasCanonicalRegistrationId()).thenReturn(false);
|
||||
when(successResult.isSuccess()).thenReturn(true);
|
||||
|
||||
GcmMessage message = new GcmMessage("foo", "+12223334444", 1, false);
|
||||
GCMSender gcmSender = new GCMSender(accountsManager, sender, executorService);
|
||||
|
||||
SettableFuture<Result> successFuture = SettableFuture.create();
|
||||
successFuture.set(successResult);
|
||||
|
||||
when(sender.send(any(Message.class), Matchers.anyObject())).thenReturn(successFuture);
|
||||
when(successResult.getContext()).thenReturn(message);
|
||||
|
||||
gcmSender.sendMessage(message);
|
||||
|
||||
verify(sender, times(1)).send(any(Message.class), eq(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendError() {
|
||||
String destinationNumber = "+12223334444";
|
||||
String gcmId = "foo";
|
||||
|
||||
AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
Sender sender = mock(Sender.class );
|
||||
Result invalidResult = mock(Result.class );
|
||||
SynchronousExecutorService executorService = new SynchronousExecutorService();
|
||||
|
||||
Account destinationAccount = mock(Account.class);
|
||||
Device destinationDevice = mock(Device.class );
|
||||
|
||||
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
|
||||
when(accountsManager.get(destinationNumber)).thenReturn(Optional.of(destinationAccount));
|
||||
when(destinationDevice.getGcmId()).thenReturn(gcmId);
|
||||
|
||||
when(invalidResult.isInvalidRegistrationId()).thenReturn(true);
|
||||
when(invalidResult.isUnregistered()).thenReturn(false);
|
||||
when(invalidResult.hasCanonicalRegistrationId()).thenReturn(false);
|
||||
when(invalidResult.isSuccess()).thenReturn(true);
|
||||
|
||||
GcmMessage message = new GcmMessage(gcmId, destinationNumber, 1, false);
|
||||
GCMSender gcmSender = new GCMSender(accountsManager, sender, executorService);
|
||||
|
||||
SettableFuture<Result> invalidFuture = SettableFuture.create();
|
||||
invalidFuture.set(invalidResult);
|
||||
|
||||
when(sender.send(any(Message.class), Matchers.anyObject())).thenReturn(invalidFuture);
|
||||
when(invalidResult.getContext()).thenReturn(message);
|
||||
|
||||
gcmSender.sendMessage(message);
|
||||
|
||||
verify(sender, times(1)).send(any(Message.class), eq(message));
|
||||
verify(accountsManager, times(1)).get(eq(destinationNumber));
|
||||
verify(accountsManager, times(1)).update(eq(destinationAccount));
|
||||
verify(destinationDevice, times(1)).setGcmId(eq((String)null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanonicalId() {
|
||||
String destinationNumber = "+12223334444";
|
||||
String gcmId = "foo";
|
||||
String canonicalId = "bar";
|
||||
|
||||
AccountsManager accountsManager = mock(AccountsManager.class);
|
||||
Sender sender = mock(Sender.class );
|
||||
Result canonicalResult = mock(Result.class );
|
||||
SynchronousExecutorService executorService = new SynchronousExecutorService();
|
||||
|
||||
Account destinationAccount = mock(Account.class);
|
||||
Device destinationDevice = mock(Device.class );
|
||||
|
||||
when(destinationAccount.getDevice(1)).thenReturn(Optional.of(destinationDevice));
|
||||
when(accountsManager.get(destinationNumber)).thenReturn(Optional.of(destinationAccount));
|
||||
when(destinationDevice.getGcmId()).thenReturn(gcmId);
|
||||
|
||||
when(canonicalResult.isInvalidRegistrationId()).thenReturn(false);
|
||||
when(canonicalResult.isUnregistered()).thenReturn(false);
|
||||
when(canonicalResult.hasCanonicalRegistrationId()).thenReturn(true);
|
||||
when(canonicalResult.isSuccess()).thenReturn(false);
|
||||
when(canonicalResult.getCanonicalRegistrationId()).thenReturn(canonicalId);
|
||||
|
||||
GcmMessage message = new GcmMessage(gcmId, destinationNumber, 1, false);
|
||||
GCMSender gcmSender = new GCMSender(accountsManager, sender, executorService);
|
||||
|
||||
SettableFuture<Result> invalidFuture = SettableFuture.create();
|
||||
invalidFuture.set(canonicalResult);
|
||||
|
||||
when(sender.send(any(Message.class), Matchers.anyObject())).thenReturn(invalidFuture);
|
||||
when(canonicalResult.getContext()).thenReturn(message);
|
||||
|
||||
gcmSender.sendMessage(message);
|
||||
|
||||
verify(sender, times(1)).send(any(Message.class), eq(message));
|
||||
verify(accountsManager, times(1)).get(eq(destinationNumber));
|
||||
verify(accountsManager, times(1)).update(eq(destinationAccount));
|
||||
verify(destinationDevice, times(1)).setGcmId(eq(canonicalId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.whispersystems.textsecuregcm.tests.util;
|
||||
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class SynchronousExecutorService implements ExecutorService {
|
||||
|
||||
private boolean shutdown = false;
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
shutdown = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shutdown = true;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
SettableFuture<T> future = null;
|
||||
try {
|
||||
future = SettableFuture.create();
|
||||
future.set(task.call());
|
||||
} catch (Throwable e) {
|
||||
future.setException(e);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Runnable task, T result) {
|
||||
SettableFuture<T> future = SettableFuture.create();
|
||||
task.run();
|
||||
|
||||
future.set(result);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> submit(Runnable task) {
|
||||
SettableFuture future = SettableFuture.create();
|
||||
task.run();
|
||||
future.set(null);
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
|
||||
List<Future<T>> results = new LinkedList<>();
|
||||
for (Callable<T> callable : tasks) {
|
||||
SettableFuture<T> future = SettableFuture.create();
|
||||
try {
|
||||
future.set(callable.call());
|
||||
} catch (Throwable e) {
|
||||
future.setException(e);
|
||||
}
|
||||
results.add(future);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return invokeAll(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
command.run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user