mirror of
https://dev.azure.com/TeilRad/sharee.bike%20App/_git/Code
synced 2025-07-09 21:07:31 +02:00
Version 3.0.381
This commit is contained in:
parent
f963c0a219
commit
3a363acf3a
1525 changed files with 60589 additions and 125098 deletions
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using ShareeBike.Model.Connector;
|
||||
using static ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.AuthCommand;
|
||||
using ShareeBike.Repository.Exception;
|
||||
|
||||
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BikeNS.Command
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAuthCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Use case: Authenticate.
|
||||
/// Final state: Authentication successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestAuth()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IAuthCommandListener>();
|
||||
|
||||
await InvokeAsync<TestAuthCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
await connector.Command.CalculateAuthKeys(Arg.Any<IBikeInfoMutable>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.Authenticate);
|
||||
await connector.Command.CalculateAuthKeys(bike);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Authenticate.
|
||||
/// Final state: Authentication failed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAuthNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IAuthCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.CalculateAuthKeys(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestAuthCommand>(
|
||||
bike,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.Authenticate);
|
||||
await connector.Command.CalculateAuthKeys(bike);
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Authenticate.
|
||||
/// Final state: Authentication failed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAuthGeneralException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IAuthCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.CalculateAuthKeys(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new Exception("Context info."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestAuthCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.Authenticate);
|
||||
await connector.Command.CalculateAuthKeys(bike);
|
||||
await listener.ReportStateAsync(State.GeneralAuthError, "Context info.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using ShareeBike.Model.Connector;
|
||||
using static ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.CancelReservationCommand;
|
||||
using ShareeBike.Repository.Exception;
|
||||
|
||||
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BikeNS.Command
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCancelReservationCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Use case: Cancel reservation.
|
||||
/// Final state: Reservation canceled successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestCancelReservation()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<ICancelReservationCommandListener>();
|
||||
|
||||
await InvokeAsync<TestCancelReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
await connector.Command.DoCancelReservation(Arg.Any<IBikeInfoMutable>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.CancelReservation);
|
||||
await connector.Command.DoCancelReservation(bike);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Cancel reservation.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCancelReservationNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<ICancelReservationCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.DoCancelReservation(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestCancelReservationCommand>(
|
||||
bike,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.CancelReservation);
|
||||
await connector.Command.DoCancelReservation(bike);
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Cancel reservation.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCancelReservationGeneralException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<ICancelReservationCommandListener>();
|
||||
|
||||
connector.Command.DoCancelReservation(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new Exception("Context info."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestCancelReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.CancelReservation);
|
||||
await connector.Command.DoCancelReservation(bike);
|
||||
await listener.ReportStateAsync(State.GeneralCancelReservationError, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Cancel reservation.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCancelReservationTooManyBikesException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<ICancelReservationCommandListener>();
|
||||
|
||||
bike.Id.Returns("1543");
|
||||
|
||||
connector.Command.DoCancelReservation(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new InvalidAuthorizationResponseException("mail", new ShareeBike.Repository.Response.ResponseBase()));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestCancelReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<InvalidAuthorizationResponseException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.CancelReservation);
|
||||
await connector.Command.DoCancelReservation(bike);
|
||||
await listener.ReportStateAsync(State.InvalidResponse, "Your session has expired. Please login new and try again.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,469 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using ShareeBike.Services.BluetoothLock;
|
||||
using ShareeBike.Services.Geolocation;
|
||||
using ShareeBike.Repository.Request;
|
||||
using ShareeBike.Model.Connector;
|
||||
using static ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.EndRentalCommand;
|
||||
using ShareeBike.Model;
|
||||
using ShareeBike.Repository.Exception;
|
||||
using ShareeBike.Model.State;
|
||||
|
||||
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BikeNS.Command
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEndRentalCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Rental ended successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("bike.State.Value should be FeedbackPending")]
|
||||
public async Task TestReturnClosingLockLocationAvailable()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
var closingLockLocation = Substitute.For<IGeolocation>();
|
||||
closingLockLocation.Latitude.Returns(7);
|
||||
closingLockLocation.Longitude.Returns(9);
|
||||
closingLockLocation.Timestamp.Returns(DateTimeOffset.MinValue);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)closingLockLocation); // When locking bike geolocation was available.
|
||||
|
||||
var endRentalLocation = closingLockLocation;
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
|
||||
await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Not.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.FeedbackPending));
|
||||
}
|
||||
|
||||
// <summary>
|
||||
// Use case: End rental.
|
||||
// Final state: Rental ended successfully
|
||||
// </summary>
|
||||
[Test]
|
||||
[Ignore("bike.State.Value should be FeedbackPending")]
|
||||
public async Task TestReturnLastGeolocationNullLockInReach()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // Simulate that when locking bike geolocation was not available
|
||||
|
||||
var newLockLocation = Substitute.For<IGeolocation>(); // Get geolocation from end rental
|
||||
newLockLocation.Latitude.Returns(7);
|
||||
newLockLocation.Longitude.Returns(9);
|
||||
newLockLocation.Timestamp.Returns(DateTimeOffset.MinValue);
|
||||
var endRentalLocation = geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Not.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.FeedbackPending));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestReturnLastGeolocatonNullLockOutOfReachNoGPSDataDoNotReturn()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Disconnected); // Simulate bike not in reach.
|
||||
|
||||
await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
var task = connector.Command.DidNotReceive().DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
await listener.ReportStateAsync(State.NoGPSData, "There is no geolocation information available since lock is not connected");
|
||||
});
|
||||
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReturnLastGeolocatonNullLockInReachGetGeolocationException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns<Task<IGeolocation>>(x => throw new Exception("Ups...")); // Simulate error query for geolocation.
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
connector.Command.DidNotReceive().DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
await listener.ReportStateAsync(State.GeneralQueryLocationFailed, "Ups...");
|
||||
});
|
||||
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("bookingFinished should be null")]
|
||||
public void TestReturnLastGeolocatonNullReturnException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
var newLockLocation = Substitute.For<IGeolocation>(); // Get geolocation from end rental
|
||||
newLockLocation.Latitude.Returns(7);
|
||||
newLockLocation.Longitude.Returns(9);
|
||||
newLockLocation.Timestamp.Returns(DateTimeOffset.MinValue);
|
||||
var endRentalLocation = geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x =>
|
||||
throw new Exception("Ups..."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
await listener.ReportStateAsync(State.GeneralEndRentalError, "Ups...");
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("bookingFinished should be null")]
|
||||
public void TestReturnReturnNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
var newLockLocation = Substitute.For<IGeolocation>(); // Get geolocation from end rental
|
||||
newLockLocation.Latitude.Returns(7);
|
||||
newLockLocation.Longitude.Returns(9);
|
||||
newLockLocation.Timestamp.Returns(DateTimeOffset.MinValue);
|
||||
var endRentalLocation = geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("Exception needs to be initialized correctly. bookingFinished should be null")]
|
||||
public void TestReturnNotAtStationException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
bike.Id.Returns("1545");
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
var newLockLocation = Substitute.For<IGeolocation>(); // Get geolocation from end rental
|
||||
newLockLocation.Latitude.Returns(7);
|
||||
newLockLocation.Longitude.Returns(9);
|
||||
newLockLocation.Timestamp.Returns(DateTimeOffset.MinValue);
|
||||
var endRentalLocation = geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns(Task.FromResult(newLockLocation));
|
||||
|
||||
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);
|
||||
notAtStationException.Distance.Returns(15986);
|
||||
notAtStationException.StationNr.Returns("42");
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x
|
||||
=> throw notAtStationException);
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<NotAtStationException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, Arg.Is<LocationDto>(x => x.Latitude == 7 && x.Longitude == 9));
|
||||
await listener.ReportStateAsync(State.NotAtStation, "End rental outside or at unsuitable station is not possible. Distance to next suitable station 42 is 15986 m.\r\n\r\nNote: Your GPS data may not be up to date. So if you are at a suitable station, please simply try again so that your location is queried again.");
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: End rental.
|
||||
/// Final state: Still rented.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("Exception needs to be initialized correctly. bookingFinished should be null")]
|
||||
public void TestReturnNoGPSDataException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var geolocation = Substitute.For<IGeolocationService>();
|
||||
var locks = Substitute.For<ILocksService>();
|
||||
static DateTime dateTime() => DateTime.MinValue;
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IEndRentalCommandListener>();
|
||||
|
||||
bike.State.Value.Returns(InUseStateEnum.Booked);
|
||||
|
||||
bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available.
|
||||
|
||||
locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach.
|
||||
|
||||
var endRentalLocation = geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>())
|
||||
.Returns((IGeolocation)null);
|
||||
|
||||
NoGPSDataException.IsNoGPSData("Failure 2245: No GPS data, state change forbidden.", out NoGPSDataException noGPSDataException);
|
||||
|
||||
var bookingFinished = connector.Command.DoReturn(Arg.Any<IBikeInfoMutable>(), Arg.Any<LocationDto>()).Returns<BookingFinishedModel>(x =>
|
||||
throw noGPSDataException);
|
||||
|
||||
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestEndRentalCommand>(
|
||||
bike,
|
||||
geolocation,
|
||||
locks,
|
||||
dateTime,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<NoGPSDataException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.GetLocation);
|
||||
var location = bike.LockInfo.Location;
|
||||
locks[0].GetDeviceState();
|
||||
var endRentalLocation = await geolocation.GetAsync(Arg.Any<CancellationToken?>(), Arg.Any<DateTime?>());
|
||||
|
||||
listener.ReportStep(Step.ReturnBike);
|
||||
var bookingFinished = await connector.Command.DoReturn(bike, null);
|
||||
await listener.ReportStateAsync(State.NoGPSData, "Failure 2245: No GPS data, state change forbidden.");
|
||||
});
|
||||
|
||||
Assert.That(bookingFinished, Is.Null);
|
||||
Assert.That(bike.State.Value, Is.EqualTo(InUseStateEnum.Booked));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using ShareeBike.Model.Connector;
|
||||
using static ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartRentalCommand;
|
||||
using ShareeBike.Repository.Exception;
|
||||
|
||||
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BikeNS.Command
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestStartRentalCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Rental started successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestLockClosedStartRental()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
await connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>(), LockingAction.Open);
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike, LockingAction.Open);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLockClosedStartRentalNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>(), LockingAction.Open).Returns(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike, LockingAction.Open);
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLockClosedStartRentalGeneralException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>(), LockingAction.Open).Returns(x
|
||||
=> throw new Exception("Context info."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike, LockingAction.Open);
|
||||
await listener.ReportStateAsync(State.GeneralStartRentalError, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Rental started successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestLockOpenStartRental()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Open);
|
||||
|
||||
await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
await connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLockOpenStartRentalNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Open);
|
||||
|
||||
connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike);
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start rental.
|
||||
/// Final state: Still reserved.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLockOpenStartRentalGeneralException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartRentalCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Open);
|
||||
|
||||
connector.Command.DoBookAsync(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new Exception("Context info."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartRentalCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.RentBike);
|
||||
await connector.Command.DoBookAsync(bike);
|
||||
await listener.ReportStateAsync(State.GeneralStartRentalError, "Context info.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework;
|
||||
using ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock;
|
||||
using ShareeBike.Services.BluetoothLock;
|
||||
using ShareeBike.Services.Geolocation;
|
||||
using ShareeBike.Repository.Request;
|
||||
using ShareeBike.Model.Connector;
|
||||
using static ShareeBike.Model.Bikes.BikeInfoNS.BikeNS.Command.StartReservationCommand;
|
||||
using ShareeBike.Model;
|
||||
using ShareeBike.Repository.Exception;
|
||||
|
||||
namespace SharedBusinessLogic.Tests.Model.Bikes.BikeInfoNS.BikeNS.Command
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestStartReservationCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Use case: Start reservation.
|
||||
/// Final state: Reservation started successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestStartReservation()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartReservationCommandListener>();
|
||||
|
||||
await InvokeAsync<TestStartReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener);
|
||||
|
||||
await connector.Command.DoReserve(Arg.Any<IBikeInfoMutable>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.ReserveBike);
|
||||
await connector.Command.DoReserve(bike);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start reservation.
|
||||
/// Final state: Reservation failed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestStartReservationNoWebException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartReservationCommandListener>();
|
||||
|
||||
bike.LockInfo.State.Returns(LockingState.Closed);
|
||||
|
||||
connector.Command.DoReserve(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new WebConnectFailureException("Context info.", new System.Exception("hoppla")));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartReservationCommand>(
|
||||
bike,
|
||||
() => false, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<WebConnectFailureException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.ReserveBike);
|
||||
await connector.Command.DoReserve(bike);
|
||||
await listener.ReportStateAsync(State.WebConnectFailed, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start reservation.
|
||||
/// Final state: Reservation failed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestStartReservationGeneralException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartReservationCommandListener>();
|
||||
|
||||
connector.Command.DoReserve(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw new Exception("Context info."));
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<Exception>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.ReserveBike);
|
||||
await connector.Command.DoReserve(bike);
|
||||
await listener.ReportStateAsync(State.GeneralStartReservationError, "Context info.");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use case: Start reservation.
|
||||
/// Final state: Reservation failed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
[Ignore("Exception needs to be initialized correctly.")]
|
||||
public void TestStartReservationTooManyBikesException()
|
||||
{
|
||||
var bike = Substitute.For<IBikeInfoMutable>();
|
||||
var connector = Substitute.For<IConnector>();
|
||||
var listener = Substitute.For<IStartReservationCommandListener>();
|
||||
|
||||
bike.Id.Returns("1543");
|
||||
|
||||
BookingDeclinedException.IsBookingDeclined("Too many bikes", out BookingDeclinedException bookingDeclinedException);
|
||||
bookingDeclinedException.MaxBikesCount.Returns(3);
|
||||
|
||||
connector.Command.DoReserve(Arg.Any<IBikeInfoMutable>()).Returns(x
|
||||
=> throw bookingDeclinedException);
|
||||
|
||||
Assert.That(
|
||||
async () => await InvokeAsync<TestStartReservationCommand>(
|
||||
bike,
|
||||
() => true, // isConnectedDelegate
|
||||
(isConnected) => connector,
|
||||
listener),
|
||||
Throws.InstanceOf<BookingDeclinedException>());
|
||||
|
||||
// Verify behavior
|
||||
Received.InOrder(async () =>
|
||||
{
|
||||
listener.ReportStep(Step.ReserveBike);
|
||||
await connector.Command.DoReserve(bike);
|
||||
await listener.ReportStateAsync(State.TooManyBikesError, "Too many bikes");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue