public async Task When_Payload_Is_Empty_ExternalDecoder_Should_Be_Called_With_Empty_Payload()
        {
            var devEui       = new DevEui(0x12);
            var fport        = FramePorts.App8;
            var decodedValue = "{\"from\":\"http\"}";

            using var httpMessageHandler = new HttpMessageHandlerMock();
            httpMessageHandler.SetupHandler((r) =>
            {
                var queryDictionary = System.Web.HttpUtility.ParseQueryString(r.RequestUri.Query);
                Assert.Equal("0000000000000012", queryDictionary.Get("devEUI"));
                Assert.Equal(((byte)fport).ToString(CultureInfo.InvariantCulture), queryDictionary.Get("fport"));
                Assert.Empty(queryDictionary.Get("payload"));

                return(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                {
                    Content = new StringContent(decodedValue, Encoding.UTF8, "application/json"),
                });
            });

            using var httpClient = new HttpClient(httpMessageHandler);
            var target = new LoRaPayloadDecoder(httpClient);
            var result = await target.DecodeMessageAsync(devEui, Array.Empty <byte>(), fport, "http://test/decoder");

            var json = JsonConvert.SerializeObject(result.GetDecodedPayload());

            Assert.Equal(decodedValue, json);
        }
        public async Task When_Resent_Message_Using_Custom_Decoder_Returns_Complex_Object_Should_Send_Decoded_Value()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID));
            var loRaDevice      = CreateLoRaDevice(simulatedDevice);

            loRaDevice.SensorDecoder = "http://customdecoder/test1";

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            _ = LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
                .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
                .ReturnsAsync(true);
            _ = LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsAny <TwinCollection>(), It.IsAny <CancellationToken>())).ReturnsAsync(true);

            // C2D message will be checked
            // LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull<TimeSpan>()))
            // .ReturnsAsync((Message)null);
            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loRaDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            var decodedObject = new { temp = 10, humidity = 22.1, text = "abc", cloudToDeviceMessage = new { test = 1 } };

            using var httpMessageHandler = new HttpMessageHandlerMock();
            _ = httpMessageHandler.SetupHandler((r) =>
            {
                return(new HttpResponseMessage(System.Net.HttpStatusCode.OK)
                {
                    Content = new StringContent(JsonConvert.SerializeObject(decodedObject), Encoding.UTF8, "application/json"),
                });
            });

            using var httpClient = new HttpClient(httpMessageHandler);
            PayloadDecoder.SetDecoder(new LoRaPayloadDecoder(httpClient));

            // sends confirmed message
            var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("1", fcnt: 10);

            using var request = CreateWaitableRequest(confirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);

            Assert.NotNull(loRaDeviceTelemetry);
            var rawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes("1"));

            Assert.Equal(rawPayload, loRaDeviceTelemetry.Rawdata);

            // Validate json
            var actualJsonTelemetry = JsonConvert.SerializeObject(loRaDeviceTelemetry, Formatting.None);

            var expectedTelemetryJson = $"{{\"time\":100000,\"tmms\":100000,\"freq\":868.3,\"chan\":2,\"rfch\":1,\"modu\":\"LoRa\",\"datr\":\"SF10BW125\",\"rssi\":2.0,\"lsnr\":0.1,\"data\":{{\"temp\":10,\"humidity\":22.1,\"text\":\"abc\"}},\"port\":1,\"fcnt\":10,\"edgets\":{loRaDeviceTelemetry.Edgets},\"rawdata\":\"{rawPayload}\",\"eui\":\"0000000000000001\",\"gatewayid\":\"test-gateway\",\"stationeui\":\"0000000000000000\"}}";

            Assert.Equal(expectedTelemetryJson, actualJsonTelemetry);

            // send a second confirmed message with same fcnt to simulate
            using var request2 = CreateWaitableRequest(confirmedMessagePayload);
            messageDispatcher.DispatchRequest(request2);
            Assert.True(await request2.WaitCompleteAsync());
            Assert.NotNull(request2.ResponseDownlink);
            Assert.NotNull(loRaDeviceTelemetry);
            var rawPayload2 = Convert.ToBase64String(Encoding.UTF8.GetBytes("1"));

            Assert.Equal(rawPayload2, loRaDeviceTelemetry.Rawdata);
            LoRaDeviceClient.Verify(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null), Times.Exactly(2));
            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }
        public async Task When_Using_Custom_Fails_Returns_Sets_Error_Information_In_Value()
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: ServerGatewayID));
            var loRaDevice      = CreateLoRaDevice(simulatedDevice);

            loRaDevice.SensorDecoder = "http://customdecoder/test1";

            // message will be sent
            LoRaDeviceTelemetry loRaDeviceTelemetry = null;

            LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null))
            .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => loRaDeviceTelemetry = t)
            .ReturnsAsync(true);

            // C2D message will be checked
            LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()))
            .ReturnsAsync((Message)null);

            using var cache           = EmptyMemoryCache();
            using var loraDeviceCache = CreateDeviceCache(loRaDevice);
            using var deviceRegistry  = new LoRaDeviceRegistry(ServerConfiguration, cache, LoRaDeviceApi.Object, LoRaDeviceFactory, loraDeviceCache);

            // Send to message processor
            using var messageDispatcher = new MessageDispatcher(
                      ServerConfiguration,
                      deviceRegistry,
                      FrameCounterUpdateStrategyProvider);

            using var httpMessageHandler = new HttpMessageHandlerMock();
            httpMessageHandler.SetupHandler((r) =>
            {
                return(new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
                {
                    Content = new StringContent("my error", Encoding.UTF8, "application/json"),
                });
            });

            using var httpClient = new HttpClient(httpMessageHandler);
            PayloadDecoder.SetDecoder(new LoRaPayloadDecoder(httpClient));

            // sends unconfirmed message
            var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("1", fcnt: 1);

            using var request = CreateWaitableRequest(unconfirmedMessagePayload);
            messageDispatcher.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());

            Assert.NotNull(loRaDeviceTelemetry);
            var rawPayload = Convert.ToBase64String(Encoding.UTF8.GetBytes("1"));

            Assert.Equal(rawPayload, loRaDeviceTelemetry.Rawdata);
            Assert.IsType <DecodingFailedPayload>(loRaDeviceTelemetry.Data);
            var decodedPayload = (DecodingFailedPayload)loRaDeviceTelemetry.Data;

            Assert.Equal("SensorDecoderModule 'http://customdecoder/test1?devEUI=0000000000000001&fport=1&payload=MQ%3d%3d' returned bad request.", decodedPayload.Error);
            Assert.Equal("my error", decodedPayload.ErrorDetail);

            // Validate json
            var actualJsonTelemetry   = JsonConvert.SerializeObject(loRaDeviceTelemetry, Formatting.None);
            var expectedTelemetryJson = $"{{\"time\":100000,\"tmms\":100000,\"freq\":868.3,\"chan\":2,\"rfch\":1,\"modu\":\"LoRa\",\"datr\":\"SF10BW125\",\"rssi\":2.0,\"lsnr\":0.1,\"data\":{{\"error\":\"SensorDecoderModule 'http://customdecoder/test1?devEUI=0000000000000001&fport=1&payload=MQ%3d%3d' returned bad request.\",\"errorDetail\":\"my error\"}},\"port\":1,\"fcnt\":1,\"edgets\":{loRaDeviceTelemetry.Edgets},\"rawdata\":\"{ rawPayload}\",\"eui\":\"0000000000000001\",\"gatewayid\":\"test-gateway\",\"stationeui\":\"0000000000000000\"}}";

            Assert.Equal(expectedTelemetryJson, actualJsonTelemetry);

            LoRaDeviceApi.VerifyAll();
            LoRaDeviceClient.VerifyAll();
        }