mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2024-11-17 07:36:26 +01:00
1359 lines
52 KiB
C#
1359 lines
52 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json;
|
|
using NSubstitute;
|
|
using NSubstitute.ExceptionExtensions;
|
|
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 static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand;
|
|
|
|
namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
|
|
{
|
|
[TestFixture]
|
|
public class TestBookedClosed
|
|
{
|
|
/// <summary>
|
|
/// Test construction of object.
|
|
/// </summary>
|
|
[Test]
|
|
public void Testctor()
|
|
{
|
|
var handler = new BookedClosed(
|
|
Substitute.For<IBikeInfoMutable>(),
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => Substitute.For<IConnector>(),
|
|
Substitute.For<IGeolocationService>(),
|
|
Substitute.For<ILocksService>(),
|
|
() => Substitute.For<IPollingUpdateTaskManager>(),
|
|
Substitute.For<ISmartDevice>(),
|
|
Substitute.For<IViewService>(),
|
|
Substitute.For<IBikesViewModel>(),
|
|
Substitute.For<IUser>());
|
|
|
|
// Verify prerequisites.
|
|
//Assert.AreEqual("End rental", handler.ButtonText);
|
|
//Assert.IsTrue(handler.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", handler.LockitButtonText);
|
|
Assert.IsTrue(handler.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Booked, lock state unknown.
|
|
/// </summary>
|
|
/// <remarks> Replaces test TestReturnOutOfReach which was removed for sharee.bike version ~3.0.362. </remarks>
|
|
[Test]
|
|
public void TestReturnLastGeolocatonNullLockOutOfReach()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
bike.GetLockedBikeLocationAsync(Arg.Any<IGetLockedLocationCommandListener>()).Throws((x) =>
|
|
{
|
|
handler.ReportStep(Step.StartingQueryLocation);
|
|
handler.ReportStateAsync(TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand.State.DisconnetedNoLocationError, "").Wait();
|
|
handler.ReportStep(Step.DisconnectingLockOnDisconnectedNoLocationError);
|
|
handler.ReportStateAsync(TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand.State.QueryLocationSucceeded, "").Wait();
|
|
throw new Exception();
|
|
});
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
viewService.DisplayAlert(
|
|
"Rental could not be terminated!",
|
|
"We could not assign the bike to any station. For this we need your location information while you are standing right next to the bike. Only then your rental can be terminated!\r\n\r\nApproach the bike lock, turn on Bluetooth and Location services and try again.",
|
|
"OK");
|
|
bikesViewModel.ActionText = "Disconnecting lock...";
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("BookedDisconnected", subsequent.ButtonText);
|
|
Assert.IsFalse(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Booked, lock state unknown.
|
|
/// </summary>
|
|
/// <remarks>Replaces test TestReturnLockInReachNoGeolocation which was removed for sharee.bike older ~ 3.0.362.</remarks>
|
|
[Test]
|
|
public void TestReturnStartGetGeolocationException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
bike.GetLockedBikeLocationAsync(Arg.Any<IGetLockedLocationCommandListener>()).Throws((x) =>
|
|
{
|
|
handler.ReportStep(Step.StartingQueryLocation);
|
|
handler.ReportStateAsync(TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand.State.QueryLocationFailed, "Ups...").Wait();
|
|
throw new Exception();
|
|
});
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state does not change.
|
|
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "Query location...";
|
|
viewService.DisplayAlert(
|
|
"Rental could not be terminated!",
|
|
"Grant Location permission, activate Location services and try again.",
|
|
"OK");
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Booked, lock state unknown.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnGetGeolocationException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
bike.GetLockedBikeLocationAsync(Arg.Any<IGetLockedLocationCommandListener>())
|
|
.Returns((x) =>
|
|
{
|
|
handler.ReportStep(Step.StartingQueryLocation);
|
|
handler.ReportStateAsync(TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand.State.QueryLocationFailed, "Ups...").Wait();
|
|
return Task.FromException<LocationDto>(new Exception("Ups..."));
|
|
});
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state does not change.
|
|
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change.
|
|
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "Query location...";
|
|
viewService.DisplayAlert(
|
|
"Rental could not be terminated!",
|
|
"Grant Location permission, activate Location services and try again.",
|
|
"OK");
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Disposable closed
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnLockInReach()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of bike Nr. 0?", "Yes", "No").Returns(Task.FromResult(true));
|
|
|
|
bike.GetLockedBikeLocationAsync(Arg.Any<IGetLockedLocationCommandListener>()).Returns((x) =>
|
|
{
|
|
handler.ReportStep(Step.StartingQueryLocation);
|
|
handler.ReportStateAsync(TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand.State.QueryLocationSucceeded, string.Empty).Wait();
|
|
return Task.FromResult(new LocationDto.Builder().Build());
|
|
});
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Disposable); // Request handler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "Query location...";
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x != null));
|
|
bikesViewModel.ActionText = "Disconnecting lock...";
|
|
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
|
|
Assert.IsFalse(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnReturnFailsWebConnectFailureException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of 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<LocationDto>()).Returns<BookingFinishedModel>(x => throw new WebConnectFailureException("Context info", new Exception("hoppla")));
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert(
|
|
"Rental could not be terminated!",
|
|
"A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.",
|
|
"OK");
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
//Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
//Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnReturnFailsNotAtStationException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of 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<LocationDto>()).Returns<BookingFinishedModel>(x =>
|
|
throw notAtStationException);
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Rental could not be terminated!", "End rental outside or at wrong station is not possible. Distance to next suitable station 42 is 15986 m.", "OK");
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
//Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
//Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnReturnFailsNoGPSDataException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of 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<LocationDto>()).Returns<BookingFinishedModel>(x =>
|
|
throw noGPSDataException);
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Rental could not be terminated!", "End rental at an unknown location is not possible.\r\nRental can be ended if\r\n- location information is available when closing lock\r\n- bike is in reach and location information is available when pressing button \"End rental\"", "OK");
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
//Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
//Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnReturnFailsResponseException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of 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<LocationDto>()).Returns<BookingFinishedModel>(x =>
|
|
throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message."));
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Requesthandler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAdvancedAlert("Rental could not be terminated!", "Outer message.", "Some invalid data received!", "OK");
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
//Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
//Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: End rental.
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestReturnReturnFailsException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
bike.Id.Returns("0");
|
|
|
|
viewService.DisplayAlert(string.Empty, "End rental of 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<LocationDto>()).Returns<BookingFinishedModel>(x => throw new Exception("Exception message."));
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object.
|
|
bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object.
|
|
|
|
var subsequent = handler.HandleRequestOption1().Result;
|
|
|
|
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
connector.Command.DoReturn(bike, Arg.Any<LocationDto>());
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert(
|
|
"Rental could not be terminated!",
|
|
"Exception message.",
|
|
"OK"
|
|
);
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
//Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
//Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked opened.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpen()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
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 behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = "Reading charging level...";
|
|
locks[0].GetBatteryPercentageAsync();
|
|
bikesViewModel.ActionText = "Updating lock state...";
|
|
connector.Command.UpdateLockingStateAsync(bike, null);
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenOpenFailsOutOfReachException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
bike.LockInfo.State.Returns(LockingState.Closed);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Lock could not be opened!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK");
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state "Booked Disconnected" after action
|
|
Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText);
|
|
Assert.IsFalse(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenOpenFailsCouldntOpenBoltBlockedException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenBoldIsBlockedException());
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
bike.LockInfo.State.Returns(LockingState.Closed);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Lock could not be opened!", "Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.", "OK");
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state "Booked Unknown" after action
|
|
Assert.AreEqual("Open lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenOpenFailsCouldntOpenInconsistentStateExecptionClosed()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Closed));
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
bike.LockInfo.State.Returns(LockingState.Closed);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Lock could not be opened!", "Lock reports it is still closed. Please try again!", "OK");
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state "Booked Closed" after action
|
|
Assert.AreEqual("End rental", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Open lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenOpenFailsCouldntOpenInconsistentStateExecption()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenBoldStatusIsUnknownException());
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
bike.LockInfo.State.Returns(LockingState.Closed);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAlert("Lock could not be opened!", "Position of lock bolt is unknown. Lock could be closed or open. Please try again!", "OK");
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state "Booked Disconnected" after action
|
|
Assert.AreEqual("Open lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Same as initial state.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenOpenFailsException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
bike.LockInfo.State.Returns(LockingState.Closed);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = string.Empty;
|
|
viewService.DisplayAdvancedAlert(
|
|
"Lock could not be opened!",
|
|
"Exception message.",
|
|
"Please try again.",
|
|
"OK"
|
|
);
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state "Booked Disconnected" after action
|
|
Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText);
|
|
Assert.IsFalse(subsequent.IsButtonVisible);
|
|
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
|
|
Assert.IsTrue(subsequent.IsLockitButtonVisible);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked opened.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenGetBatteryPercentageAsyncThrowsOutOfReachException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success
|
|
|
|
locks[0].GetBatteryPercentageAsync().Returns<Task<double>>(x => throw new OutOfReachException());
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = "Reading charging level...";
|
|
locks[0].GetBatteryPercentageAsync();
|
|
bikesViewModel.ActionText = "Battery status can only be read when bike is nearby.";
|
|
bikesViewModel.ActionText = "Updating lock state...";
|
|
connector.Command.UpdateLockingStateAsync(bike, null);
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked opened.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenGetBatteryPercentageAsyncThrowsExcepton()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success
|
|
|
|
locks[0].GetBatteryPercentageAsync().Returns<Task<double>>(x => throw new Exception());
|
|
|
|
bike.State.Value.Returns(InUseStateEnum.Booked);
|
|
|
|
var subsequent = handler.HandleRequestOption2().Result;
|
|
|
|
// Verify behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = "Reading charging level...";
|
|
locks[0].GetBatteryPercentageAsync();
|
|
bikesViewModel.ActionText = "Battery status cannot be read.";
|
|
bikesViewModel.ActionText = "Updating lock state...";
|
|
connector.Command.UpdateLockingStateAsync(bike, null);
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked open.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenUpdateLockingStateFailsWebConnectFailureException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
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 behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
locks.Received()[0].OpenAsync(); // Lock must be closed
|
|
bikesViewModel.ActionText = "Reading charging level...";
|
|
locks[0].GetBatteryPercentageAsync();
|
|
bikesViewModel.ActionText = "Updating lock state...";
|
|
connector.Command.UpdateLockingStateAsync(bike, null);
|
|
bikesViewModel.ActionText = "Internet must be available for updating lock status. Please establish an Internet connection!";
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked open.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenUpdateLockingStateFailsException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns(LockitLockingState.Open);
|
|
|
|
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).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 behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
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<LocationDto>());
|
|
bikesViewModel.ActionText = "Connection error on updating locking status.";
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use case: Open lock
|
|
/// Final state: Booked open.
|
|
/// </summary>
|
|
[Test]
|
|
public void TestOpenUpdateLockingStateFailsResponseException()
|
|
{
|
|
var bike = Substitute.For<IBikeInfoMutable>();
|
|
var connector = Substitute.For<IConnector>();
|
|
var command = Substitute.For<ICommand>();
|
|
var geolocation = Substitute.For<IGeolocationService>();
|
|
var locks = Substitute.For<ILocksService>();
|
|
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
|
|
var viewService = Substitute.For<IViewService>();
|
|
var bikesViewModel = Substitute.For<IBikesViewModel>();
|
|
var activeUser = Substitute.For<IUser>();
|
|
|
|
var handler = new BookedClosed(
|
|
bike,
|
|
() => true, // isConnectedDelegate
|
|
(isConnexted) => connector,
|
|
geolocation,
|
|
locks,
|
|
() => pollingManager,
|
|
Substitute.For<ISmartDevice>(),
|
|
viewService,
|
|
bikesViewModel,
|
|
activeUser);
|
|
|
|
locks[0].OpenAsync()
|
|
.Returns(LockitLockingState.Open);
|
|
|
|
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x =>
|
|
throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""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 behavior
|
|
Received.InOrder(() =>
|
|
{
|
|
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
|
|
bikesViewModel.ActionText = "One moment please...";
|
|
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
|
|
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
|
|
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<LocationDto>());
|
|
bikesViewModel.ActionText = "Status error on updating lock state.";
|
|
bikesViewModel.ActionText = "Updating...";
|
|
pollingManager.StartAsync(); // polling must be restarted again
|
|
bikesViewModel.ActionText = string.Empty;
|
|
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
|
|
});
|
|
|
|
// Verify state after action
|
|
Assert.AreEqual("Close lock", subsequent.ButtonText);
|
|
Assert.IsTrue(subsequent.IsButtonVisible);
|
|
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
|
|
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
|
|
}
|
|
}
|
|
}
|