@Timed
 @DELETE
 @Path("/apn/")
 public void deleteApnRegistrationId(@Auth Account account) {
   Device device = account.getAuthenticatedDevice().get();
   device.setApnId(null);
   accounts.update(account);
 }
  @Timed
  @PUT
  @Consumes(MediaType.APPLICATION_JSON)
  @Path("/code/{verification_code}")
  public void verifyAccount(
      @PathParam("verification_code") String verificationCode,
      @HeaderParam("Authorization") String authorizationHeader,
      @Valid AccountAttributes accountAttributes)
      throws RateLimitExceededException {
    try {
      AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
      String number = header.getNumber();
      String password = header.getPassword();

      rateLimiters.getVerifyLimiter().validate(number);

      Optional<String> storedVerificationCode = pendingAccounts.getCodeForNumber(number);

      if (!storedVerificationCode.isPresent()
          || !verificationCode.equals(storedVerificationCode.get())) {
        throw new WebApplicationException(Response.status(403).build());
      }

      Device device = new Device();
      device.setId(Device.MASTER_ID);
      device.setAuthenticationCredentials(new AuthenticationCredentials(password));
      device.setSignalingKey(accountAttributes.getSignalingKey());
      device.setFetchesMessages(accountAttributes.getFetchesMessages());
      device.setRegistrationId(accountAttributes.getRegistrationId());

      Account account = new Account();
      account.setNumber(number);
      account.setSupportsSms(accountAttributes.getSupportsSms());
      account.addDevice(device);

      accounts.create(account);

      pendingAccounts.remove(number);

      logger.debug("Stored device...");
    } catch (InvalidAuthorizationHeaderException e) {
      logger.info("Bad Authorization Header", e);
      throw new WebApplicationException(Response.status(401).build());
    }
  }
 @Timed
 @PUT
 @Path("/apn/")
 @Consumes(MediaType.APPLICATION_JSON)
 public void setApnRegistrationId(@Auth Account account, @Valid ApnRegistrationId registrationId) {
   Device device = account.getAuthenticatedDevice().get();
   device.setApnId(registrationId.getApnRegistrationId());
   device.setGcmId(null);
   accounts.update(account);
 }
  @Test
  public void testCredentials() throws Exception {
    MessagesManager storedMessages = mock(MessagesManager.class);
    WebSocketAccountAuthenticator webSocketAuthenticator =
        new WebSocketAccountAuthenticator(accountAuthenticator);
    AuthenticatedConnectListener connectListener =
        new AuthenticatedConnectListener(
            accountsManager,
            pushSender,
            receiptSender,
            storedMessages,
            pubSubManager,
            apnFallbackManager);
    WebSocketSessionContext sessionContext = mock(WebSocketSessionContext.class);

    when(accountAuthenticator.authenticate(eq(new BasicCredentials(VALID_USER, VALID_PASSWORD))))
        .thenReturn(Optional.of(account));

    when(accountAuthenticator.authenticate(
            eq(new BasicCredentials(INVALID_USER, INVALID_PASSWORD))))
        .thenReturn(Optional.<Account>absent());

    when(account.getAuthenticatedDevice()).thenReturn(Optional.of(device));

    when(upgradeRequest.getParameterMap())
        .thenReturn(
            new HashMap<String, String[]>() {
              {
                put("login", new String[] {VALID_USER});
                put("password", new String[] {VALID_PASSWORD});
              }
            });

    Optional<Account> account = webSocketAuthenticator.authenticate(upgradeRequest);
    when(sessionContext.getAuthenticated(Account.class)).thenReturn(account);

    connectListener.onWebSocketConnect(sessionContext);

    verify(sessionContext).addListener(any(WebSocketSessionContext.WebSocketEventListener.class));

    when(upgradeRequest.getParameterMap())
        .thenReturn(
            new HashMap<String, String[]>() {
              {
                put("login", new String[] {INVALID_USER});
                put("password", new String[] {INVALID_PASSWORD});
              }
            });

    account = webSocketAuthenticator.authenticate(upgradeRequest);
    assertFalse(account.isPresent());
  }
  @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());
  }