using System.Threading.Tasks; using NSubstitute; using NUnit.Framework; using TINK.Model.Bikes.BikeInfoNS.BluetoothLock; using TINK.Model.Connector; using TINK.Model.State; using TINK.Services.BluetoothLock; using TINK.View; using TINK.ViewModel; using TINK.ViewModel.Bikes; using TINK.ViewModel.Bikes.Bike.BluetoothLock; using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using static TINK.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand; namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock { [TestFixture] public class TestCloseLockActionViewModel { /// /// Use case: Close lock. /// Final state: Occupied closed, End rental requested /// [Test] public async Task TestCloseLockAndRequestEndRental() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var listener = Substitute.For(); var closeLockActionViewModel = new CloseLockActionViewModel( bike, () => pollingManager, viewService, bikesViewModel); bike.Id.Returns("0"); bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. closeLockActionViewModel.ReportStep(Step.StartStopingPolling); closeLockActionViewModel.ReportStep(Step.StartingQueryingLocation); closeLockActionViewModel.ReportStep(Step.ClosingLock); closeLockActionViewModel.ReportStep(Step.WaitStopPollingQueryLocation); closeLockActionViewModel.ReportStep(Step.QueryLocationTerminated); closeLockActionViewModel.ReportStep(Step.UpdateLockingState); return Task.CompletedTask; }); bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. await closeLockActionViewModel.CloseLockAsync(); // 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.StartRentalProcess(Arg.Is( x => x.BikeId == "0" && x.State == CurrentRentalProcess.CloseLock && x.StepIndex == 1 && x.Result == CurrentStepStatus.None)); //Step.StartingQueryingLocation bikesViewModel.ActionText = "Start query location..."; //Step.ClosingLock bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel."; //Step.WaitStopPollingQueryLocation bikesViewModel.ActionText = "Query location..."; //Step.UpdateLockingState bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; bikesViewModel.RentalProcess.StepIndex = 2; bikesViewModel.RentalProcess.Result = CurrentStepStatus.None; //Ask whether park bike or end rental viewService.DisplayAlert( "Please choose", "Do you want to park the bike to continue riding later or return the bike now and end the rental?", "End rental", "Park bike" ); closeLockActionViewModel.IsEndRentalRequested = true; bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; bikesViewModel.RentalProcess.State = CurrentRentalProcess.None; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked return; }); } /// /// Use case: Close lock. /// Final state: Occupied closed, Park bike requested /// [Test] public async Task TestCloseLockAndRequestParkBike() { var bike = Substitute.For(); var connector = Substitute.For(); var command = Substitute.For(); var locks = Substitute.For(); var pollingManager = Substitute.For(); var viewService = Substitute.For(); var bikesViewModel = Substitute.For(); var listener = Substitute.For(); var closeLockActionViewModel = new CloseLockActionViewModel( bike, () => pollingManager, viewService, bikesViewModel); bike.Id.Returns("0"); bike.CloseLockAsync(Arg.Any(), Arg.Any()).Returns(x => { // Add calls to ReportStep which IBikeInfoMutable implementation would do. closeLockActionViewModel.ReportStep(Step.StartStopingPolling); closeLockActionViewModel.ReportStep(Step.StartingQueryingLocation); closeLockActionViewModel.ReportStep(Step.ClosingLock); closeLockActionViewModel.ReportStep(Step.WaitStopPollingQueryLocation); closeLockActionViewModel.ReportStep(Step.QueryLocationTerminated); closeLockActionViewModel.ReportStep(Step.UpdateLockingState); return Task.CompletedTask; }); bike.State.Value.Returns(InUseStateEnum.Booked); // Request handler factory queries state to create appropriate request handler object. bike.LockInfo.State.Returns(LockingState.Closed); // Request handler factory queries lock state to create appropriate request handler object. await closeLockActionViewModel.CloseLockAsync(); // 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.StartRentalProcess(Arg.Is( x => x.BikeId == "0" && x.State == CurrentRentalProcess.CloseLock && x.StepIndex == 1 && x.Result == CurrentStepStatus.None)); //Step.StartingQueryingLocation bikesViewModel.ActionText = "Start query location..."; //Step.ClosingLock bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel."; //Step.WaitStopPollingQueryLocation bikesViewModel.ActionText = "Query location..."; //Step.UpdateLockingState bikesViewModel.ActionText = "Updating lock state..."; bikesViewModel.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; bikesViewModel.RentalProcess.StepIndex = 2; bikesViewModel.RentalProcess.Result = CurrentStepStatus.None; //Ask whether park bike or end rental viewService.DisplayAlert( "Please choose", "Do you want to park the bike to continue riding later or return the bike now and end the rental?", "End rental", "Park bike" ); closeLockActionViewModel.IsEndRentalRequested = false; bikesViewModel.RentalProcess.Result = CurrentStepStatus.Succeeded; //Show confirmation viewService.DisplayAlert( "Lock is closed", "Bike is parked. Your chargeable rental continues!", "OK" ); bikesViewModel.RentalProcess.State = CurrentRentalProcess.None; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked return; }); } } }