public async Task When_PerformNeededSyncs_Fails_Should_Release_Lock(string lockToTake)
        {
            var devAddrcache = new LoRaDevAddrCache(this.cache, null, null);
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, lockToTake == null?null : new[] { lockToTake });

            var managerInput = new List <DevAddrCacheInfo> {
                new DevAddrCacheInfo()
                {
                    DevEUI = TestEui.GenerateDevEui(), DevAddr = CreateDevAddr()
                }
            };
            var registryManagerMock = InitRegistryManager(managerInput);

            registryManagerMock.Setup(x => x.CreateQuery(It.IsAny <string>())).Throws(new RedisException(string.Empty));
            await devAddrcache.PerformNeededSyncs(registryManagerMock.Object);

            // When doing a full update, the FullUpdateKey lock should be reset to 1min, the GlobalDevAddrUpdateKey should be gone
            // When doing a partial update, the GlobalDevAddrUpdateKey should be gone
            switch (lockToTake)
            {
            case FullUpdateKey:
                Assert.Null(await this.cache.GetObjectTTL(GlobalDevAddrUpdateKey));
                break;

            case null:
                var nextFullUpdate = await this.cache.GetObjectTTL(FullUpdateKey);

                Assert.True(nextFullUpdate <= TimeSpan.FromMinutes(1));
                Assert.Null(await this.cache.GetObjectTTL(GlobalDevAddrUpdateKey));
                break;

            default: throw new InvalidOperationException("invalid test case");
            }
        }
예제 #2
0
        public async Task When_PerformNeededSyncs_Fails_Should_Release_Lock(params string[] lockToTake)
        {
            var devAddrcache = new LoRaDevAddrCache(this.cache, null, null);
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, null, lockToTake);

            List <DevAddrCacheInfo> managerInput = new List <DevAddrCacheInfo>();

            for (int i = 0; i < 2; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI  = NewUniqueEUI64(),
                    DevAddr = NewUniqueEUI32()
                });
            }

            var registryManagerMock = this.InitRegistryManager(managerInput);

            registryManagerMock.Setup(x => x.CreateQuery(It.IsAny <string>())).Throws <Exception>();
            await devAddrcache.PerformNeededSyncs(registryManagerMock.Object);

            Assert.Null(this.cache.GetObject <DevAddrCacheInfo>(GlobalDevAddrUpdateKey));
            var nextFullUpdate = await this.cache.GetObjectTTL(FullUpdateKey);

            if (lockToTake[0] == DeltaUpdateKey)
            {
                Assert.True(nextFullUpdate < TimeSpan.FromMinutes(1));
            }
        }
        private static void InitCache(ILoRaDeviceCacheStore cache, List <DevAddrCacheInfo> deviceIds)
        {
            var loradevaddrcache = new LoRaDevAddrCache(cache, null, null);

            foreach (var device in deviceIds)
            {
                loradevaddrcache.StoreInfo(device);
            }
        }
        // This test simulate a call received by multiple server. It ensures IoT Hub is only queried once.
        public async Task Multi_Gateway_When_DevAddr_Is_Not_In_Cache_Query_Iot_Hub_Only_Once_And_Save_In_Cache()
        {
            var gateway1     = NewUniqueEUI64();
            var gateway2     = NewUniqueEUI64();
            var dateTime     = DateTime.UtcNow;
            var managerInput = new List <DevAddrCacheInfo>();

            for (var i = 0; i < 2; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI  = TestEui.GenerateDevEui(),
                    DevAddr = CreateDevAddr()
                });
            }

            var devAddrJoining      = managerInput[0].DevAddr;
            var registryManagerMock = InitRegistryManager(managerInput);

            // In this test we want no updates running
            // initialize locks for test to run correctly
            var lockToTake = new string[2] {
                FullUpdateKey, GlobalDevAddrUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, lockToTake);

            var deviceGetter = new DeviceGetter(registryManagerMock.Object, this.cache);
            // Simulate three queries
            var tasks =
                from gw in new[] { gateway1, gateway2 }
            select Enumerable.Repeat(gw, 2) into gws     // repeat each gateway twice
            from gw in gws
            select deviceGetter.GetDeviceList(null, gw, new DevNonce(0xABCD), devAddrJoining);

            await Task.WhenAll(tasks);

            // If a cache miss it should save it in the redisCache
            var devAddrcache = new LoRaDevAddrCache(this.cache, null, null);
            var queryResult  = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, devAddrJoining));

            Assert.Single(queryResult);
            var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);

            Assert.Equal(managerInput[0].DevAddr, resultObject.DevAddr);
            Assert.Equal(managerInput[0].GatewayId ?? string.Empty, resultObject.GatewayId);
            Assert.Equal(managerInput[0].DevEUI, resultObject.DevEUI);

            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>(), It.IsAny <int>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Once);
        }
        // This test simulate a call received by multiple server. It ensures IoT Hub is only queried once.
        public async Task Multi_Gateway_When_DevAddr_Is_Not_In_Cache_Query_Iot_Hub_Only_Once_And_Save_In_Cache()
        {
            string   gatewayId = NewUniqueEUI64();
            DateTime dateTime  = DateTime.UtcNow;
            List <DevAddrCacheInfo> managerInput = new List <DevAddrCacheInfo>();

            for (int i = 0; i < 2; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI  = NewUniqueEUI64(),
                    DevAddr = NewUniqueEUI32()
                });
            }

            var devAddrJoining      = managerInput[0].DevAddr;
            var registryManagerMock = this.InitRegistryManager(managerInput);

            // In this test we want no updates running
            // initialize locks for test to run correctly
            var lockToTake = new string[2] {
                FullUpdateKey, DeltaUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, null, lockToTake);

            var deviceGetter = new DeviceGetter(registryManagerMock.Object, this.cache);
            // Simulate three queries
            var tasks = new Task[3]
            {
                deviceGetter.GetDeviceList(null, gatewayId, "ABCD", devAddrJoining),
                deviceGetter.GetDeviceList(null, gatewayId, "ABCD", devAddrJoining),
                deviceGetter.GetDeviceList(null, gatewayId, "ABCD", devAddrJoining),
            };

            await Task.WhenAll(tasks);

            // If a cache miss it should save it in the redisCache
            var devAddrcache = new LoRaDevAddrCache(this.cache, null, null);
            var queryResult  = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, devAddrJoining));

            Assert.Single(queryResult);
            var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);

            Assert.Equal(managerInput[0].DevAddr, resultObject.DevAddr);
            Assert.Equal(managerInput[0].GatewayId, resultObject.GatewayId);
            Assert.Equal(managerInput[0].DevEUI, resultObject.DevEUI);

            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>(), It.IsAny <int>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Once);
        }
        // This test perform a full update and we check the following
        // primary key present in the cache is still here after a fullupdate
        // Items with same Devaddr are correctly saved (one old from cache, one from iot hub)
        // Old cache items sharing a devaddr not in the new update are correctly removed
        // Items with a devAddr not in the update are correctly still in cache
        // Gateway Id is correctly updated in old cache information.
        // Primary Key are not kept as UpdateTime is not similar
        public async Task Full_Update_Perform_Correctly_On_Non_Empty_Cache_And_Keep_Old_Values_Except_Primary_Keys()
        {
            var oldGatewayId   = NewUniqueEUI64();
            var newGatewayId   = NewUniqueEUI64();
            var dateTime       = DateTime.UtcNow;
            var updateDateTime = DateTime.UtcNow.AddMinutes(3);
            var primaryKey     = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
            var newValues      = new List <DevAddrCacheInfo>();

            for (var i = 0; i < 5; i++)
            {
                newValues.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = TestEui.GenerateDevEui(),
                    DevAddr          = CreateDevAddr(),
                    GatewayId        = newGatewayId,
                    LastUpdatedTwins = updateDateTime
                });
            }

            // The cache start as empty
            var registryManagerMock = InitRegistryManager(newValues, newValues.Count);

            // Set up the cache with expectation.
            var cacheInput = new List <DevAddrCacheInfo>();

            for (var i = 0; i < 5; i++)
            {
                cacheInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = newValues[i].DevEUI,
                    DevAddr          = newValues[i].DevAddr,
                    GatewayId        = oldGatewayId,
                    LastUpdatedTwins = dateTime,
                    PrimaryKey       = primaryKey
                });
            }

            InitCache(this.cache, cacheInput);

            // initialize locks for test to run correctly
            await LockDevAddrHelper.PrepareLocksForTests(this.cache);

            var devAddrCache = new LoRaDevAddrCache(this.cache, null, newGatewayId);
            await devAddrCache.PerformNeededSyncs(registryManagerMock.Object);

            // we expect the devices are saved, the double device id should not be there anymore
            for (var i = 0; i < newValues.Count; i++)
            {
                var queryResult = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, newValues[i].DevAddr));
                Assert.Single(queryResult);
                var result2Object = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);
                Assert.Equal(newGatewayId, result2Object.GatewayId);
                Assert.Equal(newValues[i].DevEUI, result2Object.DevEUI);
                Assert.Equal(string.Empty, result2Object.PrimaryKey);
            }

            // Iot hub should never have been called.
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            // We expect to query for the key once (the device with an active connection)
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Never);
        }
        // This test perform a delta update and we check the following
        // primary key present in the cache is still here after a delta up
        // Items with save Devaddr are correctly saved (one old from cache, one from iot hub)
        // Gateway Id is correctly updated in old cache information.
        // Primary Key are kept as UpdateTime is similar
        public async Task Delta_Update_Perform_Correctly_On_Non_Empty_Cache_And_Keep_Old_Values()
        {
            var oldGatewayId = NewUniqueEUI64();
            var newGatewayId = NewUniqueEUI64();
            var dateTime     = DateTime.UtcNow;

            var primaryKey   = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
            var managerInput = new List <DevAddrCacheInfo>();

            var adressForDuplicateDevAddr = CreateDevAddr();

            for (var i = 0; i < 5; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = TestEui.GenerateDevEui(),
                    DevAddr          = CreateDevAddr(),
                    GatewayId        = newGatewayId,
                    LastUpdatedTwins = dateTime
                });
            }

            managerInput.Add(new DevAddrCacheInfo()
            {
                DevEUI           = TestEui.GenerateDevEui(),
                DevAddr          = adressForDuplicateDevAddr,
                GatewayId        = newGatewayId,
                LastUpdatedTwins = dateTime
            });

            var devAddrJoining = managerInput[0].DevAddr;
            // The cache start as empty
            var registryManagerMock = InitRegistryManager(managerInput, managerInput.Count);

            // Set up the cache with expectation.
            var cacheInput = new List <DevAddrCacheInfo>();

            for (var i = 0; i < 5; i++)
            {
                cacheInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = managerInput[i].DevEUI,
                    DevAddr          = managerInput[i].DevAddr,
                    GatewayId        = oldGatewayId,
                    LastUpdatedTwins = dateTime
                });
            }

            cacheInput[2].PrimaryKey = primaryKey;
            cacheInput[3].PrimaryKey = primaryKey;

            var devEui = TestEui.GenerateDevEui();

            cacheInput.Add(new DevAddrCacheInfo()
            {
                DevEUI           = devEui,
                DevAddr          = adressForDuplicateDevAddr,
                GatewayId        = oldGatewayId,
                PrimaryKey       = primaryKey,
                LastUpdatedTwins = dateTime
            });
            InitCache(this.cache, cacheInput);

            // initialize locks for test to run correctly
            var locksToTake = new string[1] {
                FullUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, locksToTake);

            var devAddrCache = new LoRaDevAddrCache(this.cache, null, newGatewayId);
            await devAddrCache.PerformNeededSyncs(registryManagerMock.Object);

            // we expect the devices are saved
            for (var i = 0; i < managerInput.Count; i++)
            {
                if (managerInput[i].DevAddr != adressForDuplicateDevAddr)
                {
                    var queryResult = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, managerInput[i].DevAddr));
                    Assert.Single(queryResult);
                    var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);
                    Assert.Equal(managerInput[i].GatewayId, resultObject.GatewayId);
                    Assert.Equal(managerInput[i].DevEUI, resultObject.DevEUI);
                    Assert.Equal(cacheInput[i].PrimaryKey, resultObject.PrimaryKey);
                }
            }

            // let's check the devices with a double EUI
            var query2Result = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, adressForDuplicateDevAddr));

            Assert.Equal(2, query2Result.Length);
            for (var i = 0; i < 2; i++)
            {
                var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(query2Result[0].Value);
                if (resultObject.DevEUI == devEui)
                {
                    Assert.Equal(oldGatewayId, resultObject.GatewayId);
                    Assert.Equal(primaryKey, resultObject.PrimaryKey);
                }
                else
                {
                    Assert.Equal(newGatewayId, resultObject.GatewayId);
                    Assert.True(string.IsNullOrEmpty(resultObject.PrimaryKey));
                }
            }

            // Iot hub should never have been called.
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            // We expect to query for the key once (the device with an active connection)
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Never);
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>(), It.IsAny <int>()), Times.Never);
        }
        // Trigger delta update correctly to see if it performs correctly on an empty cache
        public async Task Delta_Update_Perform_Correctly_On_Empty_Cache()
        {
            var gatewayId = NewUniqueEUI64();
            var dateTime  = DateTime.UtcNow;

            var managerInput = new List <DevAddrCacheInfo>();

            for (var i = 0; i < 5; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = TestEui.GenerateDevEui(),
                    DevAddr          = CreateDevAddr(),
                    GatewayId        = gatewayId,
                    LastUpdatedTwins = dateTime
                });
            }

            var devAddrJoining = managerInput[0].DevAddr;
            // The cache start as empty
            var registryManagerMock = InitRegistryManager(managerInput);

            // initialize locks for test to run correctly
            var locksToTake = new string[1] {
                FullUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, locksToTake);

            var devAddrCache = new LoRaDevAddrCache(this.cache, null, gatewayId);
            await devAddrCache.PerformNeededSyncs(registryManagerMock.Object);

            while (!string.IsNullOrEmpty(this.cache.StringGet(GlobalDevAddrUpdateKey)))
            {
                await Task.Delay(100);
            }

            var foundItem = 0;

            // we expect the devices are saved
            for (var i = 0; i < 5; i++)
            {
                var queryResult = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, managerInput[i].DevAddr));
                if (queryResult.Length > 0)
                {
                    foundItem++;
                    Assert.Single(queryResult);
                    var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);
                    Assert.Equal(managerInput[i].GatewayId, resultObject.GatewayId);
                    Assert.Equal(managerInput[i].DevEUI, resultObject.DevEUI);
                }
            }

            // Only two items should be updated by the delta updates
            Assert.Equal(2, foundItem);

            // Iot hub should never have been called.
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            // We expect to query for the key once (the device with an active connection)
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Never);
        }
        // This test perform a full update and we check the following
        // primary key present in the cache is still here after a fullupdate
        // Items with same Devaddr are correctly saved (one old from cache, one from iot hub)
        // Old cache items sharing a devaddr not in the new update are correctly removed
        // Items with a devAddr not in the update are correctly still in cache
        // Gateway Id is correctly updated in old cache information.
        // Primary Key are kept as UpdateTime is similar
        public async Task Full_Update_Perform_Correctly_On_Non_Empty_Cache_And_Keep_Old_Values()
        {
            string   oldGatewayId             = NewUniqueEUI64();
            string   newGatewayId             = NewUniqueEUI64();
            DateTime dateTime                 = DateTime.UtcNow;
            var      primaryKey               = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
            List <DevAddrCacheInfo> newValues = new List <DevAddrCacheInfo>();

            var adressForDuplicateDevAddr = NewUniqueEUI32();

            for (int i = 0; i < 5; i++)
            {
                newValues.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = NewUniqueEUI64(),
                    DevAddr          = NewUniqueEUI32(),
                    GatewayId        = newGatewayId,
                    LastUpdatedTwins = dateTime
                });
            }

            newValues.Add(new DevAddrCacheInfo()
            {
                DevEUI           = NewUniqueEUI64(),
                DevAddr          = adressForDuplicateDevAddr,
                GatewayId        = newGatewayId,
                LastUpdatedTwins = dateTime
            });

            var devAddrJoining = newValues[0].DevAddr;
            // The cache start as empty
            var registryManagerMock = this.InitRegistryManager(newValues, newValues.Count());

            // Set up the cache with expectation.
            List <DevAddrCacheInfo> cacheInput = new List <DevAddrCacheInfo>();

            for (int i = 0; i < 5; i++)
            {
                cacheInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = newValues[i].DevEUI,
                    DevAddr          = newValues[i].DevAddr,
                    GatewayId        = oldGatewayId,
                    LastUpdatedTwins = dateTime
                });
            }

            cacheInput[2].PrimaryKey = primaryKey;
            cacheInput[3].PrimaryKey = primaryKey;

            // this is a device that will be overwritten by the update as it share a devaddr with an updated device
            var devEuiDoubleItem = NewUniqueEUI64();

            cacheInput.Add(new DevAddrCacheInfo()
            {
                DevEUI           = devEuiDoubleItem,
                DevAddr          = adressForDuplicateDevAddr,
                GatewayId        = oldGatewayId,
                PrimaryKey       = primaryKey,
                LastUpdatedTwins = dateTime
            });

            this.InitCache(this.cache, cacheInput);

            // initialize locks for test to run correctly
            string[] neededLocksForTestToRun = new string[2] {
                GlobalDevAddrUpdateKey, FullUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, neededLocksForTestToRun, null);

            LoRaDevAddrCache devAddrCache = new LoRaDevAddrCache(this.cache, null, newGatewayId);
            await devAddrCache.PerformNeededSyncs(registryManagerMock.Object);

            // we expect the devices are saved, the double device id should not be there anymore
            for (int i = 0; i < newValues.Count; i++)
            {
                var queryResult = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, newValues[i].DevAddr));
                Assert.Single(queryResult);
                var result2Object = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);
                Assert.Equal(newGatewayId, result2Object.GatewayId);
                Assert.Equal(newValues[i].DevEUI, result2Object.DevEUI);
                if (newValues[i].DevEUI == devEuiDoubleItem)
                {
                    Assert.Equal(cacheInput[i].PrimaryKey, result2Object.PrimaryKey);
                }
            }

            // Iot hub should never have been called.
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            // We expect to query for the key once (the device with an active connection)
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Never);
        }
        // This test perform a delta update and we check the following
        // primary key present in the cache is still here after a delta up
        // Items with save Devaddr are correctly saved (one old from cache, one from iot hub)
        // Gateway Id is correctly updated in old cache information.
        // Primary Key are dropped as updatetime is defferent
        public async Task Delta_Update_Perform_Correctly_On_Non_Empty_Cache_And_Keep_Old_Values_Except_Primary_Key()
        {
            string   oldGatewayId   = NewUniqueEUI64();
            string   newGatewayId   = NewUniqueEUI64();
            DateTime dateTime       = DateTime.UtcNow;
            DateTime updateDateTime = DateTime.UtcNow.AddMinutes(10);

            var primaryKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(PrimaryKey));
            List <DevAddrCacheInfo> managerInput = new List <DevAddrCacheInfo>();

            var adressForDuplicateDevAddr = NewUniqueEUI32();

            for (int i = 0; i < 5; i++)
            {
                managerInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = NewUniqueEUI64(),
                    DevAddr          = NewUniqueEUI32(),
                    GatewayId        = newGatewayId,
                    LastUpdatedTwins = updateDateTime
                });
            }

            var registryManagerMock = this.InitRegistryManager(managerInput, managerInput.Count());

            // Set up the cache with expectation.
            List <DevAddrCacheInfo> cacheInput = new List <DevAddrCacheInfo>();

            for (int i = 0; i < 5; i++)
            {
                cacheInput.Add(new DevAddrCacheInfo()
                {
                    DevEUI           = managerInput[i].DevEUI,
                    DevAddr          = managerInput[i].DevAddr,
                    LastUpdatedTwins = dateTime,
                    PrimaryKey       = primaryKey
                });
            }

            this.InitCache(this.cache, cacheInput);
            // initialize locks for test to run correctly
            string[] neededLocksForTestToRun = new string[2] {
                GlobalDevAddrUpdateKey, DeltaUpdateKey
            };
            var locksGuideTest = new string[1] {
                FullUpdateKey
            };
            await LockDevAddrHelper.PrepareLocksForTests(this.cache, neededLocksForTestToRun, locksGuideTest);

            LoRaDevAddrCache devAddrCache = new LoRaDevAddrCache(this.cache, null, newGatewayId);
            await devAddrCache.PerformNeededSyncs(registryManagerMock.Object);

            // we expect the devices are saved
            for (int i = 0; i < managerInput.Count; i++)
            {
                var queryResult = this.cache.GetHashObject(string.Concat(CacheKeyPrefix, managerInput[i].DevAddr));
                Assert.Single(queryResult);
                var resultObject = JsonConvert.DeserializeObject <DevAddrCacheInfo>(queryResult[0].Value);
                Assert.Equal(managerInput[i].GatewayId, resultObject.GatewayId);
                Assert.Equal(managerInput[i].DevEUI, resultObject.DevEUI);
                // as the object changed the keys should not be saved
                Assert.Equal(string.Empty, resultObject.PrimaryKey);
            }

            // Iot hub should never have been called.
            registryManagerMock.Verify(x => x.CreateQuery(It.IsAny <string>()), Times.Once);
            registryManagerMock.Verify(x => x.GetTwinAsync(It.IsAny <string>()), Times.Never);
            // We expect to query for the key once (the device with an active connection)
            registryManagerMock.Verify(x => x.GetDeviceAsync(It.IsAny <string>()), Times.Never);
        }