package cc.blynk.integration.tcp; import cc.blynk.integration.IntegrationBase; import cc.blynk.integration.model.tcp.ClientPair; import cc.blynk.integration.model.tcp.TestAppClient; import cc.blynk.integration.model.tcp.TestHardClient; import cc.blynk.server.application.AppServer; import cc.blynk.server.core.BaseServer; import cc.blynk.server.core.model.Profile; import cc.blynk.server.core.model.widgets.notifications.Notification; import cc.blynk.server.core.protocol.enums.Command; import cc.blynk.server.core.protocol.model.messages.ResponseMessage; import cc.blynk.server.core.protocol.model.messages.ResponseWithBodyMessage; import cc.blynk.server.hardware.HardwareServer; import cc.blynk.server.notifications.push.android.AndroidGCMMessage; import cc.blynk.server.notifications.push.enums.Priority; import io.netty.channel.ChannelFuture; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.runners.MockitoJUnitRunner; import java.util.Map; import static cc.blynk.server.core.protocol.enums.Response.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** * The Blynk Project. * Created by Dmitriy Dumanskiy. * Created on 2/2/2015. * */ @RunWith(MockitoJUnitRunner.class) public class NotificationsLogicTest extends IntegrationBase { private BaseServer appServer; private BaseServer hardwareServer; private ClientPair clientPair; @Before public void init() throws Exception { this.hardwareServer = new HardwareServer(holder).start(); this.appServer = new AppServer(holder).start(); this.clientPair = initAppAndHardPair(); } @After public void shutdown() { this.appServer.close(); this.hardwareServer.close(); this.clientPair.stop(); } @Test public void addPushTokenWrongInput() throws Exception { TestAppClient appClient = new TestAppClient("localhost", tcpAppPort, properties); appClient.start(); appClient.send("register test@test.com 1"); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); appClient.send("login test@test.com 1 Android" + "\0" + "RC13"); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(2, OK))); appClient.send("createDash {\"id\":1, \"createdAt\":1, \"name\":\"test board\"}\""); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(3, OK))); appClient.send("addPushToken 1\0uid\0token"); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(4, NOT_ALLOWED))); } @Test public void addPushTokenWorksForAndroid() throws Exception { clientPair.appClient.send("addPushToken 1\0uid1\0token1"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); clientPair.appClient.send("loadProfileGzipped"); Profile profile = parseProfile(clientPair.appClient.getBody(2)); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); assertNotNull(notification); assertEquals(2, notification.androidTokens.size()); assertEquals(0, notification.iOSTokens.size()); assertTrue(notification.androidTokens.containsKey("uid1")); assertTrue(notification.androidTokens.containsValue("token1")); } @Test public void addPushTokenNotOverridedOnProfileSave() throws Exception { clientPair.appClient.send("addPushToken 1\0uid1\0token1"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); clientPair.appClient.send("loadProfileGzipped"); Profile profile = parseProfile(clientPair.appClient.getBody(2)); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); assertNotNull(notification); assertEquals(2, notification.androidTokens.size()); assertEquals(0, notification.iOSTokens.size()); assertTrue(notification.androidTokens.containsKey("uid1")); assertTrue(notification.androidTokens.containsValue("token1")); clientPair.appClient.send("updateDash " + profile.getDashById(1).toString()); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(3, OK))); clientPair.appClient.send("loadProfileGzipped"); profile = parseProfile(clientPair.appClient.getBody(4)); notification = profile.getDashById(1).getWidgetByType(Notification.class); assertNotNull(notification); assertEquals(2, notification.androidTokens.size()); assertEquals(0, notification.iOSTokens.size()); assertTrue(notification.androidTokens.containsKey("uid1")); assertTrue(notification.androidTokens.containsValue("token1")); } @Test public void addPushTokenWorksForIos() throws Exception { TestAppClient appClient = new TestAppClient("localhost", tcpAppPort, properties); appClient.start(); appClient.send("login " + DEFAULT_TEST_USER +" 1 iOS" + "\0" + "1.10.2"); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); appClient.send("addPushToken 1\0uid2\0token2"); verify(appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(2, OK))); appClient.reset(); appClient.send("loadProfileGzipped"); Profile profile = parseProfile(appClient.getBody()); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); assertNotNull(notification); assertEquals(1, notification.androidTokens.size()); assertEquals(1, notification.iOSTokens.size()); Map.Entry<String, String> entry = notification.iOSTokens.entrySet().iterator().next(); assertEquals("uid2", entry.getKey()); assertEquals("token2", entry.getValue()); } @Test public void testHardwareDeviceWentOffline() throws Exception { Profile profile = parseProfile(readTestUserProfile()); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); notification.notifyWhenOffline = false; clientPair.appClient.send("updateDash " + profile.getDashById(1).toString()); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); ChannelFuture channelFuture = clientPair.hardwareClient.stop(); channelFuture.await(); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseWithBodyMessage(0, Command.RESPONSE, DEVICE_WENT_OFFLINE, 1))); } @Test public void testHardwareDeviceWentOfflineAndPushWorks() throws Exception { Profile profile = parseProfile(readTestUserProfile()); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); notification.notifyWhenOffline = true; clientPair.appClient.send("updateDash " + profile.getDashById(1).toString()); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); ChannelFuture channelFuture = clientPair.hardwareClient.stop(); channelFuture.await(); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, timeout(500).times(1)).send(objectArgumentCaptor.capture(), any(), any()); AndroidGCMMessage message = objectArgumentCaptor.getValue(); String expectedJson = new AndroidGCMMessage("token", Priority.normal, "Your UNO went offline. \"My Dashboard\" project is disconnected.", 1).toJson(); assertEquals(expectedJson, message.toJson()); } @Test public void testHardwareDeviceWentOfflineAndPushDelayedWorks() throws Exception { Profile profile = parseProfile(readTestUserProfile()); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); notification.notifyWhenOffline = true; notification.notifyWhenOfflineIgnorePeriod = 1000; long now = System.currentTimeMillis(); clientPair.appClient.send("updateDash " + profile.getDashById(1).toString()); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); ChannelFuture channelFuture = clientPair.hardwareClient.stop(); channelFuture.await(); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, timeout(1500).times(1)).send(objectArgumentCaptor.capture(), any(), any()); AndroidGCMMessage message = objectArgumentCaptor.getValue(); assertTrue(System.currentTimeMillis() - now > notification.notifyWhenOfflineIgnorePeriod ); String expectedJson = new AndroidGCMMessage("token", Priority.normal, "Your UNO went offline. \"My Dashboard\" project is disconnected.", 1).toJson(); assertEquals(expectedJson, message.toJson()); } @Test public void testHardwareDeviceWentOfflineAndPushDelayedNotTriggeredDueToReconnect() throws Exception { Profile profile = parseProfile(readTestUserProfile()); Notification notification = profile.getDashById(1).getWidgetByType(Notification.class); notification.notifyWhenOffline = true; notification.notifyWhenOfflineIgnorePeriod = 1000; clientPair.appClient.send("updateDash " + profile.getDashById(1).toString()); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); ChannelFuture channelFuture = clientPair.hardwareClient.stop(); channelFuture.await(); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(2); TestHardClient newHardClient = new TestHardClient("localhost", tcpHardPort); newHardClient.start(); newHardClient.send("login " + token); verify(newHardClient.responseMock, timeout(500)).channelRead(any(), eq(ok(1))); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, after(1500).never()).send(objectArgumentCaptor.capture(), any(), any()); } @Test public void testCreateNewNotificationWidget() throws Exception { clientPair.appClient.send("deleteWidget 1\0" + "9"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(1))); clientPair.appClient.send("createWidget 1\0{\"id\":9, \"x\":1, \"y\":1, \"width\":1, \"height\":1, \"type\":\"NOTIFICATION\", \"notifyWhenOfflineIgnorePeriod\":0, \"priority\":\"high\", \"notifyWhenOffline\":true}"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(2))); clientPair.appClient.send("addPushToken 1\0uid1\0token1"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(3))); clientPair.appClient.send("updateWidget 1\0{\"id\":9, \"x\":1, \"y\":1, \"width\":1, \"height\":1, \"type\":\"NOTIFICATION\", \"notifyWhenOfflineIgnorePeriod\":0, \"priority\":\"high\", \"notifyWhenOffline\":false}"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(2))); clientPair.hardwareClient.send("push 123"); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, timeout(500).times(1)).send(objectArgumentCaptor.capture(), any(), any()); AndroidGCMMessage message = objectArgumentCaptor.getValue(); String expectedJson = new AndroidGCMMessage("token1", Priority.high, "123", 1).toJson(); assertEquals(expectedJson, message.toJson()); } @Test public void testPushWhenHardwareOffline() throws Exception { ChannelFuture channelFuture = clientPair.hardwareClient.stop(); channelFuture.await(); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, timeout(500).times(1)).send(objectArgumentCaptor.capture(), any(), any()); AndroidGCMMessage message = objectArgumentCaptor.getValue(); String expectedJson = new AndroidGCMMessage("token", Priority.normal, "Your UNO went offline. \"My Dashboard\" project is disconnected.", 1).toJson(); assertEquals(expectedJson, message.toJson()); } @Test public void testPushHandler() throws Exception { clientPair.hardwareClient.send("push Yo!"); ArgumentCaptor<AndroidGCMMessage> objectArgumentCaptor = ArgumentCaptor.forClass(AndroidGCMMessage.class); verify(gcmWrapper, timeout(500).times(1)).send(objectArgumentCaptor.capture(), any(), any()); AndroidGCMMessage message = objectArgumentCaptor.getValue(); String expectedJson = new AndroidGCMMessage("token", Priority.normal, "Yo!", 1).toJson(); assertEquals(expectedJson, message.toJson()); } }