package cc.blynk.integration.http; import cc.blynk.integration.IntegrationBase; import cc.blynk.integration.model.tcp.ClientPair; import cc.blynk.integration.model.tcp.TestHardClient; import cc.blynk.integration.tcp.EventorTest; import cc.blynk.server.api.http.HttpAPIServer; import cc.blynk.server.application.AppServer; import cc.blynk.server.core.BaseServer; import cc.blynk.server.core.model.Pin; import cc.blynk.server.core.model.device.Device; import cc.blynk.server.core.model.enums.PinType; import cc.blynk.server.core.model.widgets.controls.Timer; import cc.blynk.server.core.model.widgets.others.eventor.Eventor; import cc.blynk.server.core.model.widgets.others.eventor.Rule; import cc.blynk.server.core.model.widgets.others.eventor.TimerTime; import cc.blynk.server.core.model.widgets.others.eventor.model.action.BaseAction; import cc.blynk.server.core.model.widgets.others.eventor.model.action.SetPinAction; import cc.blynk.server.core.model.widgets.others.rtc.RTC; import cc.blynk.server.core.protocol.model.messages.ResponseMessage; import cc.blynk.server.core.protocol.model.messages.appllication.CreateDevice; import cc.blynk.server.hardware.HardwareServer; import cc.blynk.utils.DateTimeUtils; import cc.blynk.utils.JsonParser; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; import java.time.LocalTime; import java.time.ZoneId; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static cc.blynk.server.core.protocol.enums.Command.HARDWARE; import static cc.blynk.server.core.protocol.enums.Response.OK; import static cc.blynk.server.core.protocol.model.messages.MessageFactory.produce; import static cc.blynk.server.workers.timer.TimerWorker.TIMER_MSG_ID; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; /** * The Blynk Project. * Created by Dmitriy Dumanskiy. * Created on 07.01.16. */ @RunWith(MockitoJUnitRunner.class) public class HttpAndTCPSameJVMTest extends IntegrationBase { private BaseServer httpServer; private BaseServer hardwareServer; private BaseServer appServer; private CloseableHttpClient httpclient; private String httpServerUrl; private ClientPair clientPair; @After public void shutdown() throws Exception { httpclient.close(); httpServer.close(); hardwareServer.close(); appServer.close(); clientPair.stop(); } @Before public void init() throws Exception { httpServer = new HttpAPIServer(holder).start(); hardwareServer = new HardwareServer(holder).start(); appServer = new AppServer(holder).start(); httpServerUrl = String.format("http://localhost:%s/", httpPort); httpclient = HttpClients.createDefault(); clientPair = initAppAndHardPair(tcpAppPort, tcpHardPort, properties); clientPair.hardwareClient.reset(); clientPair.appClient.reset(); } @Test public void testChangeNonWidgetPinValueViaHardwareAndGetViaHTTP() throws Exception { clientPair.hardwareClient.send("hardware vw 10 200"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(1, HARDWARE, b("1 vw 10 200")))); reset(clientPair.appClient.responseMock); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet request = new HttpGet(httpServerUrl + token + "/pin/v10"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("200", values.get(0)); } clientPair.appClient.send("hardware 1 vw 10 201"); verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(2, HARDWARE, b("vw 10 201")))); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("201", values.get(0)); } } @Test public void testChangePinValueViaAppAndHardware() throws Exception { clientPair.hardwareClient.send("hardware vw 4 200"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(1, HARDWARE, b("1 vw 4 200")))); reset(clientPair.appClient.responseMock); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet request = new HttpGet(httpServerUrl + token + "/pin/v4"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("200", values.get(0)); } clientPair.appClient.send("hardware 1 vw 4 201"); verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(2, HARDWARE, b("vw 4 201")))); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("201", values.get(0)); } } @Test public void testRTCWorksViaHttpAPI() throws Exception { RTC rtc = new RTC(); rtc.pin = 113; rtc.pinType = PinType.VIRTUAL; rtc.id = 434; rtc.height = 1; rtc.width = 2; clientPair.appClient.send("createWidget 1\0" + JsonParser.mapper.writeValueAsString(rtc)); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(1))); reset(clientPair.appClient.responseMock); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet request = new HttpGet(httpServerUrl + token + "/pin/v113"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); } } @Test public void testEventorWorksViaHttpAPI() throws Exception { Eventor eventor = EventorTest.oneRuleEventor("if v100 = 37 then setpin v2 123"); eventor.height = 1; eventor.width = 2; clientPair.appClient.send("createWidget 1\0" + JsonParser.mapper.writeValueAsString(eventor)); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(1))); reset(clientPair.appClient.responseMock); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v100"); request.setEntity(new StringEntity("[\"37\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 100 37")))); verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 100 37")))); verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(888, HARDWARE, b("vw 2 123")))); } @Test public void testEventorTimerWidgeWorkerWorksAsExpectedWithHttp() throws Exception { Executors.newScheduledThreadPool(1).scheduleAtFixedRate(holder.timerWorker, 0, 1000, TimeUnit.MILLISECONDS); TimerTime timerTime = new TimerTime(); timerTime.days = new int[] {1,2,3,4,5,6,7}; //adding 2 seconds just to be sure we no gonna miss timer event timerTime.time = LocalTime.now(DateTimeUtils.UTC).toSecondOfDay() + 2; timerTime.tzName = DateTimeUtils.UTC; Rule rule = new Rule(); rule.isActive = true; rule.triggerTime = timerTime; SetPinAction setPinAction = new SetPinAction(); setPinAction.pin = new Pin(); setPinAction.pin.pin = 4; setPinAction.pin.pinType = PinType.VIRTUAL; setPinAction.value = "1"; rule.actions = new BaseAction[] { setPinAction }; Eventor eventor = new Eventor(new Rule[] { rule }); eventor.id = 1000; clientPair.appClient.send("createWidget 1\0" + JsonParser.mapper.writeValueAsString(eventor)); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(ok(1))); verify(clientPair.appClient.responseMock, timeout(2000)).channelRead(any(), eq(produce(TIMER_MSG_ID, HARDWARE, b("1 vw 4 1")))); verify(clientPair.hardwareClient.responseMock, timeout(2000)).channelRead(any(), eq(produce(TIMER_MSG_ID, HARDWARE, b("vw 4 1")))); clientPair.appClient.reset(); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet requestGET = new HttpGet(httpServerUrl + token + "/pin/v4"); try (CloseableHttpResponse response = httpclient.execute(requestGET)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("1", values.get(0)); } } @Test public void testTimerWidgeWorkerWorksAsExpectedWithHttp() throws Exception { Executors.newScheduledThreadPool(1).scheduleAtFixedRate(holder.timerWorker, 0, 1000, TimeUnit.MILLISECONDS); Timer timer = new Timer(); timer.id = 112; timer.x = 1; timer.y = 1; timer.pinType = PinType.VIRTUAL; timer.pin = 4; timer.width = 2; timer.height = 1; timer.startValue = "1"; timer.stopValue = "0"; LocalTime localDateTime = LocalTime.now(ZoneId.of("UTC")); int curTime = localDateTime.toSecondOfDay(); timer.startTime = curTime + 1; timer.stopTime = curTime + 1; clientPair.appClient.send("createWidget 1\0" + JsonParser.toJson(timer)); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); verify(clientPair.hardwareClient.responseMock, timeout(2500).times(2)).channelRead(any(), any()); verify(clientPair.hardwareClient.responseMock, timeout(2000)).channelRead(any(), eq(produce(7777, HARDWARE, b("vw 4 1")))); verify(clientPair.hardwareClient.responseMock, timeout(2000)).channelRead(any(), eq(produce(7777, HARDWARE, b("vw 4 0")))); clientPair.appClient.reset(); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet requestGET = new HttpGet(httpServerUrl + token + "/pin/v4"); try (CloseableHttpResponse response = httpclient.execute(requestGET)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); //todo order is not guarateed here!!! Known issue String res = values.get(0); assertTrue("0".equals(res) || "1".equals(res)); } } @Test public void testChangePinValueViaAppAndHardwareForWrongPWMButton() throws Exception { clientPair.appClient.send("createWidget 1\0{\"type\":\"BUTTON\",\"id\":1000,\"x\":0,\"y\":0,\"color\":616861439,\"width\":2,\"height\":2,\"label\":\"Relay\",\"pinType\":\"DIGITAL\",\"pin\":18,\"pwmMode\":true,\"rangeMappingOn\":false,\"min\":0,\"max\":0,\"value\":\"1\",\"pushMode\":false}"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); clientPair.appClient.reset(); clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet requestGET = new HttpGet(httpServerUrl + token + "/pin/d18"); try (CloseableHttpResponse response = httpclient.execute(requestGET)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals("1", values.get(0)); } HttpPut requestPUT = new HttpPut(httpServerUrl + token + "/pin/d18"); requestPUT.setEntity(new StringEntity("[\"0\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(requestPUT)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("dw 18 0")))); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 dw 18 0")))); } @Test public void testChangePinValueViaHttpAPI() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v4"); HttpGet getRequest = new HttpGet(httpServerUrl + token + "/pin/v4"); for (int i = 0; i < 50; i++) { request.setEntity(new StringEntity("[\"" + i + "\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } clientPair.hardwareClient.send("hardsync " + b("vr 4")); verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(i + 1, HARDWARE, b("vw 4 " + i)))); try (CloseableHttpResponse response = httpclient.execute(getRequest)) { assertEquals(200, response.getStatusLine().getStatusCode()); List<String> values = consumeJsonPinValues(response); assertEquals(1, values.size()); assertEquals(i, Integer.valueOf(values.get(0)).intValue()); } } } @Test public void testIsHardwareAndAppConnected() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet request = new HttpGet(httpServerUrl + token + "/isHardwareConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("true", value); } request = new HttpGet(httpServerUrl + token + "/isAppConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("true", value); } } @Test public void testIsHardwareAndAppDisconnected() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); clientPair.stop(); HttpGet request = new HttpGet(httpServerUrl + token + "/isHardwareConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("false", value); } request = new HttpGet(httpServerUrl + token + "/isAppConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("false", value); } clientPair = initAppAndHardPair(tcpAppPort, tcpHardPort, properties); } @Test public void testIsHardwareConnecteedWithMultiDevices() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpGet request = new HttpGet(httpServerUrl + token + "/isHardwareConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("true", value); } request = new HttpGet(httpServerUrl + token + "/isAppConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("true", value); } Device device1 = new Device(1, "My Device", "ESP8266"); clientPair.appClient.send("createDevice 1\0" + device1.toString()); String createdDevice = clientPair.appClient.getBody(2); Device device = JsonParser.parseDevice(createdDevice); assertNotNull(device); assertNotNull(device.token); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new CreateDevice(2, device.toString()))); clientPair.appClient.reset(); clientPair.appClient.send("getDevices 1"); String deviceResponse = clientPair.appClient.getBody(); Device[] devices = JsonParser.mapper.readValue(deviceResponse, Device[].class); assertNotNull(devices); assertEquals(2, devices.length); TestHardClient hardClient2 = new TestHardClient("localhost", tcpHardPort); hardClient2.start(); hardClient2.send("login " + devices[1].token); verify(hardClient2.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); clientPair.stop(); request = new HttpGet(httpServerUrl + token + "/isHardwareConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("false", value); } request = new HttpGet(httpServerUrl + token + "/isAppConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("false", value); } request = new HttpGet(httpServerUrl + devices[1].token + "/isHardwareConnected"); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); String value = consumeText(response); assertNotNull(value); assertEquals("true", value); } } @Test public void testChangePinValueViaHttpAPIAndNoActiveProject() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); clientPair.appClient.reset(); clientPair.appClient.send("deactivate 1"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(1, OK))); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v31"); request.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 31 100")))); verify(clientPair.appClient.responseMock, after(400).never()).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 31 100")))); clientPair.appClient.send("activate 1"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(2, OK))); } @Test public void testChangeLCDPinValueViaHttpAPIAndValueChanged() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v0"); request.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 0 100")))); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 0 100")))); request = new HttpPut(httpServerUrl + token + "/pin/v1"); request.setEntity(new StringEntity("[\"101\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 1 101")))); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 1 101")))); } @Test public void testChangePinValueViaHttpAPIAndNoWidgetSinglePinValue() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v31"); request.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 31 100")))); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 31 100")))); } @Test public void testChangePinValueViaHttpAPIAndForTerminal() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); clientPair.appClient.send("createWidget 1\0{\"id\":222, \"width\":1, \"height\":1, \"x\":2, \"y\":2, \"label\":\"Some Text 2\", \"type\":\"TERMINAL\", \"pinType\":\"VIRTUAL\", \"pin\":100}"); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(new ResponseMessage(2, OK))); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/V100"); request.setEntity(new StringEntity("[\"100\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 100 100")))); verify(clientPair.appClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("1 vw 100 100")))); } @Test public void testChangePinValueViaHttpAPIAndNoWidgetMultiPinValue() throws Exception { clientPair.appClient.send("getToken 1"); String token = clientPair.appClient.getBody(); HttpPut request = new HttpPut(httpServerUrl + token + "/pin/v31"); request.setEntity(new StringEntity("[\"100\",\"101\",\"102\"]", ContentType.APPLICATION_JSON)); try (CloseableHttpResponse response = httpclient.execute(request)) { assertEquals(200, response.getStatusLine().getStatusCode()); } verify(clientPair.hardwareClient.responseMock, timeout(500)).channelRead(any(), eq(produce(111, HARDWARE, b("vw 31 100 101 102")))); } }