public async Task When_Getting_Device_Information_From_Twin_Returns_JoinAccept(string deviceGatewayID)
        {
            var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID));
            var joinRequest     = simulatedDevice.CreateJoinRequest();

            var devAddr = (DevAddr?)null;
            var devEui  = simulatedDevice.LoRaDevice.DevEui;

            // Device twin will be queried
            var twin = new Twin();

            twin.Properties.Desired[TwinProperty.DevEUI] = devEui.ToString();
            twin.Properties.Desired[TwinProperty.AppEui] = simulatedDevice.LoRaDevice.AppEui?.ToString();
            twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey?.ToString();
            if (deviceGatewayID != null)
            {
                twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID;
            }
            twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder;
            LoRaDeviceClient.Setup(x => x.GetTwinAsync(CancellationToken.None)).ReturnsAsync(twin);

            // Device twin will be updated
            AppSessionKey?    afterJoinAppSKey = null;
            NetworkSessionKey?afterJoinNwkSKey = null;
            string            afterJoinDevAddr = null;

            LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>(), It.IsAny <CancellationToken>()))
            .Callback <TwinCollection, CancellationToken>((updatedTwin, _) =>
            {
                afterJoinAppSKey = AppSessionKey.Parse(updatedTwin[TwinProperty.AppSKey].Value);
                afterJoinNwkSKey = NetworkSessionKey.Parse(updatedTwin[TwinProperty.NwkSKey].Value);
                afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr];
            })
            .ReturnsAsync(true);

            // Lora device api will be search by devices with matching deveui,
            LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, joinRequest.DevNonce))
            .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEui, "aabb").AsList()));

            using var memoryCache    = new MemoryCache(new MemoryCacheOptions());
            using var deviceRegistry = new LoRaDeviceRegistry(ServerConfiguration, memoryCache, LoRaDeviceApi.Object, LoRaDeviceFactory, DeviceCache);

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

            using var request = CreateWaitableRequest(TestUtils.GenerateTestRadioMetadata(), joinRequest);
            messageProcessor.DispatchRequest(request);
            Assert.True(await request.WaitCompleteAsync());
            Assert.NotNull(request.ResponseDownlink);
            Assert.Single(DownstreamMessageSender.DownlinkMessages);
            var downlinkMessage = DownstreamMessageSender.DownlinkMessages[0];
            var joinAccept      = new LoRaPayloadJoinAccept(downlinkMessage.Data, simulatedDevice.LoRaDevice.AppKey.Value);

            Assert.Equal(joinAccept.DevAddr.ToString(), afterJoinDevAddr);

            // check that the device is in cache
            Assert.True(DeviceCache.HasRegistrations(joinAccept.DevAddr));
            Assert.True(DeviceCache.TryGetByDevEui(devEui, out var cachedDevice));
            Assert.Equal(afterJoinAppSKey, cachedDevice.AppSKey);
            Assert.Equal(afterJoinNwkSKey, cachedDevice.NwkSKey);
            Assert.Equal(joinAccept.DevAddr, cachedDevice.DevAddr);
            Assert.True(cachedDevice.IsOurDevice);
            if (deviceGatewayID == null)
            {
                Assert.Null(cachedDevice.GatewayID);
            }
            else
            {
                Assert.Equal(deviceGatewayID, cachedDevice.GatewayID);
            }

            // fcnt is restarted
            Assert.Equal(0U, cachedDevice.FCntUp);
            Assert.Equal(0U, cachedDevice.FCntDown);
            Assert.False(cachedDevice.HasFrameCountChanges);
        }