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.Services.BluetoothLock; using ShareeBike.View; using ShareeBike.ViewModel; using ShareeBike.ViewModel.Bikes; using ShareeBike.ViewModel.Bikes.Bike.BluetoothLock; using ShareeBike.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; using CloseCommand = ShareeBike.Model.Bikes.BikeInfoNS.BluetoothLock.Command.CloseCommand; namespace SharedBusinessLogic.Tests.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(CloseCommand.Step.StartingQueryingLocation); closeLockActionViewModel.ReportStep(CloseCommand.Step.ClosingLock); closeLockActionViewModel.ReportStep(CloseCommand.Step.WaitStopPollingQueryLocation); closeLockActionViewModel.ReportStep(CloseCommand.Step.QueryLocationTerminated); closeLockActionViewModel.ReportStep(CloseCommand.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.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.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; 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(CloseCommand.Step.StartingQueryingLocation); closeLockActionViewModel.ReportStep(CloseCommand.Step.ClosingLock); closeLockActionViewModel.ReportStep(CloseCommand.Step.WaitStopPollingQueryLocation); closeLockActionViewModel.ReportStep(CloseCommand.Step.QueryLocationTerminated); closeLockActionViewModel.ReportStep(CloseCommand.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.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.ActionText = "One moment please..."; pollingManager.StartAsync(); // polling must be restarted again bikesViewModel.ActionText = string.Empty; bikesViewModel.RentalProcess.State = CurrentRentalProcess.None; bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked return; }); } } }