using NSubstitute; using NUnit.Framework; using Plugin.BLE.Abstractions.Contracts; using System; using System.Threading.Tasks; using TINK.Services.BluetoothLock.Tdo; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.BLE; using DeviceState = Plugin.BLE.Abstractions.DeviceState; using Plugin.BLE.Abstractions.EventArgs; using System.Threading; namespace TestLockItBLE { public class Tests { [Test] public void TestOpen() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var controlService = Substitute.For(); var controlCharacteristic = Substitute.For(); var stateCharacteristic = Substitute.For(); var activateCharacteristic = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'p', (byte)'a', (byte)'w', (byte)'m', (byte)'X', (byte)'8', (byte)'T', (byte)'X', (byte)'Q', (byte)'Z', (byte)'d', (byte)'l', (byte)'k', (byte)'3', (byte)'e', (byte)'V', }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(controlService)); controlService.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to open functionality. controlService.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 1 /* State read before open action: closed */ })); controlService.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateCharacteristic)); activateCharacteristic.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.Open /* State passed as event argument after opening. */}); // Use factory to create LockIt-object. var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; var lockState = lockIt.OpenAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); Assert.That(lockState.Result, Is.EqualTo(LockitLockingState.Open)); } [Test] public void TestOpen_ThrowsCouldntOpenInconsistentStateExecption() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var controlService = Substitute.For(); var controlCharacteristic = Substitute.For(); var stateCharacteristic = Substitute.For(); var activateLock = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'m', (byte)'l', (byte)'Q', (byte)'I', (byte)'S', (byte)'z', (byte)'p', (byte)'H', (byte)'m', (byte)'n', (byte)'V', (byte)'n', (byte)'7', (byte)'f', (byte)'i', (byte)'3' }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(controlService)); controlService.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to open functionality. controlService.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 1 /* State read before open action: closed */ })); controlService.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateLock)); activateLock.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.Closed /* State passed as event argument after opening. */}); // Use factory to create LockIt-object. var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; Assert.That(async () => { var lockState = lockIt.OpenAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); await lockState; }, Throws.InstanceOf()); } [Test] public void TestOpen_ThrowsCouldntOpenBoldBlockedException() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var lockControl = Substitute.For(); var controlCharacteristic = Substitute.For(); var activateLock = Substitute.For(); var stateCharacteristic = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'i', (byte)'r', (byte)'F', (byte)'h', (byte)'G', (byte)'T', (byte)'z', (byte)'P', (byte)'F', (byte)'Z', (byte)'n', (byte)'z', (byte)'Y', (byte)'B', (byte)'s', (byte)'9' }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(lockControl)); lockControl.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to open functionality. lockControl.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 1 /* closed */}), Task.FromResult(new byte[] { 4 /* bold blocked */ })); lockControl.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateLock)); activateLock.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.CouldntOpenBoldBlocked /* State passed as event argument after opening. */}); var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; // Use factory to create LockIt-object. Assert.That(async () => { var lockState = lockIt.OpenAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); await lockState; }, Throws.InstanceOf()); } [Test] public void TestClose() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var lockControl = Substitute.For(); var controlCharacteristic = Substitute.For(); var activateLock = Substitute.For(); var stateCharacteristic = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'I', (byte)'7', (byte)'y', (byte)'B', (byte)'f', (byte)'s', (byte)'v', (byte)'L', (byte)'G', (byte)'L', (byte)'7', (byte)'b', (byte)'7', (byte)'X', (byte)'z', (byte)'t' }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(lockControl)); lockControl.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to close functionality. lockControl.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 0 /* open */ }), Task.FromResult(new byte[] { 1 /* closed */})); lockControl.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateLock)); activateLock.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.Closed /* State passed as event argument after closing. */}); // Use factory to create LockIt-object. var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; var lockState = lockIt.CloseAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); Assert.That(lockState.Result, Is.EqualTo(LockitLockingState.Closed)); } [Test] public void TestClose_ThrowsCouldntCloseInconsistentStateExecption() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var lockControl = Substitute.For(); var controlCharacteristic = Substitute.For(); var activateLock = Substitute.For(); var stateCharacteristic = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'8', (byte)'q', (byte)'3', (byte)'9', (byte)'i', (byte)'6', (byte)'c', (byte)'g', (byte)'9', (byte)'L', (byte)'V', (byte)'7', (byte)'T', (byte)'G', (byte)'l', (byte)'f' }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(lockControl)); lockControl.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to close functionality. lockControl.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 0 /* opened */})); lockControl.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateLock)); activateLock.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.Open /* State passed as event argument after closing. */}); // Use factory to create LockIt-object. var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; // Use factory to create LockIt-object. Assert.That(async () => { var lockState = lockIt.CloseAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); await lockState; }, Throws.InstanceOf()); } [Test] public void TestClose_ThrowsCouldntCloseBoldBlockedException() { var device = Substitute.For(); var adapter = Substitute.For(); var cipher = Substitute.For(); var auth = Substitute.For(); var lockControl = Substitute.For(); var controlCharacteristic = Substitute.For(); var activateLock = Substitute.For(); var stateCharacteristic = Substitute.For(); var authTdo = new LockInfoAuthTdo.Builder { Id = 12, K_seed = new byte[] { (byte)'v', (byte)'f', (byte)'u', (byte)'v', (byte)'j', (byte)'E', (byte)'b', (byte)'x', (byte)'p', (byte)'z', (byte)'a', (byte)'h', (byte)'V', (byte)'5', (byte)'9', (byte)'i' }, K_u = new byte[16] }.Build(); // Calls related to Authenticate functionality. device.State.Returns(DeviceState.Connected); device.Id.Returns(new Guid("0000f00d-1212-efde-1523-785fef13d123")); device.GetServiceAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(lockControl)); lockControl.GetCharacteristicAsync(new Guid("0000baab-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(auth)); auth.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); auth.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[8])); cipher.Decrypt(Arg.Any(), Arg.Any()).Returns(new byte[3]); cipher.Encrypt(Arg.Any(), Arg.Any()).Returns(new byte[16]); auth.WriteAsync(Arg.Any()).Returns(true); device.Name.Returns("ISHAREIT+123"); // Calls related to Close functionality. lockControl.GetCharacteristicAsync(new Guid("0000baaa-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(controlCharacteristic)); controlCharacteristic.ReadAsync(Arg.Any()).Returns(Task.FromResult(new byte[] { 0 /* open */})); lockControl.GetCharacteristicAsync(new Guid("0000beee-1212-efde-1523-785fef13d123")).Returns(Task.FromResult(activateLock)); activateLock.WriteAsync(Arg.Any()).Returns(Task.FromResult(true)); stateCharacteristic.Value.Returns(new byte[] { (byte)LockitLockingState.CouldntCloseBoldBlocked /* State passed as event argument after opening. */}); // Use factory to create LockIt-object. var lockIt = LockItEventBased.Authenticate(device, authTdo, adapter, cipher).Result; // Use factory to create LockIt-object. Assert.That(async () => { var lockState = lockIt.CloseAsync(); controlCharacteristic.ValueUpdated += Raise.EventWith(new object(), new CharacteristicUpdatedEventArgs(stateCharacteristic)); await lockState; }, Throws.InstanceOf()); } } }