using System; using System.Threading.Tasks; using NSubstitute; using NUnit.Framework; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; using TINK.Model.Connector; using TINK.Model.Device; using TINK.Model.State; using TINK.Model.User; using TINK.Repository.Exception; using TINK.Services.BluetoothLock; using TINK.Services.BluetoothLock.Exception; using TINK.Services.BluetoothLock.Tdo; using TINK.Services.Geolocation; using TINK.View; using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler { [TestFixture] public class TestDisposableDisconnected { /// /// Test construction of object. /// [Test] public void Testctor() { var handler = new DisposableDisconnected( 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("Reserve bike", handler.ButtonText); Assert.IsTrue(handler.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", handler.LockitButtonText); Assert.IsFalse(handler.IsLockitButtonVisible); } /// /// Use case: Cancel reservation. /// Final state: Same as initial state. /// [Test] public void TestReserveAndConnectCancel() { 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(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(false)); var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No"); 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: Reserve bike. /// Final state: Reserved closed. /// [Test] public void TestReserveAndConnect() { 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 timeOuts = Substitute.For(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); locks.ConnectAsync(Arg.Any(), Arg.Any()) .Returns(Task.FromResult(new LockInfoTdo.Builder { State = LockitLockingState.Closed }.Build())); // Return lock state indicating success locks.TimeOut.Returns(timeOuts); viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); locks[0].OpenAsync() .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior 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 = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = "Searching lock..."; locks.ConnectAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = string.Empty; bikesViewModel.ActionText = "Renting bike..."; connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed bikesViewModel.ActionText = "

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

"; locks.Received()[0].OpenAsync(); // Lock must be opened bikesViewModel.ActionText = "Reading charging level..."; locks[0].GetBatteryPercentageAsync(); bikesViewModel.ActionText = "Updating lock state..."; connector.Command.UpdateLockingStateAsync(bike, null); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Close lock & end rental", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Close lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Same as initial state. /// [Test] public void TestReserveAndConnectReserveFailsBookingDeclinedException() { 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(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoReserve(bike).Returns(x => throw new BookingDeclinedException(7)); // Booking must be performed bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior 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 = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Hint", string.Format("A reservation of bike {0} was rejected because the maximum allowed number of {1} reservations/rentals had already been made.", 0, 7), "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Same as initial state. /// [Test] public void TestReserveAndConnectReserveFailsWebConnectFailureException() { 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(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoReserve(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior 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 = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Connection error when reserving the bike!", "Context info.\r\nIs WIFI/mobile network available and mobile data activated?", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Same as initial state. /// [Test] public void TestReserveAndConnectReserveFailsException() { 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(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); connector.Command.DoReserve(bike).Returns(x => throw new Exception("Exception message.")); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior 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 = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = string.Empty; viewService.DisplayAlert("Error when reserving the bike!", "Exception message.", "OK"); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Reserved unknown. /// [Test] public void TestReserveAndConnectConnectOutOfReachException() { 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 timeOuts = Substitute.For(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); locks.ConnectAsync(Arg.Any(), Arg.Any()).Returns(x => throw new OutOfReachException()); locks.TimeOut.Returns(timeOuts); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = "Searching lock..."; locks.ConnectAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Lock out of reach"; bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Reserved unknown. /// [Test] public void TestReserveAndConnectConnectException() { 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 timeOuts = Substitute.For(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); locks.ConnectAsync(Arg.Any(), Arg.Any()).Returns(x => throw new Exception("Exception message.")); locks.TimeOut.Returns(timeOuts); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // Requesthandler factory queries lock state to create appropriate request handler object. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior Received.InOrder(() => { bikesViewModel.Received(1).IsIdle = false; // GUI must be locked bikesViewModel.ActionText = "One moment please..."; pollingManager.StopUpdatePeridically(); // Polling must be stopped before any COPR and lock service action bikesViewModel.ActionText = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = "Searching lock..."; locks.ConnectAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Lock not found"; bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } /// /// Use case: Reserve bike. /// Final state: Reserved unknown. /// [Test] public void TestReserveAndConnectConnectStateUnknown() { 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 timeOuts = Substitute.For(); bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15)); var handler = new DisposableDisconnected( bike, () => true, // isConnectedDelegate (isConnexted) => connector, geolocation, locks, () => pollingManager, Substitute.For(), viewService, bikesViewModel, activeUser); bike.Id.Returns("0"); viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true)); locks.TimeOut.Returns(timeOuts); bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); // Connect did not throw an exception but lock state is still unknown. var subsequent = handler.HandleRequestOption1().Result; // Verify behavior 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 = "Reserving bike..."; connector.Command.DoReserve(bike); // Booking must be performed bikesViewModel.ActionText = "Searching lock..."; locks.ConnectAsync(Arg.Any(), Arg.Any()); bikesViewModel.ActionText = "Updating..."; pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked }); // Verify state after action Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("Search lock", subsequent.LockitButtonText); Assert.IsTrue(subsequent.IsLockitButtonVisible); } [Test] public void TestNotsupported() { var handler = new DisposableDisconnected( Substitute.For(), () => true, // isConnectedDelegate (isConnexted) => Substitute.For(), Substitute.For(), Substitute.For(), () => Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For(), Substitute.For()); var subsequent = handler.HandleRequestOption2().Result; // Verify state after action Assert.AreEqual("Reserve bike", subsequent.ButtonText); Assert.IsTrue(subsequent.IsButtonVisible); Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText); Assert.IsFalse(subsequent.IsLockitButtonVisible); } } }