Version 3.0.381

This commit is contained in:
Anja 2024-04-09 12:53:23 +02:00
parent f963c0a219
commit 3a363acf3a
1525 changed files with 60589 additions and 125098 deletions

View file

@ -0,0 +1,491 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.State;
using ShareeBike.Repository.Request;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.BluetoothLock.Tdo;
using ShareeBike.Services.Geolocation;
using Geolocation = ShareeBike.Services.Geolocation.Geolocation;
using static ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand;
using NSubstitute.ExceptionExtensions;
using ShareeBike.Services.BluetoothLock.Exception;
using ShareeBike.Repository.Exception;
using Newtonsoft.Json;
using ShareeBike.Repository.Response;
using ShareeBike.ViewModel;
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
[TestFixture]
public class TestCloseCommand
{
/// <summary>
/// Use case: Close lock
/// </summary>
[Test]
public async Task TestClose()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
await InvokeAsync<TestCloseCommand>(
bike,
geolocation,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null);
// Verify that location is kept because bike might be returned later.
Assert.That(
bike.LockInfo.Location.Latitude,
Is.EqualTo(47.99).Within(0.001));
Assert.That(
bike.LockInfo.Location.Longitude,
Is.EqualTo(7.78).Within(0.001));
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Closed));
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
listener.Received().ReportStep(Step.WaitStopPollingQueryLocation);
listener.Received().ReportStep(Step.QueryLocationTerminated);
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
}
[Test]
public async Task TestCloseGeolocationServiceGetAsyncFails()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
var startOfTest = DateTime.Now;
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Throws(new Exception("Ups no location..."));
locks[0].CloseAsync()
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success
bike.State.Value.Returns(InUseStateEnum.Booked);
await InvokeAsync<TestCloseCommand>(
bike,
geolocation,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null);
// Verify that location is kept because bike might be returned later.
Assert.That(
bike.LockInfo.Location,
Is.Null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
await listener.Received().ReportStateAsync(State.StartGeolocationException, "Ups no location...");
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
});
}
[Test]
public void TestCloseCloseFailsOutOfReachException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(
bike,
geolocation,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null),
Throws.InstanceOf<OutOfReachException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.Received().ReportStateAsync(State.OutOfReachError, "Exception of type 'ShareeBike.Services.BluetoothLock.Exception.OutOfReachException' was thrown.");
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
[Test]
public void TestCloseCloseFailsCouldntCloseMovingException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseMovingException());
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(
bike, geolocation, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntCloseMovingException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.Received().ReportStateAsync(State.CouldntCloseMovingError, "The process is motion sensitive. Step close to the lock, do not move, and try again.");
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Open));
}
[Test]
public void TestCloseCloseFailsCouldntCloseBoltBlockedException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseBoltBlockedException());
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntCloseBoltBlockedException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.Received().ReportStateAsync(State.CouldntCloseBoltBlockedError, "Lock bolt is blocked. Make sure that no spoke or any other obstacle prevents the lock from closing and try again.");
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownFromHardwareError));
}
[Test]
public void TestCloseCloseFailsCouldntCloseBoltBlockedExceptionOpen()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseBoltBlockedException(LockingState.Open));
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntCloseBoltBlockedException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.Received().ReportStateAsync(State.CouldntCloseBoltBlockedError, "Lock bolt is blocked. Make sure that no spoke or any other obstacle prevents the lock from closing and try again.");
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Open));
}
[Test]
public void TestCloseCloseFailsCouldntCloseInconsistentState()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var geolocation = Substitute.For<IGeolocationService>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseInconsistentStateExecption(LockitLockingState.Unknown.GetLockingState()));
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntCloseInconsistentStateExecption>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.Received().ReportStateAsync(State.GeneralCloseError, "Lock reports unknown bold position. Lock could be closed or open. Please try again or contact customer support.");
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownFromHardwareError));
}
[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 listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(new Geolocation.Builder { Latitude = 47.99, Longitude = 7.78 }.Build());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Booked);
Assert.That(
async () => await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<Exception>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await listener.ReportStateAsync(State.GeneralCloseError, "Exception message.");
await connector.Command.UpdateLockingStateAsync(bike, Arg.Is<LocationDto>(x => x.Latitude == 47.99 && x.Longitude == 7.78));
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
[Test]
public async Task TestCloseWaitGeolocationServiceGetAsyncFails()
{
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 listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
locks[0].CloseAsync()
.Returns(LockitLockingState.Closed); // Return lock state indicating success
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>()).Returns(Task.FromException<IGeolocation>(new Exception("Ups, some error...")));
bike.State.Value.Returns(InUseStateEnum.Booked);
await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
listener.Received().ReportStep(Step.WaitStopPollingQueryLocation);
await listener.Received().ReportStateAsync(State.WaitGeolocationException, "Ups, some error...");
listener.Received().ReportStep(Step.QueryLocationTerminated);
await connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
});
}
[Test]
public async Task 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 listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
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")));
bike.State.Value.Returns(InUseStateEnum.Booked);
await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
});
}
[Test]
public async Task 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 listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
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."));
bike.State.Value.Returns(InUseStateEnum.Booked);
await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
await listener.ReportStateAsync(State.ResponseIsInvalid, "Outer message.");
});
}
[Test]
public async Task 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 listener = Substitute.For<ICloseCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
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."));
bike.State.Value.Returns(InUseStateEnum.Booked);
await InvokeAsync<TestCloseCommand>(bike, geolocation, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.StartingQueryingLocation);
await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
listener.Received().ReportStep(Step.ClosingLock);
await locks.Received()[0].CloseAsync(); // Lock must be closed
await connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>());
await listener.ReportStateAsync(State.BackendUpdateFailed, "Exception message.");
});
}
}
}

View file

@ -0,0 +1,131 @@
using System;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.BluetoothLock.Tdo;
using static ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.ConnectAndGetStateCommand;
using ShareeBike.Services.BluetoothLock.Exception;
using ShareeBike.Repository.Exception;
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
[TestFixture]
public class TestConnectCommand
{
/// <summary>
/// Use case: Connect lock
/// </summary>
[Test]
public async Task TestConnect()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IConnectAndGetStateCommandListener>();
bike.LockInfo.Id.Returns(0);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>())
.Returns(Task.FromResult(new LockInfoTdo.Builder { State = LockitLockingState.Closed }.Build())); // Return lock state indicating success
await InvokeAsync<TestConnectCommand>(
bike,
locks,
listener);
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Closed));
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.ConnectLock);
await locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
listener.Received().ReportStep(Step.GetLockingState);
});
}
/// <summary>
/// Use case: Connect lock, lock out of reach
/// </summary>
[Test]
public void TestConnectOutOfReachException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IConnectAndGetStateCommandListener>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.LockInfo.Id.Returns(0);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>())
.Returns<LockInfoTdo>(x => throw new OutOfReachException()); // Return exception
locks.TimeOut.Returns(timeOuts);
Assert.That(
async () => await InvokeAsync<TestConnectCommand>(
bike,
locks,
listener),
Throws.InstanceOf<OutOfReachException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.ConnectLock);
await locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
await listener.Received().ReportStateAsync(State.OutOfReachError, "Exception of type 'ShareeBike.Services.BluetoothLock.Exception.OutOfReachException' was thrown.");
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
/// <summary>
/// Use case: Connect lock
/// </summary>
[Test]
public void TestConnectException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IConnectAndGetStateCommandListener>();
var timeOuts = Substitute.For<ITimeOutProvider>();
bike.Id.Returns("0");
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>())
.Returns<LockInfoTdo>(x => throw new Exception("Ups...")); // Return exception
locks.TimeOut.Returns(timeOuts);
Assert.That(
async () => await InvokeAsync<TestConnectCommand>(
bike,
locks,
listener),
Throws.InstanceOf<Exception>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.ConnectLock);
await locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
await listener.Received().ReportStateAsync(State.GeneralConnectLockError, "Please step as close as possible to the bike lock and try again.");
await locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
await listener.Received().ReportStateAsync(State.GeneralConnectLockError, "Your mobile device still does not connect to the bike lock. Please restart the app or even your mobile device and repeat the process.");
await locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
await listener.Received().ReportStateAsync(State.GeneralConnectLockError, "It is still not possible to establish a connection between your mobile device and the bike lock. Please contact customer support!\r\n\r\nAlternative:\r\n1. close app,\r\n2. press the button at the top of the lock briefly and release it as soon as it starts flashing,\r\n3. make sure that the lock is completely closed and remains closed.\r\n4. send e-mail to operator (otherwise your chargeable rental will continue!): Please include problem description, bike-id and drop-off station.");
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
}
}

View file

@ -0,0 +1,82 @@
using System;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.BluetoothLock.Tdo;
using static ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.DisconnectCommand;
using ShareeBike.Services.BluetoothLock.Exception;
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
[TestFixture]
public class TestDisconnectCommand
{
/// <summary>
/// Use case: Disconnect lock
/// </summary>
[Test]
public async Task TestDisconnect()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IDisconnectCommandListener>();
bike.LockInfo.Id.Returns(0);
bike.LockInfo.State.Returns(LockingState.Closed);
await locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
await InvokeAsync<TestDisconnectCommand>(
bike,
locks,
listener);
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.DisconnectLock);
await locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
});
}
/// <summary>
/// Use case: Disconnect lock
/// </summary>
[Test]
public async Task TestDisconnectException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IDisconnectCommandListener>();
bike.LockInfo.Id.Returns(0);
bike.LockInfo.State.Returns(LockingState.Closed);
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>())
.Returns<Task>(x => throw new Exception("exception"));
await InvokeAsync<TestDisconnectCommand>(
bike,
locks,
listener);
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Closed));
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.DisconnectLock);
await locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
await listener.Received().ReportStateAsync(State.GeneralDisconnectError,"exception");
});
}
}
}

View file

@ -0,0 +1,329 @@
using System;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
using ShareeBike.Model.Connector;
using ShareeBike.Model.State;
using ShareeBike.Repository.Request;
using ShareeBike.Services.BluetoothLock;
using ShareeBike.Services.BluetoothLock.Tdo;
using static ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.OpenCommand;
using ShareeBike.Services.BluetoothLock.Exception;
using ShareeBike.Repository.Exception;
using Newtonsoft.Json;
using ShareeBike.Repository.Response;
using ShareeBike.ViewModel;
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BluetoothLock.Command
{
[TestFixture]
public class TestOpenCommand
{
/// <summary>
/// Use case: Open lock
/// </summary>
[Test]
public async Task TestOpen()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
locks[0].OpenAsync()
.Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success
await InvokeAsync<TestOpenCommand>(
bike,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.WaitStopPolling);
listener.Received().ReportStep(Step.UpdateLockingState);
await connector.Command.UpdateLockingStateAsync(bike);
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Open));
}
[Test]
public void TestOpenOpenFailsOutOfReachException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
Assert.That(
async () => await InvokeAsync<TestOpenCommand>(
bike,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null),
Throws.InstanceOf<OutOfReachException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
await listener.ReportStateAsync(State.OutOfReachError, "Exception of type 'ShareeBike.Services.BluetoothLock.Exception.OutOfReachException' was thrown.");
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
[Test]
public void TestOpenOpenFailsBoltStatusUnknownException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenBoldStatusIsUnknownException());
Assert.That(
async () => await InvokeAsync<TestOpenCommand>(
bike,
locks,
() => true, // isConnectedDelegate
(isConnected) => connector,
listener, null),
Throws.InstanceOf<CouldntOpenBoldStatusIsUnknownException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
await listener.ReportStateAsync(State.CouldntOpenBoldStatusIsUnknownError, "Position of lock bolt is unknown. Lock could be closed or open. Please try again or contact customer support.");
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.UpdateLockingState);
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownFromHardwareError));
}
[Test]
public void TestOpenOpenFailsCouldntOpenBoltBlockedException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenBoldIsBlockedException());
Assert.That(
async () => await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntOpenBoldIsBlockedException>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
await listener.ReportStateAsync(State.CouldntOpenBoldIsBlockedError, "Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.");
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.UpdateLockingState);
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownFromHardwareError));
}
[Test]
public void TestOpenOpenFailsCouldntOpenInconsistentStateException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenInconsistentStateExecption(LockitLockingState.Closed.GetLockingState()));
Assert.That(
async () => await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<CouldntOpenInconsistentStateExecption>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
await listener.ReportStateAsync(State.CouldntOpenInconsistentStateError, "Unexpected locking state \"Closed\" detected after sending open command. Please try again or contact customer support.");
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.UpdateLockingState);
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.Closed));
}
[Test]
public void TestOpenOpenFailsException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Closed);
Assert.That(
async () => await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null),
Throws.InstanceOf<Exception>());
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
await listener.ReportStateAsync(State.GeneralOpenError, "Exception message.");
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.UpdateLockingState);
});
Assert.That(
bike.LockInfo.State,
Is.EqualTo(LockingState.UnknownDisconnected));
}
[Test]
public async Task TestOpenUpdateLockingStateFailsWebConnectFailure()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x => throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.WaitStopPolling);
listener.Received().ReportStep(Step.UpdateLockingState);
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
});
}
[Test]
public async Task TestOpenUpdateLockingStateFailsInvalidResponse()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
connector.Command.UpdateLockingStateAsync(bike, Arg.Any<LocationDto>()).Returns(x =>
throw new ReturnBikeException(JsonConvert.DeserializeObject<DoReturnResponse>(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message."));
await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.WaitStopPolling);
listener.Received().ReportStep(Step.UpdateLockingState);
await listener.ReportStateAsync(State.ResponseIsInvalid, "Outer message.");
});
}
[Test]
public async Task TestOpenUpdateLockingStateFails()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var locks = Substitute.For<ILocksService>();
var listener = Substitute.For<IOpenCommandListener>();
var viewUpdateManager = Substitute.For<IPollingUpdateTaskManager>();
locks[0].OpenAsync()
.Returns(LockitLockingState.Open); // Return lock state indicating success
connector.Command.UpdateLockingStateAsync(bike).Returns(x => throw new Exception("Exception message."));
await InvokeAsync<TestOpenCommand>(bike, locks, () => true, (isConnected) => connector, listener, null);
// Verify behavior
Received.InOrder(async () =>
{
listener.Received().ReportStep(Step.OpeningLock);
await locks.Received()[0].OpenAsync(); // Lock must be open
listener.Received().ReportStep(Step.GetLockInfos);
listener.Received().ReportStep(Step.WaitStopPolling);
listener.Received().ReportStep(Step.UpdateLockingState);
await connector.Command.UpdateLockingStateAsync(bike);
await listener.ReportStateAsync(State.BackendUpdateFailed, "Exception message.");
});
}
}
}