Version 3.0.375

This commit is contained in:
Anja 2023-11-06 12:23:09 +01:00
parent 2c790239cb
commit ca080c87c0
194 changed files with 10092 additions and 10464 deletions

View file

@ -58,7 +58,7 @@ namespace TestShareeLib.Model.Bike.BluetoothLock
new Guid(),
"17"),
"My Station Name").ToString(),
Is.EqualTo("Id=MyBikeId;type=Cargo;state=Disposable"));
Is.EqualTo("Id=MyBikeId;type=Cargo;state=Disposable;Lock id=42"));
}
[Test]

View file

@ -3,6 +3,7 @@ using NUnit.Framework;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
using TINK.Model.Bikes.BikeInfoNS.DriveNS.BatteryNS;
using TINK.Model.Bikes.BikeInfoNS.DriveNS.EngineNS;
using DriveType = TINK.Model.Bikes.BikeInfoNS.DriveNS.DriveType;
namespace TestShareeLib.Model.BikeInfo.DriveNS
{

View file

@ -314,6 +314,252 @@ namespace TestShareeLib.Model.Connector
}
}";
private const string BIKESAVAILABLE2 = @"{
""lang"" : ""de"",
""user_group"" : [
""FR300103"",
""FR300101""
],
""uri_primary"" : ""https://shareeapp-primary.copri.eu"",
""bikes_occupied"" : {
""160317"" : {
""station"" : ""FR101"",
""bike_type"" : {
""category"" : ""cargo"",
""wheels"" : ""2""
},
""rental_minute_all"" : 2666,
""description"" : ""Lastenrad Oliver Pieper"",
""unit_price"" : ""2.00"",
""end_time"" : ""2023-10-29 12:03:28"",
""total_price"" : ""0.00"",
""K_u"" : ""[72, 41, 3, -121, -121, -40, -125, -92, -70, -78, -12, -73, 11, -31, 108, 63, -22, 28, -73, 35, 0, 0, 0, 0]"",
""real_clock"" : ""1 day 20:26"",
""rentalog"" : """",
""bike"" : ""FR1011"",
""gps"" : {
""latitude"" : ""47.9765599546954"",
""longitude"" : ""7.82557798549533""
},
""request_time"" : ""2023-10-27 15:37:16.296418+02"",
""aa_ride"" : ""0"",
""state"" : ""occupied"",
""lock_state"" : ""unlocked"",
""K_seed"" : ""[-77, 79, -81, -34, 107, -34, -100, -29, 124, 30, -50, -63, -111, 0, 22, -19]"",
""Ilockit_ID"" : ""ISHAREIT-2200536"",
""system"" : ""Ilockit"",
""computed_hours"" : ""44.1"",
""freed_time"" : ""- 00:20"",
""bike_group"" : [
""FR300101""
],
""start_time"" : ""2023-10-27 15:37:24.371987+02"",
""rental_description"" : {
""tarif_elements"" : {
""4"" : [
""Max. Gebühr"",
""24,00 / 24 Std""
],
""1"" : [
""Mietgebühr"",
""2,00 / 30 Min ""
],
""7"" : [
""Aktuelle Mietzeit"",
""1 Tag 20 Std 26 Min ""
],
""2"" : [
""ab 2. Tag"",
""3,00 / 30 Min ""
],
""6"" : [
""Gratis Mietzeit"",
""20 Min / Tag""
]
},
""reserve_timerange"" : ""15"",
""name"" : ""E-Lastenrad private"",
""id"" : ""5533""
},
""discount"" : ""-100%"",
""uri_operator"" : ""https://shareeapp-fr01.copri.eu""
}
},
""bike_info_html"" : ""site/bike_info_sharee_230922.html"",
""agb_checked"" : ""1"",
""user_tour"" : [],
""last_used_operator"" : {
""operator_name"" : ""TeilRad GmbH"",
""operator_phone"" : ""+49 761 45370097"",
""operator_email"" : ""hotline@sharee.bike"",
""operator_hours"" : ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr""
},
""user_id"" : ""ohauff@posteo.de"",
""impress_html"" : ""site/impress_1.html"",
""authcookie"" : ""5781_f172cf59108fe53e7524c841847fee69_shoo0faiNg"",
""faq_app_fullurl"" : ""https://sharee.bike/faq/fahrrad-nutzung"",
""tariff_info_html"" : ""site/tarif_info_sharee_230922.html"",
""agb_html"" : ""site/agb_sharee_2.html"",
""response"" : ""bikes_available"",
""bikes"" : {
""FR1545"" : {
""Ilockit_GUID"" : ""00000000-0000-0000-0000-e38bf9d32234"",
""description"" : ""C2-02-00545"",
""lock_state"" : ""locked"",
""system"" : ""Ilockit"",
""Ilockit_ID"" : ""ISHAREIT-2200545"",
""state"" : ""available"",
""aa_ride"" : ""0"",
""smartlock_type"" : {
""battery"" : {
""charge_current_percent"" : ""100""
},
""engine"" : {
""manufacturer"" : ""Ilockit""
}
},
""bike_type"" : {
""battery"" : {
""charge_max_bars"" : ""5"",
""charge_current_bars"" : ""0"",
""charge_current_percent"" : ""0"",
""hidden"" : ""0"",
""backend_accessible"" : ""0""
},
""engine"" : {
""manufacturer"" : ""dummy""
},
""category"" : ""cargo"",
""wheels"" : ""2""
},
""station"" : ""FR101"",
""uri_operator"" : ""https://shareeapp-fr01.copri.eu"",
""gps"" : {
""latitude"" : ""47.9767844"",
""longitude"" : ""7.8258182""
},
""bike"" : ""FR1545"",
""bike_group"" : [
""FR300101""
],
""authed"" : ""1"",
""rental_description"" : {
""id"" : ""5533"",
""reserve_timerange"" : ""15"",
""name"" : ""E-Lastenrad private"",
""tarif_type"" : ""3"",
""tarif_elements"" : {
""6"" : [
""Gratis Mietzeit"",
""20 Min / Tag""
],
""2"" : [
""ab 2. Tag"",
""3,00 / 30 Min ""
],
""4"" : [
""Max. Gebühr"",
""24,00 / 24 Std""
],
""1"" : [
""Mietgebühr"",
""2,00 / 30 Min ""
]
}
}
},
""FR1012"" : {
""uri_operator"" : ""https://shareeapp-fr01.copri.eu"",
""bike"" : ""FR1012"",
""gps"" : {
""latitude"" : ""47.998034725897"",
""longitude"" : ""7.78498157858849""
},
""bike_group"" : [
""FR300101""
],
""rental_description"" : {
""rental_info"" : {
""1"" : [
""Tracking"",
""Ich stimme der Speicherung (Tracking) meiner Fahrstrecke zwecks wissenschaftlicher Auswertung und Berechnung der CO2-Einsparung zu!""
]
},
""tarif_elements"" : {
""1"" : [
""Mietgebühr"",
""2,00 / 30 Min ""
],
""4"" : [
""Max. Gebühr"",
""24,00 / 24 Std""
],
""6"" : [
""Gratis Mietzeit"",
""20 Min / Tag""
],
""2"" : [
""ab 2. Tag"",
""3,00 / 30 Min ""
]
},
""reserve_timerange"" : ""15"",
""name"" : ""E-Lastenrad private"",
""id"" : ""5533"",
""tarif_type"" : ""3""
},
""authed"" : ""1"",
""description"" : ""Contributor-bike devel"",
""Ilockit_GUID"" : ""00000000-0000-0000-0000-ef6655036de7"",
""lock_state"" : ""locked"",
""system"" : ""Ilockit"",
""Ilockit_ID"" : ""ISHAREIT-2309492"",
""state"" : ""available"",
""bike_type"" : {
""engine"" : {
""manufacturer"" : ""dummy""
},
""battery"" : {
""charge_current_bars"" : ""5"",
""charge_max_bars"" : ""5"",
""backend_accessible"" : ""0"",
""charge_current_percent"" : ""100"",
""hidden"" : ""0""
},
""category"" : ""cargo"",
""wheels"" : ""2""
},
""smartlock_type"" : {
""engine"" : {
""manufacturer"" : ""Ilockit""
},
""battery"" : {
""charge_current_percent"" : """"
}
},
""aa_ride"" : ""0"",
""station"" : ""FR103""
},
},
""aowner"" : ""186"",
""init_map"" : {
""radius"" : ""2.9"",
""center"" : {
""longitude"" : ""7.825490"",
""latitude"" : ""47.976634""
}
},
""new_authcoo"" : ""0"",
""apiserver"" : ""https://shareeapp-fr01.copri.eu"",
""merchant_id"" : ""shoo0faiNg"",
""privacy_html"" : ""site/privacy_sharee_2.html"",
""response_state"" : ""OK, nothing todo"",
""debuglevel"" : ""104"",
""clearing_cache"" : ""0"",
""copri_version"" : ""4.1.23.20"",
""project_id"" : ""Freiburg""
}";
private const string BIKESAVAILABLEEMPTY = @"{
""copri_version"" : ""4.1.0.0"",
""bikes"" : {},
@ -609,5 +855,46 @@ namespace TestShareeLib.Model.Connector
Assert.IsNull(result.Exception);
}
[Test]
public async Task TestGetBikesOperratorUri()
{
var server = Substitute.For<ICachedCopriServer>();
server.GetBikesAvailable(operatorUri: Arg.Any<Uri>()).Returns(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE2),
new GeneralData())));
var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAsync(new Uri("https://shareeapp-fr01.copri.eu//APIjsonserver"));
await server.DidNotReceive().GetBikesOccupied();
await server.DidNotReceive().GetBikesOccupied(Arg.Any<bool>());
server.Received().AddToCache(Arg.Any<Result<BikesAvailableResponse>>(), Arg.Any<Uri>());
Assert.AreEqual(3, result.Response.Count);
Assert.AreEqual(typeof(CopriCallsHttps), result.Source);
Assert.IsNull(result.Exception);
}
[Test]
public async Task TestGetBikesOperratorUriCache()
{
var server = Substitute.For<ICachedCopriServer>();
server.GetBikesAvailable(operatorUri: Arg.Any<Uri>()).Returns(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE2),
new GeneralData())));
var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAsync(new Uri("https://shareeapp-fr01.copri.eu//APIjsonserver"));
await server.DidNotReceive().GetBikesOccupied();
await server.DidNotReceive().GetBikesOccupied(Arg.Any<bool>());
server.DidNotReceive().AddToCache(Arg.Any<Result<BikesAvailableResponse>>(), Arg.Any<Uri>());
Assert.AreEqual(3, result.Response.Count);
Assert.AreEqual(typeof(CopriCallsMonkeyStore), result.Source);
Assert.IsNull(result.Exception);
}
}
}

View file

@ -7,7 +7,6 @@ using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Connector;
using TINK.Repository.Exception;
using TINK.Repository.Response;
using TINK.Repository.Response.Stations;
using TINK.Repository.Response.Stations.Station;
using JsonConvertRethrow = TINK.Repository.Response.JsonConvertRethrow;
@ -1080,5 +1079,93 @@ namespace TestTINKLib.Fixtures.Connector
}").GetMaxReservationTimeSpan().TotalMinutes,
Is.EqualTo(15));
[Test]
public void TestGetOperatorUriNull()
=> Assert.That(
TextToTypeHelper.GetOperatorUri(stationInfo: null),
Is.Null);
[Test]
public void TestGetOperatorUriEmpty()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<StationInfo>(@"").GetOperatorUri(),
Is.Null);
[Test]
public void TestGetOperatorUriNoUri()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<StationInfo>(@"
{
""station"": ""FR101""
}").GetOperatorUri(),
Is.Null);
[Test]
public void TestGetOperatorUriInvalidUrl()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<StationInfo>(@"
{
""station"": ""FR101"",
""uri_operator"": ""ThisIsNoUrl""
}").GetOperatorUri(),
Is.Null);
[Test]
public void TestGetOperatorUri()
=> Assert.That(
JsonConvertRethrow.DeserializeObject<StationInfo>(@"
{
""station"": ""FR101"",
""uri_operator"": ""https://shareedms-tr.copri.eu""
}").GetOperatorUri().AbsoluteUri,
Is.EqualTo("https://shareedms-tr.copri.eu//APIjsonserver"));
[Test]
public void TestGetStation()
{
var stationInfo = JsonConvertRethrow.DeserializeObject<StationInfo>(@"{
""station"": ""FR104"",
""withpub"": ""1"",
""cached"": ""1"",
""authed"": ""1"",
""state"": ""available"",
""operator_data"": {
""operator_email"": ""hotline@sharee.bike"",
""operator_hours"": ""¼rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_phone"": ""+49 761 45370097"",
""operator_name"": ""TeilRad GmbH""
},
""station_type"": {
""city"": {
""bike_group"": ""FR300103"",
""bike_count"": ""0""
},
""cargo"": {
""bike_group"": ""FR300101"",
""bike_count"": ""0""
}
},
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""gps_radius"": ""50"",
""capacity"": ""1"",
""description"": ""fahrradspezialitäten"",
""gps"": {
""longitude"": ""7.837621"",
""latitude"": ""47.989807""
},
""station_group"": [
""FR300101"",
""FR300103""
],
""bike_count"": ""0""
}");
var station = stationInfo.GetStation();
Assert.That(
station.OperatorUri.AbsoluteUri,
Is.EqualTo("https://shareeapp-fr01.copri.eu//APIjsonserver"));
}
}
}

View file

@ -1,8 +1,9 @@
using Newtonsoft.Json;
using Newtonsoft.Json;
using NUnit.Framework;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
using TINK.Model.Connector.Updater;
using TINK.Repository.Response;
using DriveType = TINK.Model.Bikes.BikeInfoNS.DriveNS.DriveType;
namespace TestShareeLib.Model.Connector.Updater
{

View file

@ -164,7 +164,7 @@ namespace TestShareeLib.Repository.Request
/// <summary> Hypothetical scenario.</summary>
[Test]
public void TestDoBookActionClose()
public void TestDoBookActionCloseLock()
{
Assert.AreEqual(
"request=booking_update&bike=42&authcookie=456123&Ilockit_GUID=0000f00d-1212-efde-1523-785fef13d123&state=occupied&lock_state=locking",

View file

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Model\Bikes\BikeInfoNS\BC\TestBikeSerializeJSON.cs" />
<Compile Remove="Model\Bikes\BikeInfoNS\TestBikeCollectionSerializeJSON.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Model\Bikes\BikeInfoNS\BC\TestBikeSerializeJSON.cs" />
<None Include="Model\Bikes\BikeInfoNS\TestBikeCollectionSerializeJSON.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Xamarin.Essentials" Version="1.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TestFramework\TestFrameworkCore.csproj" />
<ProjectReference Include="..\TINKLib\ShareeBikeLibCore.csproj" />
</ItemGroup>
</Project>

View file

@ -315,7 +315,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
@ -833,6 +832,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -894,6 +897,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Lock reports it is still closed. Please try again!", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -954,6 +961,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Position of lock bolt is unknown. Lock could be closed or open. Make sure that no spoke presses against the lock bolt and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -1019,6 +1030,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
"Please try again.",
"OK"
);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;

View file

@ -161,6 +161,10 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -221,6 +225,10 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -281,6 +289,10 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be opened!", "Lock reports it is still closed. Please try again!", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -341,6 +353,10 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
locks.Received()[0].OpenAsync(); // Lock must be closed
bikesViewModel.ActionText = "";
viewService.DisplayAlert("Lock could not be opened!", "Position of lock bolt is unknown. Lock could be closed or open. Make sure that no spoke presses against the lock bolt and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -405,6 +421,10 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
"Exception message.",
"Please try again.",
"OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;

View file

@ -43,7 +43,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify prerequisites.
Assert.AreEqual("Reserve bike", handler.ButtonText);
Assert.IsTrue(handler.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", handler.LockitButtonText);
Assert.IsFalse(handler.IsLockitButtonVisible);
}
@ -95,16 +94,15 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Reserve bike.
/// Final state: Reserved closed.
/// Final state: Booked open.
/// </summary>
[Test]
public void TestReserveAndConnect()
public void TestReserveAndConnectAndOpen()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
@ -159,7 +157,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.DoReserve(bike); // Booking must be performed
bikesViewModel.ActionText = "Searching lock...";
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
bikesViewModel.ActionText = string.Empty;
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
@ -181,6 +178,183 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
}
/// <summary>
/// Use case: Reserve bike, book and open.
/// Final state: Booked unspecific.
/// </summary>
[Test]
public void TestReserveAndConnectAndOpenOpenFailsException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true));
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>())
.Returns(Task.FromResult(new LockInfoTdo.Builder { State = LockitLockingState.Closed }.Build())); // Return lock state indicating success
locks.TimeOut.Returns(timeOuts);
viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reserving bike...";
connector.Command.DoReserve(bike); // Booking must be performed
bikesViewModel.ActionText = "Searching lock...";
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
locks.Received()[0].OpenAsync(); // Lock must be opened
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAdvancedAlert(
"Lock could not be opened!",
"Please try again.",
"Exception message.",
"OK"
);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state "booked unspecific" after action
Assert.AreEqual("BookedDisconnected", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Reserve bike, book and open.
/// Final state: Booked unspecific.
/// </summary>
[Test]
public void TestReserveAndConnectAndOpenOpenFailsCouldntOpenBoltBlockedException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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>();
bike.TariffDescription.MaxReservationTimeSpan.Returns(TimeSpan.FromMinutes(15));
var handler = new DisposableDisconnected(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Reserve bike Nr. 0 free of charge for 15 min?", "Yes", "No").Returns(Task.FromResult(true));
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>())
.Returns(Task.FromResult(new LockInfoTdo.Builder { State = LockitLockingState.Closed }.Build())); // Return lock state indicating success
locks.TimeOut.Returns(timeOuts);
viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new CouldntOpenBoldIsBlockedException());
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reserving bike...";
connector.Command.DoReserve(bike); // Booking must be performed
bikesViewModel.ActionText = "Searching lock...";
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";
locks.Received()[0].OpenAsync(); // Lock must be opened
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Lock could not be opened!",
"Lock bolt is blocked. Make sure that no spoke presses against the lock bolt and try again.",
"OK"
);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state "booked unspecific" after action
Assert.AreEqual("Open lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Reserve bike.
/// Final state: Same as initial state.
@ -242,7 +416,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
@ -307,7 +480,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
@ -377,7 +549,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
@ -603,7 +774,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
}

View file

@ -42,10 +42,198 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
Substitute.For<IUser>());
// Verify prerequisites.
Assert.AreEqual("Close lock or rent bike", handler.ButtonText);
Assert.AreEqual("Rent bike", handler.ButtonText);
Assert.IsTrue(handler.IsButtonVisible);
Assert.AreEqual(nameof(DisposableOpen), handler.LockitButtonText);
Assert.IsFalse(handler.IsLockitButtonVisible);
Assert.AreEqual("Close lock", handler.LockitButtonText);
Assert.IsTrue(handler.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Booked Open.
/// </summary>
[Test]
public void TestDoBook()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reserving bike...";
connector.Command.DoReserve(bike);
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Disposable Closed.
/// </summary>
[Test]
public void TestDoBookDoReserveFailsWebConnectFailureException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
connector.Command.DoReserve(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Disposable);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reserving bike...";
connector.Command.DoReserve(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Connection to booking system not possible.",
"A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.",
"OK");
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Rent bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Disposable Closed.
/// </summary>
[Test]
public void TestDoBookDoReserveFailsException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
connector.Command.DoReserve(bike).Returns(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Disposable);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reserving bike...";
connector.Command.DoReserve(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAdvancedAlert("Bike could not be reserved!", "Exception message.","Please try again.", "OK");
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Rent bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Close lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
@ -79,13 +267,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(false));
locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
var subsequent = handler.HandleRequestOption1().Result;
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -95,6 +282,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
@ -141,16 +332,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(false));
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
var subsequent = handler.HandleRequestOption1().Result;
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -162,6 +349,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
locks[0].CloseAsync();
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be closed!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -206,16 +399,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(false));
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open);
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
var subsequent = handler.HandleRequestOption1().Result;
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -232,6 +421,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
"Please try again.",
"OK"
);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -244,209 +439,5 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
Assert.AreEqual(nameof(DisposableDisconnected), subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Booked Open.
/// </summary>
[Test]
public void TestDoBook()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(true));
bike.State.Value.Returns(InUseStateEnum.Booked);
bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual(string.Empty, subsequent.LockitButtonText);
Assert.That(subsequent.IsLockitButtonVisible, Is.False);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Disposable Closed.
/// </summary>
[Test]
public void TestDoBookDoBookFailsWebConnectFailureException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(true));
connector.Command.DoBookAsync(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub")));
locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed));
bike.State.Value.Returns(InUseStateEnum.Disposable);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Bike could not be rented!",
"A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.",
"OK");
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Disposable Closed.
/// </summary>
[Test]
public void TestDoBookDoBookFailsException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 DisposableOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Rent bike", "Close lock").Returns(Task.FromResult(true));
connector.Command.DoBookAsync(bike).Returns(x => throw new Exception("Exception message."));
locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed));
bike.State.Value.Returns(InUseStateEnum.Disposable);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Bike could not be rented!", "Exception message.", "OK");
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Reserve bike", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("DisposableDisconnected", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
}
}

View file

@ -737,7 +737,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
locks[0].OpenAsync()
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message.")); // Return lock state indicating success
.Returns<Task<LockitLockingState?>>(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking call leads to setting of state to booked.
bike.LockInfo.State.Returns(LockingState.UnknownFromHardwareError);

View file

@ -421,7 +421,6 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
connector.Command.CalculateAuthKeys(bike);
bikesViewModel.ActionText = "Searching lock...";
locks.ConnectAsync(Arg.Any<LockInfoAuthTdo>(), Arg.Any<TimeSpan>());
bikesViewModel.ActionText = string.Empty;
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike, LockingAction.Open); // Booking must be performed
bikesViewModel.ActionText = "<h4><b>Lock is opening.<br/>Please wait until it is completely open.</b></h4>";

View file

@ -43,10 +43,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
Substitute.For<IUser>());
// Verify prerequisites.
Assert.AreEqual("Close lock or rent bike", handler.ButtonText);
Assert.AreEqual("Close lock", handler.ButtonText);
Assert.IsTrue(handler.IsButtonVisible);
Assert.AreEqual("Alarm/ Sounds verwalten", handler.LockitButtonText);
Assert.IsFalse(handler.IsLockitButtonVisible);
Assert.AreEqual("Rent bike", handler.LockitButtonText);
Assert.IsTrue(handler.IsLockitButtonVisible);
}
/// <summary>
@ -78,12 +78,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bikesViewModel,
activeUser);
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(false));
bike.State.Value.Returns(InUseStateEnum.Booked); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result;
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -91,11 +89,13 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike, LockingAction.Open);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
bikesViewModel.ActionText = "Updating...";
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
@ -137,15 +137,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bikesViewModel,
activeUser);
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(false));
connector.Command.DoBookAsync(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("Tst")));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
connector.Command.DoBookAsync(bike, LockingAction.Open).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("Tst")));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result;
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -153,17 +150,17 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
connector.Command.DoBookAsync(bike, LockingAction.Open);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Bike could not be rented!",
"Connection to booking system not possible.",
"A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.",
"OK");
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -171,15 +168,15 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
});
// Verify state after action
Assert.AreEqual("Cancel reservation", subsequent.ButtonText);
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText);
Assert.AreEqual("Rent bike", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: User books bike.
/// Final state: Reserved closed.
/// Final state: Reserved open.
/// </summary>
[Test]
public void TestBookBookFailsException()
@ -206,15 +203,13 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bikesViewModel,
activeUser);
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(false));
connector.Command.DoBookAsync(bike).Returns(x => throw new Exception("Exception message."));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
connector.Command.DoBookAsync(bike, LockingAction.Open).Returns(x => throw new Exception("Exception message."));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open); // Requesthandler factory queries lock state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result;
var subsequent = handler.HandleRequestOption2().Result;
// Verify behavior
Received.InOrder(() =>
@ -222,18 +217,18 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
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.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Renting bike...";
connector.Command.DoBookAsync(bike);
connector.Command.DoBookAsync(bike, LockingAction.Open);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAdvancedAlert(
"Bike could not be rented!",
"Exception message.",
"Please try again.",
"OK");
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -241,15 +236,15 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
});
// Verify state after action
Assert.AreEqual("Cancel reservation", subsequent.ButtonText);
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText);
Assert.AreEqual("Rent bike", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Final state: Booked.
/// Use case: Lock bike.
/// Final state: Disposable Disconnected.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservation()
@ -278,11 +273,12 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync().Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
var subsequent = handler.HandleRequestOption1().Result;
@ -294,8 +290,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
pollingManager.StopAsync(); // Polling must be stopped before any COPR and lock service action
bikesViewModel.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Canceling reservation...";
connector.Command.DoCancelReservation(bike);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Disconnecting lock...";
locks.DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
bikesViewModel.ActionText = "Updating...";
@ -312,82 +310,11 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Final state: Reserved open.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservationCloseFailsOutOfReachException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 ReservedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(
string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => { throw new OutOfReachException(); });
bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open);
var subsequent = handler.HandleRequestOption1().Result;
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
// 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.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Lock could not be closed!",
"Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.",
"OK");
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Cancel reservation", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Use case: Lock bike.
/// Final state: Same as initial state.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservationCloseFailsException()
public void TestCloseLockCloseFailsOutOfReachExcption()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
@ -413,11 +340,76 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bike.Id.Returns("0");
viewService.DisplayAlert(
string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new OutOfReachException());
locks[0].CloseAsync()
.Returns<Task<LockitLockingState?>>(x => { throw new Exception("Exception message."); });
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.UnknownDisconnected);
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert("Lock could not be closed!", "Make sure you have granted Bluetooth permission to the app. Step as close as possible to the bike lock and try again.", "OK");
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("Cancel reservation", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Lock bike.
/// Final state: Same as initial state.
/// </summary>
[Test]
public void TestCloseLockCloseFailsCouldntCloseMovingException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 ReservedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
locks[0].CloseAsync().Returns<Task<LockitLockingState?>>(x => throw new CouldntCloseMovingException());
viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false));
bike.State.Value.Returns(InUseStateEnum.Reserved); // Requesthandler factory queries state to create appropriate request handler object.
bike.LockInfo.State.Returns(LockingState.Open);
@ -437,8 +429,13 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Lock could not be closed!",
"Step close to the lock and try again or report bike to operator!\r\nException message.",
"OK");
"The process is motion sensitive. Step close to the lock, do not move, and try again.",
"OK"
);
bikesViewModel.ActionText = "Reading charging level...";
locks[0].GetBatteryPercentageAsync();
bikesViewModel.ActionText = "Updating lock state...";
connector.Command.UpdateLockingStateAsync(bike, null);
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
@ -446,233 +443,10 @@ namespace TestTINKLib.Fixtures.ObjectTests.ViewModel.Bikes.Bike.BluetoothLock.Re
});
// Verify state after action
Assert.AreEqual("Cancel reservation", subsequent.ButtonText);
Assert.AreEqual("Close lock", subsequent.ButtonText);
Assert.IsTrue(subsequent.IsButtonVisible);
Assert.AreEqual("Search lock", subsequent.LockitButtonText);
Assert.AreEqual("Rent bike", subsequent.LockitButtonText);
Assert.IsTrue(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Final state: Reserved closed.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservationCancelReservationInvalidAuthorizationResponseException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 ReservedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
var response = JsonConvert.DeserializeObject<AuthorizationResponse>(@"
{
""response"" : ""authorization"",
""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"",
""user_group"" : [ ""TINK"", ""Konrad"" ],
""response_state"" : ""OK"",
""apiserver"" : ""https://tinkwwp.copri-bike.de""
}");
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
var subsequent = handler.HandleRequestOption1().Result;
// 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.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Canceling reservation...";
connector.Command.DoCancelReservation(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Reservation could not be canceled!",
"Your session has expired. Please login new and try again.",
"OK");
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("InvalidState", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("InvalidState", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Final state: Reserved closed.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservationCancelReservationWebConnectFailureException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 ReservedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result;
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
// 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.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Canceling reservation...";
connector.Command.DoCancelReservation(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAlert(
"Reservation could not be canceled!",
"A stable Internet connection is required. Connect to WIFI or to mobile network and activate mobile data. Try again.",
"OK");
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("InvalidState", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("InvalidState", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
/// <summary>
/// Use case: Close lock and cancel reservation.
/// Final state: Reserved closed.
/// </summary>
[Test]
public void TestCloseLockAndCancelReservationCancelReservationException()
{
var bike = Substitute.For<IBikeInfoMutable>();
var connector = Substitute.For<IConnector>();
var command = Substitute.For<ICommand>();
var geolocation = Substitute.For<IGeolocationService>();
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 ReservedOpen(
bike,
() => true, // isConnectedDelegate
(isConnexted) => connector,
geolocation,
locks,
() => pollingManager,
Substitute.For<ISmartDevice>(),
viewService,
bikesViewModel,
activeUser);
bike.Id.Returns("0");
viewService.DisplayAlert(string.Empty, "Close lock or rent bike Nr. 0?", "Close lock", "Rent bike").Returns(Task.FromResult(true));
locks[0].CloseAsync().Returns(LockitLockingState.Closed);
connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.", new Exception("chub")));
bike.State.Value.Returns(InUseStateEnum.Disposable); // Requesthandler factory queries state to create appropriate request handler object.
var subsequent = handler.HandleRequestOption1().Result;
locks.DidNotReceive().DisconnectAsync(Arg.Any<int>(), Arg.Any<Guid>());
// 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.ActionText = "The lock bolt moves through the spokes of the rear wheel.";
locks[0].CloseAsync();
bikesViewModel.ActionText = "Canceling reservation...";
connector.Command.DoCancelReservation(bike);
bikesViewModel.ActionText = string.Empty;
viewService.DisplayAdvancedAlert(
"Reservation could not be canceled!",
"Exception message.",
"Please try again.",
"OK");
bikesViewModel.ActionText = "Updating...";
pollingManager.StartAsync(); // polling must be restarted again
bikesViewModel.ActionText = string.Empty;
bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked
});
// Verify state after action
Assert.AreEqual("InvalidState", subsequent.ButtonText);
Assert.IsFalse(subsequent.IsButtonVisible);
Assert.AreEqual("InvalidState", subsequent.LockitButtonText);
Assert.IsFalse(subsequent.IsLockitButtonVisible);
}
}
}

View file

@ -47,7 +47,7 @@ namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
// Verify prerequisites.
Assert.AreEqual("Open lock & rent bike", handler.ButtonText);
Assert.IsFalse(handler.IsButtonVisible);
Assert.IsTrue(handler.IsButtonVisible);
Assert.AreEqual("Close lock", handler.LockitButtonText);
Assert.IsTrue(handler.IsLockitButtonVisible);
}