sharee.bike-App/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedOpen.cs
2023-09-22 11:38:42 +02:00

620 lines
23 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using NSubstitute;
using NUnit.Framework;
using TINK.Model;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command;
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.CloseCommand;
namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
[TestFixture]
public class TestBookedOpen
{
/// <summary>
/// Test construction of object.
/// </summary>
[Test]
public void Testctor()
{
var handler = new BookedOpen(
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 state after action
Assert.AreEqual("Close lock", handler.ButtonText);
Assert.IsTrue(handler.IsButtonVisible);
Assert.AreEqual(string.Empty, handler.LockitButtonText);
Assert.That(handler.IsLockitButtonVisible, Is.False);
}
/// <summary>
/// Use case: Close lock
/// Final state: Booked closed.
/// </summary>
[Test]
public void TestClose()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
var viewService = Substitute.For<IViewService>();
var bikesViewModel = Substitute.For<IBikesViewModel>();
var handler = new BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => Substitute.For<IConnector>(),
Substitute.For<IGeolocationService>(),
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
Substitute.For<IUser>());
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>()).Returns(x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
handler.ReportStep(Step.UpdateLockingState);
return Task.CompletedTask;
}
);
#else
locks[0].CloseAsync()
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success
#endif
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed); // Return lock state indicating success
var subsequent = handler.HandleRequestOption1().Result;
// Verify behavior
Received.InOrder(() =>
{
bikesViewModel.Received(1).IsIdle = false; // GUI must be locked
bikesViewModel.ActionText = "One moment please...";
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
bikesViewModel.ActionText = "One moment please...";
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: Close lock
/// </summary>
[Test]
public void TestCloseCloseFailsOutOfReachException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
var viewService = Substitute.For<IViewService>();
var bikesViewModel = Substitute.For<IBikesViewModel>();
var handler = new BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => Substitute.For<IConnector>(),
Substitute.For<IGeolocationService>(),
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
Substitute.For<IUser>());
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>())
.Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
await handler.ReportStateAsync(CloseCommand.State.OutOfReachError, "");
throw new OutOfReachException();
}
);
#else
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
#endif
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); /// If <see cref="OutOfReachException"/> is fired lock state is set to state unknown because disconnected.
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 = "The lock bolt moves through the spokes of the rear wheel.";
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be closed!", "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("BookedDisconnected", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
[Test]
public void TestCloseCloseFailsCouldntCloseMovingException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
var viewService = Substitute.For<IViewService>();
var bikesViewModel = Substitute.For<IBikesViewModel>();
var handler = new BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => Substitute.For<IConnector>(),
Substitute.For<IGeolocationService>(),
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
Substitute.For<IUser>());
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>())
.Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
await handler.ReportStateAsync(CloseCommand.State.CouldntCloseMovingError, "");
throw new CouldntCloseMovingException();
}
);
bike.LockInfo.State.Returns(LockingState.Open); // Locking state is open when CouldntCloseMovingException occurs.
#else
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseMovingException());
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be closed!", "The process is motion sensitive. Step close to the lock, do not move, and try again.", "OK");
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
}
[Test]
public void TestCloseCloseFailsCouldntCloseBoltBlockedException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var pollingManager = Substitute.For<IPollingUpdateTaskManager>();
var viewService = Substitute.For<IViewService>();
var bikesViewModel = Substitute.For<IBikesViewModel>();
var handler = new BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => Substitute.For<IConnector>(),
Substitute.For<IGeolocationService>(),
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
Substitute.For<IUser>());
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>())
.Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
await handler.ReportStateAsync(CloseCommand.State.CouldntCloseBoltBlockedError, "");
throw new CouldntCloseBoltBlockedException();
}
);
bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError); // Locking state is unknown when CouldntCloseBoltBlockedException occurs.
#else
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseBoltBlockedException());
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be closed!", "Lock bolt is blocked. Make sure that no spoke or any other obstacle prevents the lock from closing 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("Open lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock
/// Final state: Same as initial state.
/// </summary>
[Test]
public void TestCloseCloseFailsException()
{
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 BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>()).Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
await handler.ReportStateAsync(CloseCommand.State.GeneralCloseError, "Exception message.");
throw new Exception("Exception message.");
}
);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected); // If CloseLock fires an exception of type Exception lock state is set to unknown.
#else
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
#if USELOCALINSTANCE
locks.Received()[0].CloseAsync(); // Lock must be closed
#endif
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Lock could not be closed!",
"Exception message.",
"OK"
);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state "Booked Disconnected" after action
Assert.AreEqual("BookedDisconnected", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock
/// Final state: Booked closed.
/// </summary>
[Test]
public void TestCloseUpdateLockingStateFailsWebConnectFailureException()
{
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 BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>()).Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
handler.ReportStep(Step.UpdateLockingState);
await handler.ReportStateAsync(CloseCommand.State.WebConnectFailed, "Context info");
}
);
bike.LockInfo.State.Returns(LockingState.Closed);
#else
locks[0].CloseAsync()
.Returns(LockitLockingState.Closed); // Return lock state indicating success
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla")));
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
#if USELOCALINSTANCE
locks.Received()[0].CloseAsync(); // Lock must be closed
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
#endif
bikesViewModel.ActionText = "Internet must be available for updating lock status. Please establish an Internet connection!";
bikesViewModel.ActionText = "One moment please...";
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: close lock
/// Final state: Booked closed.
/// </summary>
[Test]
public void TestCloseUpdateLockingStateFailsException()
{
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 BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>())
.Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
handler.ReportStep(Step.UpdateLockingState);
await handler.ReportStateAsync(CloseCommand.State.BackendUpdateFailed, "Exception message.");
}
);
bike.LockInfo.State.Returns(LockingState.Closed);
#else
locks[0].CloseAsync()
.Returns(LockitLockingState.Closed); // Return lock state indicating success
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x => throw new Exception("Exception message."));
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
#if USELOCALINSTANCE
locks.Received()[0].CloseAsync(); // Lock must be closed
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
#endif
bikesViewModel.ActionText = "Connection error on updating locking status.";
bikesViewModel.ActionText = "One moment please...";
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: close lock
/// Final state: Booked closed.
/// </summary>
[Test]
public void TestCloseUpdateLockingStateFailsResponseException()
{
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 BookedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
#if !USELOCALINSTANCE
bike.CloseLockAsync(Arg.Any<ICloseCommandListener>(), Arg.Any<Task>())
.Returns(async x =>
{
// Add calls to ReportStep which IBikeInfoMutable implementation would do.
handler.ReportStep(Step.ClosingLock);
handler.ReportStep(Step.UpdateLockingState);
await handler.ReportStateAsync(CloseCommand.State.ResponseIsInvalid, "Some invalid data received");
}
);
bike.LockInfo.State.Returns(LockingState.Closed);
#else
locks[0].CloseAsync()
.Returns(LockitLockingState.Closed); // Return lock state indicating success
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x =>
throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message."));
#endif
bike.State.Value.Returns(InUseStateEnum.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.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
#if USELOCALINSTANCE
locks.Received()[0].CloseAsync(); // Lock must be closed
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
#endif
bikesViewModel.ActionText = "Status error on updating lock state.";
bikesViewModel.ActionText = "One moment please...";
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);
}
}
}