using NSubstitute; using NUnit.Framework; using System; using System.Threading.Tasks; using TINK.Model.Bike.BluetoothLock; using TINK.Model.Bikes.Bike.BluetoothLock; using TINK.Model.Connector; using TINK.Repository.Exception; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; using TINK.Services.Geolocation; using TINK.Model.State; using TINK.View; using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using TINK.Model.User; using TINK.Repository.Request; using TINK.Repository.Response; using Newtonsoft.Json; using TINK.Model.Device; using System.Threading; using TINK.Model; namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { [TestFixture] public class TestBookedClosed { /// /// Test construction of object. /// [Test] public void Testctor() { var handler = new BookedClosed( 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("Return bike", handler.ButtonText); Assert.IsTrue(handler.IsButtonVisible); Assert.AreEqual("Open lock", handler.LockitButtonText); Assert.IsTrue(handler.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Comment: User deceide to abort returning bike /// Final state: Same as initial state. /// [Test] public void TestReturnCancel() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(false)); 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 viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No"); bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Disposable closed /// [Test] public void TestReturnOutOfReach() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Disconnected); // Simulate bike out of reach. bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().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 = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Is(x => x == null)); 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 after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Disposable closed /// [Test] public void TestReturnLockInReach() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. geolocation.GetAsync(Arg.Any< CancellationToken?>(), Arg.Any()).Returns(Task.FromResult( new Xamarin.Essentials.Location(7, 9) )); bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked 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 = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Is(x => x != null)); 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 after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Disposable closed /// [Test] public void TestReturnLockInReachNoGeolocation() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. geolocation.GetAsync(Arg.Any(), Arg.Any()).Returns(x => throw new Exception()); bike.State.Value.Returns(InUseStateEnum.Disposable); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behaviour Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked 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 = "Query location..."; bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Is(x => x == null)); 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 after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Same as initial state. /// [Test] public void TestReturnReturnFailsWebConnectFailureException() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new Exception("hoppla"))); bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); 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 = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Any()); 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 after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Same as initial state. /// [Test] public void TestReturnReturnFailsNotAtStationException() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. NotAtStationException.IsNotAtStation("Failure 2178: bike 1545 out of GEO fencing. 15986 meter distance to next station 42. OK: bike 1545 locked confirmed", out NotAtStationException notAtStationException); connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw notAtStationException); bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. 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 = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Any()); bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error returning bike!", "Returning bike outside of station is not possible. Distance to station 42 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 after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Same as initial state. /// [Test] public void TestReturnReturnFailsNoGPSDataException() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. NoGPSDataException.IsNoGPSData("Failure 2245: No GPS data, state change forbidden.", out NoGPSDataException noGPSDataException); connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw noGPSDataException); bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. 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 = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Any()); bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error returning bike!", "Returning bike at an unknown location is not possible.\r\nBike can be returned if\r\n- location information is available when closing lock\r\n- bike is in reach and location information is available when pressing button \"Return bike\"", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = ""; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Same as initial state. /// [Test] public void TestReturnReturnFailsResponseException() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. 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 = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Any()); 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 after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Return bike. /// Final state: Same as initial state. /// [Test] public void TestReturnReturnFailsException() { 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Return bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true)); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. connector.Command.DoReturn(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Booked); // Reqesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. 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 = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Returning bike..."; connector.Command.DoReturn(bike, Arg.Any()); 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 after action Assert.AreEqual("Return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Open lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open 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 BookedClosed( 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.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 = "Opening lock..."; 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.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("Close lock & return bike", 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 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 BookedClosed( 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.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 = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error while opening lock!", "Lock cannot be opened 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(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 TestOpenOpenFailsCouldntOpenBoldBlockedException() { 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 BookedClosed( 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.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 = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error while opening lock!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening 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 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 BookedClosed( 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.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 = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error while opening lock!", "After try to open lock state closed 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 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: 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 BookedClosed( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); locks[0].OpenAsync() .Returns>(x => throw new CouldntOpenBoldWasBlockedException()); bike.State.Value.Returns(InUseStateEnum.Booked); bike.LockInfo.State.Returns(LockingState.Closed); 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 = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Lock can not be opened!", "Lock was blocked and might still be. Please ensure that no obstacle prevents lock from opening 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 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 BookedClosed( 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.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 = "Opening lock..."; locks.Received()[0].OpenAsync(); // Lock must be closed bikesViewModel.ActionText = ""; viewService.DisplayAlert("Error while opening lock!", "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(nameof(BookedDisconnected), subsequent.ButtonText); Assert.IsFalse(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open 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 BookedClosed( 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.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 = "Opening lock..."; 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.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("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Open 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 BookedClosed( 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.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 = "Opening lock..."; 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.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("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// 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 BookedClosed( 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.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 = "Opening lock..."; 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 = "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 Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// 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 BookedClosed( 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.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 = "Opening lock..."; 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.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("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// 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 BookedClosed( 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.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 = "Opening lock..."; 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.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("Close lock & return bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } } }