using System; using System.Threading; using System.Threading.Tasks; using NSubstitute; using NSubstitute.ExceptionExtensions; using NUnit.Framework; 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.Services.BluetoothLock; using TINK.Services.Geolocation; using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.GetLockedLocationCommand; namespace TestShareeLib.Model.Bikes.BikeInfoNS.BluetoothLock.Command { public class TestGetBikeLocationCommand { /// /// Use case: End rental. /// Final state: Booked, lock state unknown. /// /// Replaces test TestReturnOutOfReach which was removed for sharee.bike version ~3.0.362. [Test] public void TestReturnLastGeolocatonNullLockOutOfReach() { var bike = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var listener = Substitute.For(); bike.Id.Returns("0"); bike.LockInfo.Location.Returns((IGeolocation)null); // When locking bike geolocation was not available. locks[0].GetDeviceState().Returns(DeviceState.Disconnected); // Simulate bike out of reach. locks.DisconnectAsync(0, Arg.Any()).Returns(LockingState.UnknownDisconnected); // Simulate disconnecting. bike.State.Value.Returns(InUseStateEnum.Booked); Assert.That( async () => await InvokeAsync( bike, geolocation, locks, listener: listener), Throws.TypeOf()); // Verify behavior Received.InOrder(() => { listener.ReportStep(Step.StartingQueryLocation); var location = bike.LockInfo.Location; locks[0].GetDeviceState(); listener.ReportStateAsync(State.DisconnetedNoLocationError, ""); listener.ReportStep(Step.DisconnectingLockOnDisconnectedNoLocationError); locks.DisconnectAsync(0, Arg.Any()); listener.ReportStateAsync(State.QueryLocationSucceeded, ""); }); Assert.That( bike.LockInfo.State, Is.EqualTo(LockingState.UnknownDisconnected)); } /// /// Use case: End rental. /// Final state: Booked, lock state unknown. /// /// Replaces test TestReturnLockInReachNoGeolocation which was removed for sharee.bike older ~ 3.0.362. [Test] public void TestReturnStartGetGeolocationException() { var bike = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var listener = Substitute.For(); bike.Id.Returns("0"); 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(), Arg.Any()).Throws(new Exception("Ups...")); // Simulate error when starting query for geolocation. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state does not change. bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. var subsequent = InvokeAsync( bike, geolocation, locks, listener: listener); // Verify behavior Received.InOrder(async () => { listener.ReportStep(Step.StartingQueryLocation); locks[0].GetDeviceState(); await geolocation.GetAsync(Arg.Any(), Arg.Any()); await listener.ReportStateAsync(State.QueryLocationFailed, "Ups..."); }); } /// /// Use case: End rental. /// Final state: Booked, lock state unknown. /// [Test] public void TestReturnGetGeolocationException() { var bike = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var listener = Substitute.For(); bike.Id.Returns("0"); 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(), Arg.Any()) .Returns(Task.FromException(new Exception("Ups..."))); // Simulate error starting query for geolocation. bike.State.Value.Returns(InUseStateEnum.Booked); // Booking state does not change. bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. Assert.That( async () => await InvokeAsync( bike, geolocation, locks, listener: listener), Throws.TypeOf()); // Verify behavior Received.InOrder(async () => { listener.ReportStep(Step.StartingQueryLocation); locks[0].GetDeviceState(); await geolocation.GetAsync(Arg.Any(), Arg.Any()); await listener.ReportStateAsync(State.QueryLocationFailed, "Ups..."); }); } /// /// Use case: End rental. /// Final state: Disposable closed /// [Test] public async Task TestReturnLockInReach() { var bike = Substitute.For(); var geolocation = Substitute.For(); var locks = Substitute.For(); var listener = Substitute.For(); bike.LockInfo.Location.Returns((IGeolocation)null); bike.Id.Returns("0"); locks[0].GetDeviceState().Returns(DeviceState.Connected); // Simulate bike in reach. If bike is out of reach bluetooth state changes to unknown. var location = Substitute.For(); location.Latitude.Returns(7); location.Longitude.Returns(9); location.Timestamp.Returns(DateTimeOffset.MinValue); geolocation.GetAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult( location )); ; var lockingLocation = await InvokeAsync( bike, geolocation, locks, () => DateTime.MinValue, listener); Assert.That(lockingLocation.Latitude, Is.EqualTo(7)); Assert.That(lockingLocation.Longitude, Is.EqualTo(9)); // Verify behavior Received.InOrder(async () => { listener.ReportStep(Step.StartingQueryLocation); var dummyLoc = bike.LockInfo.Location; // Check if location was available when closing lock. locks[0].GetDeviceState(); // Check if bike is connected. await geolocation.GetAsync(Arg.Any(), Arg.Any()); // Query current location. await listener.ReportStateAsync(State.QueryLocationSucceeded, string.Empty); }); } } }