using System; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using NSubstitute; using NUnit.Framework; using TINK.Model; 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; using Xamarin.Essentials; namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { [TestFixture] public class TestBookedOpen { /// /// Test construction of object. /// [Test] public void Testctor() { var handler = new BookedOpen( 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("Close lock & return bike", handler.ButtonText); Assert.IsTrue(handler.IsButtonVisible); Assert.AreEqual("Close lock", handler.LockitButtonText); Assert.IsTrue(handler.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Comment: User cancels operation. /// Final state: Booked and open. /// Similar to TestReservedClosed::TestBookAndOpenCancelBook /// [Test] public void TestCloseAndReturnCancelReturn() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0 Allround Mono?", "Yes", "No").Returns(Task.FromResult(false)); var subsequent = handler.HandleRequestOption1().Result; // Verify behaviour 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("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Disposable closed. /// Similar to TestReservedClosed::TestBookAndOpen /// [Test] public void TestCloseAndReturn() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success geolocation.GetAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(new Location(1, 2))); bike.State.Value.Returns(InUseStateEnum.Disposable); // Return call leads to setting of state to disposable. var subsequent = handler.HandleRequestOption1().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked geolocation.GetAsync(Arg.Any(), Arg.Any()); // Geolocation must be retrieved bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Is(x => x.Latitude == 1 && x.Longitude == 2), Arg.Any()); // Booking must be performed bikesViewModel.ActionText = "Disconnecting lock..."; locks.DisconnectAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Disposable Closed" after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Same as initial state. /// [Test] public void TestCloseAndReturnCloseFailsOutOfReachException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(x => throw new OutOfReachException()); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = ""; bike.LockInfo.State = LockingState.UnknownDisconnected; connector.Command.UpdateLockingStateAsync(Arg.Any()); viewService.DisplayAlert("Lock can not be closed!", "Lock cannot be closed until bike is near.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; 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); } [Test] public void TestCloseAndReturnStartReturningBikeWebConnectFailureException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); connector.Command.StartReturningBike(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("hoppla"))); bike.LockInfo.State.Returns(LockingState.Open); // If locking fails bike remains open. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = ""; viewService.DisplayAdvancedAlert( "Connection error when returning the bike!", "Internet must be available when returning the bike.\r\nIs WIFI available/ mobile networt available and mobile data activated / ... ?", "Context info", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked open" after action Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } [Test] public void TestCloseAndReturnStartReturningBikeException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); connector.Command.StartReturningBike(bike).Returns(x => throw new Exception("Context info", new Exception("hoppla"))); bike.LockInfo.State.Returns(LockingState.Open); // If locking fails bike remains open. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = ""; viewService.DisplayAlert( "Error returning bike!", "Context info", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked open" after action Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Same as initial state. /// [Test] public void TestCloseAndReturnCloseFailsException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(x => throw new Exception("Blu")); bike.LockInfo.State.Returns(LockingState.Open); // If locking fails bike remains open. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = ""; bike.LockInfo.State = LockingState.UnknownDisconnected; connector.Command.UpdateLockingStateAsync(Arg.Any()); viewService.DisplayAlert("Lock can not be closed!", "Blu", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked open" 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 and return. /// Final state: Same as initial state. /// [Test] public void TestCloseAndReturnCloseFailsCouldntCloseMovingException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(x => throw new CouldntCloseMovingException()); //bike.LockInfo.State.Returns(LockingState.Open); // If locking fails bike remains open. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = ""; bike.LockInfo.State = LockingState.Open; connector.Command.UpdateLockingStateAsync(Arg.Any()); viewService.DisplayAlert( "Lock can not be closed!", "Lock can only be closed if bike is not moving. Please park bike and try again.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked open" after action Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Same as initial state. /// [Test] public void TestCloseAndReturnCloseFailsNotClosed() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Open); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bike.LockInfo.State = LockingState.Open; bikesViewModel.ActionText = ""; connector.Command.UpdateLockingStateAsync(Arg.Any()); viewService.DisplayAlert("Lock can not be closed!", "After try to close lock state open is reported.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked open" after action Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public async Task TestCloseAndReturnGetGeolocationFailsException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); geolocation.GetAsync(Arg.Any(), Arg.Any()).Returns(Task.FromException(new Exception("noloc"))); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = await handler.HandleRequestOption1(); await locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = ""; viewService.DisplayAdvancedAlert("Error Query Location!", "Closing the lock and ending the rental is not possible.", "noloc", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public void TestCloseAndReturnReturnFailsWebConnectFailureException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); connector.Command.DoReturn(bike, Arg.Any(), Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; bikesViewModel.ActionText = ""; viewService.DisplayAdvancedAlert( "Connection error when returning the bike!", "Internet must be available when returning the bike.\r\nIs WIFI available/ mobile networt available and mobile data activated / ... ?", "Context info", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public void TestCloseAndReturnReturnFailsException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); connector.Command.DoReturn(bike, Arg.Any(), Arg.Any()).Returns(x => throw new System.Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error returning bike!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public void TestCloseAndReturnReturnFailsNotAtStationException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); NotAtStationException.IsNotAtStation("Failure 2178: bike 1545 out of GEO fencing. 15986 meter distance to next station 77. OK: bike 1545 locked confirmed", out NotAtStationException notAtStationException); connector.Command.DoReturn(bike, Arg.Any(), Arg.Any()).Returns(x => throw notAtStationException); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); var subsequent = handler.HandleRequestOption1().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error returning bike!", "Returning bike outside of station is not possible. Distance to station 77 is 15986 m.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public void TestCloseAndReturnReturnFailsNoGPSDataException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); NoGPSDataException.IsNoGPSData("Failure 2245: No GPS data, state change forbidden.", out NoGPSDataException noGPSDataException); connector.Command.DoReturn(bike, Arg.Any(), Arg.Any()).Returns(x => throw noGPSDataException); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error returning bike!", "Returning bike at an unknown location is not possible.\r\nBike can only be returned if bike is in reach and location information is available.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Close lock and return. /// Final state: Booked locked. /// [Test] public void TestCloseAndReturnReturnFailsResponseException() { 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Close lock and return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); connector.Command.DoReturn(bike, Arg.Any(), Arg.Any()).Returns(x => throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state remains unchanged if closing fails. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "Start query location..."; geolocation.GetAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Starting bike return..."; connector.Command.StartReturningBike(bike); // Notify about start bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; bikesViewModel.ActionText = ""; viewService.DisplayAdvancedAlert("Statusfehler beim Zurückgeben des Rads!", "Outer message.", "Some invalid data received!", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked closed" after action //Assert.AreEqual("Return bike", 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 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action // Following two assertions are deactivated temporarily in context of issue https://dev.azure.com/TeilRad/sharee.bike%20Buchungsplattform/_workitems/edit/440/. //Assert.AreEqual("Return bike", 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns>(x => throw new OutOfReachException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Open); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Lock can not be closed!", "Lock cannot be closed until bike is near.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns>(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Open); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Lock can not be closed!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); // Return lock state indicating success connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "No web error on updating locking status."; bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action // Following two assertions are deactivated temporarily in context of issue https://dev.azure.com/TeilRad/sharee.bike%20Buchungsplattform/_workitems/edit/440/. //Assert.AreEqual("Return bike", 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); // Return lock state indicating success connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Connection error on updating locking status."; bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action // Following two assertions are deactivated temporarily in context of issue https://dev.azure.com/TeilRad/sharee.bike%20Buchungsplattform/_workitems/edit/440/. //Assert.AreEqual("Return bike", 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 BookedOpen( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].CloseAsync() .Returns(LockitLockingState.Closed); // Return lock state indicating success 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); var subsequent = handler.HandleRequestOption2().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Closing lock..."; locks.Received()[0].CloseAsync(); // Lock must be closed bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); bikesViewModel.ActionText = "Status error on updating lock state."; bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state "Booked Closed" after action // Following two assertions are deactivated temporarily in context of issue https://dev.azure.com/TeilRad/sharee.bike%20Buchungsplattform/_workitems/edit/440/. //Assert.AreEqual("Return bike", subsequent.ButtonText); //Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } } }