@Test
  public void testOnlineSend() throws Exception {
    MessagesManager storedMessages = mock(MessagesManager.class);
    OutgoingMessageSignal firstMessage =
        OutgoingMessageSignal.newBuilder()
            .setMessage(ByteString.copyFrom("first".getBytes()))
            .setSource("sender1")
            .setTimestamp(System.currentTimeMillis())
            .setSourceDevice(1)
            .setType(OutgoingMessageSignal.Type.CIPHERTEXT_VALUE)
            .build();

    OutgoingMessageSignal secondMessage =
        OutgoingMessageSignal.newBuilder()
            .setMessage(ByteString.copyFrom("second".getBytes()))
            .setSource("sender2")
            .setTimestamp(System.currentTimeMillis())
            .setSourceDevice(2)
            .setType(OutgoingMessageSignal.Type.CIPHERTEXT_VALUE)
            .build();

    List<OutgoingMessageEntity> pendingMessages = new LinkedList<>();

    when(device.getId()).thenReturn(2L);
    when(device.getSignalingKey()).thenReturn(Base64.encodeBytes(new byte[52]));

    when(account.getAuthenticatedDevice()).thenReturn(Optional.of(device));
    when(account.getNumber()).thenReturn("+14152222222");

    final Device sender1device = mock(Device.class);

    Set<Device> sender1devices =
        new HashSet<Device>() {
          {
            add(sender1device);
          }
        };

    Account sender1 = mock(Account.class);
    when(sender1.getDevices()).thenReturn(sender1devices);

    when(accountsManager.get("sender1")).thenReturn(Optional.of(sender1));
    when(accountsManager.get("sender2")).thenReturn(Optional.<Account>absent());

    when(storedMessages.getMessagesForDevice(account.getNumber(), device.getId()))
        .thenReturn(pendingMessages);

    final List<SettableFuture<WebSocketResponseMessage>> futures = new LinkedList<>();
    final WebSocketClient client = mock(WebSocketClient.class);

    when(client.sendRequest(eq("PUT"), eq("/api/v1/message"), any(Optional.class)))
        .thenAnswer(
            new Answer<SettableFuture<WebSocketResponseMessage>>() {
              @Override
              public SettableFuture<WebSocketResponseMessage> answer(
                  InvocationOnMock invocationOnMock) throws Throwable {
                SettableFuture<WebSocketResponseMessage> future = SettableFuture.create();
                futures.add(future);
                return future;
              }
            });

    WebsocketAddress websocketAddress = new WebsocketAddress(account.getNumber(), device.getId());
    WebSocketConnection connection =
        new WebSocketConnection(pushSender, receiptSender, storedMessages, account, device, client);

    connection.onDispatchSubscribed(websocketAddress.serialize());
    connection.onDispatchMessage(
        websocketAddress.serialize(),
        PubSubProtos.PubSubMessage.newBuilder()
            .setType(PubSubProtos.PubSubMessage.Type.DELIVER)
            .setContent(ByteString.copyFrom(firstMessage.toByteArray()))
            .build()
            .toByteArray());

    connection.onDispatchMessage(
        websocketAddress.serialize(),
        PubSubProtos.PubSubMessage.newBuilder()
            .setType(PubSubProtos.PubSubMessage.Type.DELIVER)
            .setContent(ByteString.copyFrom(secondMessage.toByteArray()))
            .build()
            .toByteArray());

    verify(client, times(2)).sendRequest(eq("PUT"), eq("/api/v1/message"), any(Optional.class));

    assertEquals(futures.size(), 2);

    WebSocketResponseMessage response = mock(WebSocketResponseMessage.class);
    when(response.getStatus()).thenReturn(200);
    futures.get(1).set(response);
    futures.get(0).setException(new IOException());

    verify(receiptSender, times(1))
        .sendReceipt(
            eq(account),
            eq("sender2"),
            eq(secondMessage.getTimestamp()),
            eq(Optional.<String>absent()));
    verify(pushSender, times(1))
        .sendMessage(eq(account), eq(device), any(OutgoingMessageSignal.class));

    connection.onDispatchUnsubscribed(websocketAddress.serialize());
    verify(client).close(anyInt(), anyString());
  }
  @Test
  public void testOpen() throws Exception {
    MessagesManager storedMessages = mock(MessagesManager.class);

    List<OutgoingMessageEntity> outgoingMessages =
        new LinkedList<OutgoingMessageEntity>() {
          {
            add(createMessage(1L, "sender1", 1111, false, "first"));
            add(createMessage(2L, "sender1", 2222, false, "second"));
            add(createMessage(3L, "sender2", 3333, false, "third"));
          }
        };

    when(device.getId()).thenReturn(2L);
    when(device.getSignalingKey()).thenReturn(Base64.encodeBytes(new byte[52]));

    when(account.getAuthenticatedDevice()).thenReturn(Optional.of(device));
    when(account.getNumber()).thenReturn("+14152222222");

    final Device sender1device = mock(Device.class);

    Set<Device> sender1devices =
        new HashSet<Device>() {
          {
            add(sender1device);
          }
        };

    Account sender1 = mock(Account.class);
    when(sender1.getDevices()).thenReturn(sender1devices);

    when(accountsManager.get("sender1")).thenReturn(Optional.of(sender1));
    when(accountsManager.get("sender2")).thenReturn(Optional.<Account>absent());

    when(storedMessages.getMessagesForDevice(account.getNumber(), device.getId()))
        .thenReturn(outgoingMessages);

    final List<SettableFuture<WebSocketResponseMessage>> futures = new LinkedList<>();
    final WebSocketClient client = mock(WebSocketClient.class);

    when(client.sendRequest(eq("PUT"), eq("/api/v1/message"), any(Optional.class)))
        .thenAnswer(
            new Answer<SettableFuture<WebSocketResponseMessage>>() {
              @Override
              public SettableFuture<WebSocketResponseMessage> answer(
                  InvocationOnMock invocationOnMock) throws Throwable {
                SettableFuture<WebSocketResponseMessage> future = SettableFuture.create();
                futures.add(future);
                return future;
              }
            });

    WebsocketAddress websocketAddress = new WebsocketAddress(account.getNumber(), device.getId());
    WebSocketConnection connection =
        new WebSocketConnection(pushSender, receiptSender, storedMessages, account, device, client);

    connection.onDispatchSubscribed(websocketAddress.serialize());
    verify(client, times(3)).sendRequest(eq("PUT"), eq("/api/v1/message"), any(Optional.class));

    assertTrue(futures.size() == 3);

    WebSocketResponseMessage response = mock(WebSocketResponseMessage.class);
    when(response.getStatus()).thenReturn(200);
    futures.get(1).set(response);

    futures.get(0).setException(new IOException());
    futures.get(2).setException(new IOException());

    verify(storedMessages, times(1)).delete(eq(account.getNumber()), eq(2L));
    verify(receiptSender, times(1))
        .sendReceipt(eq(account), eq("sender1"), eq(2222L), eq(Optional.<String>absent()));

    connection.onDispatchUnsubscribed(websocketAddress.serialize());
    verify(client).close(anyInt(), anyString());
  }