using System; using System.Threading.Tasks; using Newtonsoft.Json; using NSubstitute; using NUnit.Framework; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.State; using TINK.Model.User; using TINK.Repository.Exception; using TINK.Repository.Request; using TINK.Repository.Response; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; using TINK.Services.Geolocation; using TINK.View; using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { [TestFixture] public class TestBookedUnknown { /// /// Test construction of object. /// [Test] public void Testctor() { var handler = new BookedUnknown( Substitute.For(), () => true, // isConnectedDelegate (isConnexted) => Substitute.For(), Substitute.For(), Substitute.For(), () => Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); // Verify prerequisites. Assert.AreEqual("Open lock", handler.ButtonText); Assert.IsTrue(handler.IsButtonVisible); Assert.AreEqual("Close lock", handler.LockitButtonText); Assert.IsTrue(handler.IsLockitButtonVisible); } /// /// Use case: Opens lock /// Final state: Booked opened. /// [Test] public void TestOpen() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Open lock /// Final state: Same as initial state. /// [Test] public void TestOpenOpenFailsOutOfReachException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new OutOfReachException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Lock could not be opened!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Disconnected" after action Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); Assert.IsFalse(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock /// Final state: Same as initial state. /// [Test] public void TestOpenOpenFailsCouldntOpenBoltBlockedException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new CouldntOpenBoldIsBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Lock could not be opened!", "Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Unknown" after action Assert.AreEqual("Open lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock /// Final state: Same as initial state. /// [Test] public void TestOpenOpenFailsCouldntOpenInconsistentStateExecptionClosed() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Closed)); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Lock could not be opened!", "Lock reports it is still closed. Please try again!", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action Assert.AreEqual("End rental", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock /// Final state: Same as initial state. /// [Test] public void TestOpenOpenFailsCouldntOpenInconsistentStateExecption() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new CouldntOpenBoldStatusIsUnknownException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Lock could not be opened!", "Position of lock bolt is unknown. Lock could be closed or open. Please try again!", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Disconnected" after action Assert.AreEqual("Open lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock /// Final state: Same as initial state. /// [Test] public void TestOpenOpenFailsException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = string.Empty; viewService.DisplayAdvancedAlert( "Lock could not be opened!", "Exception message.", "Please try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Disconnected" after action Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); Assert.IsFalse(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Opens lock /// Final state: Booked opened. /// [Test] public void TestOpenGetBatteryPercentageAsyncThrowsOutOfReachException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success locks[0].GetBatteryPercentageAsync().Returns>(x => throw new OutOfReachException()); bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Battery status can only be read when bike is nearby."; bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Opens lock /// Final state: Booked opened. /// [Test] public void TestOpenGetBatteryPercentageAsyncThrowsExcepton() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success locks[0].GetBatteryPercentageAsync().Returns>(x => throw new Exception()); bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Battery status cannot be read."; bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Open lock /// Final state: Booked open. /// [Test] public void TestOpenUpdateLockingStateFailsWebConnectFailureException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(LockitLockingState.Open); connector.Command.UpdateLockingStateAsync(bike, null).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Open); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Internet must be available for updating lock status. Please establish an Internet connection!"; bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Open lock /// Final state: Booked open. /// [Test] public void TestOpenUpdateLockingStateFailsException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(LockitLockingState.Open); connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Open); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Connection error on updating locking status."; bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Open lock /// Final state: Booked open. /// [Test] public void TestOpenUpdateLockingStateFailsResponseException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns(LockitLockingState.Open); connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Open); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "

Lock is opening.
Please wait until it is completely open.

"; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Status error on updating lock state."; bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual(string.Empty, subsequent.LockitButtonText); Assert.That(subsequent.IsLockitButtonVisible, Is.False); } /// /// Use case: Close lock /// Final state: Booked closed. /// [Test] public void TestClose() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); // Return lock state indicating success bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); handler.ReportStep(Step.UpdateLockingState); return Task.CompletedTask; } ); var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Open" after action //Assert.AreEqual("End rental", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock /// Final state: Same as initial state. /// [Test] public void TestCloseCloseFailsOutOfReachException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.CloseLockAsync(Arg.Any(), Arg.Any()) .Returns(async x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); await handler.ReportStateAsync(CloseCommand.State.OutOfReachError, ""); throw new OutOfReachException(); } ); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); /// If is fired lock state is set to state unknown because disconnected. var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Lock could not be closed!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked disconnected" after action Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); Assert.IsFalse(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock /// Final state: Same as initial state. /// [Test] public void TestCloseCloseFailsException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(async x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); await handler.ReportStateAsync(State.GeneralCloseError, "Exception message."); throw new Exception("Exception message."); } ); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // If CloseLock fires an exception of type Exception lock state is set to unknown. var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert( "Lock could not be closed!", "Exception message.", "OK" ); bikesViewModel.ActionText = "Updating..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Disconnected" after action Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); Assert.IsFalse(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock /// Final state: Booked closed. /// [Test] public void TestCloseUpdateLockingStateFailsWebConnectFailureException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(async x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); handler.ReportStep(Step.UpdateLockingState); await handler.ReportStateAsync(CloseCommand.State.WebConnectFailed, "Context info"); } ); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "Internet must be available for updating lock status. Please establish an Internet connection!"; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action Assert.AreEqual("End rental", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: close lock /// Final state: Booked closed. /// [Test] public void TestCloseUpdateLockingStateFailsException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.CloseLockAsync(Arg.Any(), Arg.Any()) .Returns(async x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); handler.ReportStep(Step.UpdateLockingState); await handler.ReportStateAsync(CloseCommand.State.BackendUpdateFailed, "Exception message."); } ); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "Connection error on updating locking status."; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action Assert.AreEqual("End rental", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: close lock /// Final state: Booked closed. /// [Test] public void TestCloseUpdateLockingStateFailsResponseException() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var activeUser = Substitute.For(); var handler = new BookedUnknown( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.CloseLockAsync(Arg.Any(), Arg.Any()) .Returns(async x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. handler.ReportStep(Step.ClosingLock); handler.ReportStep(Step.UpdateLockingState); await handler.ReportStateAsync(CloseCommand.State.ResponseIsInvalid, "Some invalid data received"); } ); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Stay with the bike until the lock is closed."; bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "Status error on updating lock state."; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action Assert.AreEqual("End rental", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } } }