2022-09-06 16:08:19 +02:00
using System ;
2021-07-12 21:31:46 +02:00
using System.Threading.Tasks ;
2022-08-30 15:42:25 +02:00
using NSubstitute ;
using NUnit.Framework ;
using TINK.Model.Bikes.BikeInfoNS.BluetoothLock ;
2021-07-12 21:31:46 +02:00
using TINK.Model.Connector ;
2022-08-30 15:42:25 +02:00
using TINK.Model.Device ;
using TINK.Model.State ;
using TINK.Model.User ;
2021-07-12 21:31:46 +02:00
using TINK.Repository.Exception ;
using TINK.Services.BluetoothLock ;
using TINK.Services.BluetoothLock.Exception ;
using TINK.Services.BluetoothLock.Tdo ;
2022-04-10 17:38:34 +02:00
using TINK.Services.Geolocation ;
2021-07-12 21:31:46 +02:00
using TINK.View ;
using TINK.ViewModel ;
using TINK.ViewModel.Bikes ;
using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler ;
namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
{
2022-09-06 16:08:19 +02:00
[TestFixture]
public class TestBookedDisconnected
{
/// <summary>
/// Test construction of object.
/// </summary>
[Test]
public void Testctor ( )
{
var handler = new BookedDisconnected (
Substitute . For < IBikeInfoMutable > ( ) ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > Substitute . For < IConnector > ( ) ,
2023-04-05 15:02:10 +02:00
Substitute . For < IGeolocationService > ( ) ,
2022-09-06 16:08:19 +02:00
Substitute . For < ILocksService > ( ) ,
( ) = > Substitute . For < IPollingUpdateTaskManager > ( ) ,
Substitute . For < ISmartDevice > ( ) ,
Substitute . For < IViewService > ( ) ,
Substitute . For < IBikesViewModel > ( ) ,
Substitute . For < IUser > ( ) ) ;
// Verify prerequisites.
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
[Test]
public void TestNotSupported ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
var subsequent = handler . HandleRequestOption1 ( ) . Result ;
// Verify that nothing happened because request is not supported.
Assert . AreEqual ( "BookedDisconnected" , subsequent . ButtonText ) ;
Assert . IsFalse ( subsequent . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , subsequent . LockitButtonText ) ;
Assert . IsTrue ( subsequent . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked open
/// </summary>
[Test]
public void TestSearch ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var timeOuts = Substitute . For < ITimeOutProvider > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) )
. Returns ( Task . FromResult ( new LockInfoTdo . Builder { State = LockitLockingState . Open } . Build ( ) ) ) ;
locks . TimeOut . Returns ( timeOuts ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
connector . Command . CalculateAuthKeys ( bike ) ;
bikesViewModel . ActionText = "Searching lock..." ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) ) ;
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
2023-04-05 15:02:10 +02:00
Assert . AreEqual ( "Close lock & end rental" , subsequent . ButtonText ) ;
2022-09-06 16:08:19 +02:00
Assert . IsTrue ( subsequent . IsButtonVisible ) ;
Assert . AreEqual ( "Close lock" , subsequent . LockitButtonText ) ;
Assert . IsTrue ( subsequent . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked unknown
/// </summary>
[Test]
public void TestSearchCalculateAuthKeysFailsWebConnectFailureException ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
connector . Command . CalculateAuthKeys ( bike ) . Returns ( x = > throw new WebConnectFailureException ( "Context info" , new Exception ( "Tst" ) ) ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
bike . LockInfo . State . Returns ( LockingState . UnknownFromHardwareError ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2023-04-05 15:02:10 +02:00
viewService . DisplayAlert ( "Error when connecting with lock!" , "Internet must be reachable to connect to lock of rented bike. Please establish an Internet connection!\r\nContext info\r\nIs WIFI/mobile network available and mobile data activated?" , "OK" ) ;
2022-09-06 16:08:19 +02:00
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked unknown
/// </summary>
[Test]
public void TestSearchCalculateAuthKeysFailsException ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
connector . Command . CalculateAuthKeys ( bike ) . Returns ( x = > throw new Exception ( "Exception message." ) ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
bike . LockInfo . State . Returns ( LockingState . UnknownFromHardwareError ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
viewService . DisplayAlert ( "Error when connecting with lock!" , "Communication error during lock search.\r\nException message." , "OK" ) ;
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked unknown
/// </summary>
[Test]
public void TestSearchConnectFailsOutOfReachException ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var timeOuts = Substitute . For < ITimeOutProvider > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) )
. Returns < Task < LockInfoTdo > > ( x = > { throw new OutOfReachException ( ) ; } ) ;
locks . TimeOut . Returns ( timeOuts ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
connector . Command . CalculateAuthKeys ( bike ) ;
bikesViewModel . ActionText = "Searching lock..." ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) ) ;
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
viewService . DisplayAlert (
"Error when connecting with lock!" ,
"Lock can only be found when rented bike is nearby." ,
"Retry" ,
"Cancel" ) ;
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked unknown
/// </summary>
[Test]
public void TestSearchConnectFailsException ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var timeOuts = Substitute . For < ITimeOutProvider > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) )
. Returns < Task < LockInfoTdo > > ( x = > throw new Exception ( "Exception message." ) ) ;
locks . TimeOut . Returns ( timeOuts ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
connector . Command . CalculateAuthKeys ( bike ) ;
bikesViewModel . ActionText = "Searching lock..." ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) ) ;
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
viewService . DisplayAdvancedAlert (
"Error when connecting with lock!" ,
"Your mobile device does not connect to the bike lock. Please step as close as possible to the bike and try again." ,
"" ,
"Retry" ,
"Cancel" ) ;
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
/// <summary>
/// Use case: Search.
/// Final state: Booked unknown
/// </summary>
[Test]
public void TestSearchConnectNotOpen ( )
{
var bike = Substitute . For < IBikeInfoMutable > ( ) ;
var connector = Substitute . For < IConnector > ( ) ;
var command = Substitute . For < ICommand > ( ) ;
2023-04-05 15:02:10 +02:00
var geolocation = Substitute . For < IGeolocationService > ( ) ;
2022-09-06 16:08:19 +02:00
var locks = Substitute . For < ILocksService > ( ) ;
var pollingManager = Substitute . For < IPollingUpdateTaskManager > ( ) ;
var viewService = Substitute . For < IViewService > ( ) ;
var bikesViewModel = Substitute . For < IBikesViewModel > ( ) ;
var activeUser = Substitute . For < IUser > ( ) ;
var timeOuts = Substitute . For < ITimeOutProvider > ( ) ;
var handler = new BookedDisconnected (
bike ,
( ) = > true , // isConnectedDelegate
( isConnexted ) = > connector ,
geolocation ,
locks ,
( ) = > pollingManager ,
Substitute . For < ISmartDevice > ( ) ,
viewService ,
bikesViewModel ,
activeUser ) ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) )
. Returns ( new LockInfoTdo . Builder { State = null } . Build ( ) ) ;
locks . TimeOut . Returns ( timeOuts ) ;
bike . State . Value . Returns ( InUseStateEnum . Booked ) ;
var subsequent = handler . HandleRequestOption2 ( ) . Result ;
2023-04-19 12:14:14 +02:00
// Verify behavior
2022-09-06 16:08:19 +02:00
Received . InOrder ( ( ) = >
{
bikesViewModel . Received ( 1 ) . IsIdle = false ; // GUI must be locked
bikesViewModel . ActionText = "One moment please..." ;
pollingManager . StopUpdatePeridically ( ) ; // Polling must be stopped before any COPR and lock service action
bikesViewModel . ActionText = "Request server..." ;
connector . Command . CalculateAuthKeys ( bike ) ;
bikesViewModel . ActionText = "Searching lock..." ;
locks . ConnectAsync ( Arg . Any < LockInfoAuthTdo > ( ) , Arg . Any < TimeSpan > ( ) ) ;
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
viewService . DisplayAlert ( "Error when connecting with lock!" , "Schlossstatus des gemieteten Rads konnte nicht ermittelt werden." , "OK" ) ;
bikesViewModel . ActionText = "Updating..." ;
pollingManager . StartUpdateAyncPeridically ( ) ; // polling must be restarted again
2023-02-22 14:03:35 +01:00
bikesViewModel . ActionText = string . Empty ;
2022-09-06 16:08:19 +02:00
bikesViewModel . Received ( 1 ) . IsIdle = true ; // GUI must be unlocked
} ) ;
// Verify state after action
Assert . AreEqual ( "BookedDisconnected" , handler . ButtonText ) ;
Assert . IsFalse ( handler . IsButtonVisible ) ;
Assert . AreEqual ( "Search lock" , handler . LockitButtonText ) ;
Assert . IsTrue ( handler . IsLockitButtonVisible ) ;
}
}
2021-07-12 21:31:46 +02:00
}