using System; using System.Threading.Tasks; using Newtonsoft.Json; using NSubstitute; using NUnit.Framework; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; 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; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { [TestFixture] public class TestReservedClosed { /// /// Test construction of object. /// [Test] public void Testctor() { var handler = new ReservedClosed( 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("Cancel reservation", handler.ButtonText); Assert.IsTrue(handler.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", handler.LockitButtonText); Assert.IsTrue(handler.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Comment: User decide to abort canceling (user is ased whether to really cancel) /// Final state: Same as initial state. /// [Test] public void TestCancelReservationCancel() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false)); var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No"); bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Final state: Disposable. /// [Test] public void TestCancelReservation() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. 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 = "Canceling reservation..."; connector.Command.DoCancelReservation(bike); // Booking must be performed bikesViewModel.ActionText = "Disconnecting lock..."; locks.DisconnectAsync(Arg.Any(), Arg.Any()); 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("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Comment: Exception is thrown. /// Final state: Same as initial state. /// [Test] public void TestCancelReservationDoCancelReservationInvalidAuthorizationResponseException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); var response = JsonConvert.DeserializeObject(@" { ""response"" : ""authorization"", ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", ""user_group"" : [ ""TINK"", ""Konrad"" ], ""response_state"" : ""OK"", ""apiserver"" : ""https://tinkwwp.copri-bike.de"" }"); connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response)); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // 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 = "Canceling reservation..."; connector.Command.DoCancelReservation(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert( "Reservation could not be canceled!", "Your session has expired. Please login new 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 after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Comment: Canceling reservation fails. /// Final state: Same as initial state. /// [Test] public void TestCancelReservationDoCancelReservationWebConnectFailureException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // 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 = "Canceling reservation..."; connector.Command.DoCancelReservation(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert( "Reservation could not be canceled!", "A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. 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 after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Comment: Canceling reservation fails. /// Final state: Same as initial state. /// [Test] public void TestCancelReservationDoCancelReservationException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // 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 = "Canceling reservation..."; connector.Command.DoCancelReservation(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAdvancedAlert( "Reservation could not be canceled!", "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 after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: user cancels operation. /// Final state: Reserved and closed. /// [Test] public void TestBookAndOpenCancelBook() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false)); var subsequent = handler.HandleRequestOption2().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpen() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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 and book bike. /// Comment: COPRI request to book bike fails (WebConnectFailureException). Lock is opened and closed again. /// Final state: Reserved closed. /// [Test] public void TestBookAndOpenBookingFailsWebConnectFailureException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. connector.Command.DoBookAsync(bike, LockingAction.Open).Returns(x => throw new WebConnectFailureException("Context info", new Exception("Tst"))); bike.State.Value.Returns(InUseStateEnum.Reserved); // State remains reserved because booking request failed. 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert( "Bike could not be rented!", "A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. 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 "reserved closed" after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: COPRI request to book bike fails (Exception). Lock is opened and closed again. /// Final state: Reserved closed. /// [Test] public void TestBookAndOpenBookingFailsException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. connector.Command.DoBookAsync(bike, LockingAction.Open).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Reserved); // When booking fails state remains reserved. 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAdvancedAlert( "Bike could not be rented!", "Please try again.", "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 "reserved closed" after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike, /// Comment: Unlocking bike throws an OutOfReach-Exception. /// Final state: Reserved unknown. /// [Test] public void TestBookAndOpenOpenFailsExceptionOutOfReachException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns>(x => { throw new OutOfReachException(); }); bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = string.Empty; viewService.DisplayAdvancedAlert( "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.", "Attention! Your rental has already started.\r\nIf the lock still won't open, make sure the lock is closed and end rental.\r\nImportant: Send an email to customer support (otherwise your paid rental will continue!) with: Problem description, bike number, drop-off station.", "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 after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: Unlocking bike throws an exception. /// Final state: Reserved unknown. /// [Test] public void TestBookAndOpenOpenFailsException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns>(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = string.Empty; viewService.DisplayAdvancedAlert( "Lock could not be opened!", "Please try again.", "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 after action Assert.AreEqual("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: Unlocking bike does not return state bike opened. /// Final state: Reserved unknown. /// [Test] public void TestBookAndOpenOpenFailsNotOpen() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)null)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking state does not change. 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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("Cancel reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open lock and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpenBatteryPercentageOutOfReachException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. locks[0].GetBatteryPercentageAsync().Returns>(x => throw new OutOfReachException()); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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: Open lock and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpenBatteryPercentageException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. locks[0].GetBatteryPercentageAsync().Returns>(x => throw new Exception()); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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 and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpenUpdateLockingStateFailsWebConnectFailureException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. connector.Command.UpdateLockingStateAsync(bike, null).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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 and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpenUpdateLockingStateFailsException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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 and book bike. /// Comment: Bike gets opened. /// Final state: Booked open. /// [Test] public void TestBookAndOpenUpdateLockingStateFailsResponseException() { 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 ReservedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); 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 = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened 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); } } }