From e4fb48f6ab9d6a8232383a91624e2596bd7c792f Mon Sep 17 00:00:00 2001 From: Oliver Hauff Date: Thu, 13 May 2021 20:09:46 +0200 Subject: [PATCH] Initial version --- TestShareeLib/Model/Bike/BC/TestBikeInfo.cs | 17 + .../Model/Bike/BC/TestBikeMutable.cs | 80 ++ .../Model/Bike/BC/TestBikeSerializeJSON.cs | 59 + .../Model/Bike/BluetoothLock/TestBikeInfo.cs | 47 + .../Model/Bike/BluetoothLock/TestLockInfo.cs | 172 +++ .../Bike/BluetoothLock/TestLockInfoHelper.cs | 53 + .../Bike/BluetoothLock/TestLockInfoMutable.cs | 66 + TestShareeLib/Model/Bike/TestBike.cs | 46 + .../Model/Bike/TestBikeCollection.cs | 22 + .../Model/Bike/TestBikeCollectionMutable.cs | 109 ++ .../Bike/TestBikeCollectionSerializeJSON.cs | 67 + .../State/TestStateBookedInfoSerializeJSON.cs | 42 + .../Model/TestBikeCollectionFilter.cs | 86 ++ .../Response/TestBikesAvailableResponse.cs | 66 + .../Repository/Response/TestJsonConvert.cs | 26 + .../Response/TestTariffDescription.cs | 97 ++ .../Services/TestServicesContainerMutable.cs | 68 + TestShareeLib/TestHelper.cs | 18 + TestShareeLib/TestShareeLib.csproj | 43 + TestShareeLib/TestShareeLib.sln | 31 + .../RequestHandler/TestBookedUnknown.cs | 963 ++++++++++++++ .../RequestHandler/TestReservedClosed.cs | 1127 +++++++++++++++++ .../RequestHandler/TestReservedUnknown.cs | 961 ++++++++++++++ .../Settings/TestServicesViewModel.cs | 121 ++ 24 files changed, 4387 insertions(+) create mode 100644 TestShareeLib/Model/Bike/BC/TestBikeInfo.cs create mode 100644 TestShareeLib/Model/Bike/BC/TestBikeMutable.cs create mode 100644 TestShareeLib/Model/Bike/BC/TestBikeSerializeJSON.cs create mode 100644 TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs create mode 100644 TestShareeLib/Model/Bike/BluetoothLock/TestLockInfo.cs create mode 100644 TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoHelper.cs create mode 100644 TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoMutable.cs create mode 100644 TestShareeLib/Model/Bike/TestBike.cs create mode 100644 TestShareeLib/Model/Bike/TestBikeCollection.cs create mode 100644 TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs create mode 100644 TestShareeLib/Model/Bike/TestBikeCollectionSerializeJSON.cs create mode 100644 TestShareeLib/Model/State/TestStateBookedInfoSerializeJSON.cs create mode 100644 TestShareeLib/Model/TestBikeCollectionFilter.cs create mode 100644 TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs create mode 100644 TestShareeLib/Repository/Response/TestJsonConvert.cs create mode 100644 TestShareeLib/Repository/Response/TestTariffDescription.cs create mode 100644 TestShareeLib/Services/TestServicesContainerMutable.cs create mode 100644 TestShareeLib/TestHelper.cs create mode 100644 TestShareeLib/TestShareeLib.csproj create mode 100644 TestShareeLib/TestShareeLib.sln create mode 100644 TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs create mode 100644 TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs create mode 100644 TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs create mode 100644 TestShareeLib/ViewModel/Settings/TestServicesViewModel.cs diff --git a/TestShareeLib/Model/Bike/BC/TestBikeInfo.cs b/TestShareeLib/Model/Bike/BC/TestBikeInfo.cs new file mode 100644 index 0000000..ab1714b --- /dev/null +++ b/TestShareeLib/Model/Bike/BC/TestBikeInfo.cs @@ -0,0 +1,17 @@ +using NUnit.Framework; +using TINK.Model.Bike.BC; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike.BC +{ + [TestFixture] + public class TestBikeInfo + { + [Test] + public void TestCtorCopyNull() + { + Assert.Throws( + () => new BikeInfo(null), + "Verify that no unspecific reference not set to... exception is thrown"); + } + } +} diff --git a/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs b/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs new file mode 100644 index 0000000..3c2d452 --- /dev/null +++ b/TestShareeLib/Model/Bike/BC/TestBikeMutable.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using TINK.Model.Bike; +using TINK.Model.Bikes.Bike; +using TINK.Model.State; + +namespace TestTINKLib +{ + [TestFixture] + class TestBikeMutable + { + private class BikeInfoMutable : TINK.Model.Bike.BC.BikeInfoMutable + { + public BikeInfoMutable( + int id, + bool isDemo = false, + IEnumerable group = null, + WheelType? wheelType = null, + TypeOfBike? typeOfBike = null, + string description = null, + int? currentStationId = null, + Uri operatorUri = null, + TariffDescription tariffDescription = null, + Func dateTimeProvider = null, + IStateInfo stateInfo = null) : base(id, isDemo, group, wheelType, typeOfBike, description, currentStationId, operatorUri, tariffDescription, dateTimeProvider, stateInfo) + { + } + } + + [Test] + public void TestConstruct() + { + var l_oBike = new BikeInfoMutable( + 42, + false, + new List { "TINK" }, + WheelType.Two, + TypeOfBike.Cargo); + + Assert.AreEqual(42, l_oBike.Id); + Assert.IsFalse(l_oBike.IsDemo); + Assert.AreEqual(WheelType.Two, l_oBike.WheelType); + Assert.AreEqual(TypeOfBike.Cargo, l_oBike.TypeOfBike); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBike.State.Value); + Assert.IsNull(l_oBike.CurrentStation); + + l_oBike = new BikeInfoMutable( + 17, + true, + new List { "TINK" }, + WheelType.Mono, + TypeOfBike.Allround, + "Test description", + 1); + + Assert.AreEqual(17, l_oBike.Id); + Assert.IsTrue(l_oBike.IsDemo); + Assert.AreEqual(WheelType.Mono, l_oBike.WheelType); + Assert.AreEqual(TypeOfBike.Allround, l_oBike.TypeOfBike); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBike.State.Value); + Assert.AreEqual(1, l_oBike.CurrentStation); + } + + [Test] + public void TestToString() + { + var l_oBike = new BikeInfoMutable(3, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Cargo, dateTimeProvider: () => new DateTime(1970, 1, 1)); + + Assert.AreEqual( + "Id=3, wheel(s)=Two, type=Cargo, demo=False, state=Disposable, location=On the road.", + l_oBike.ToString()); + + l_oBike = new BikeInfoMutable(3, true, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 5, dateTimeProvider: () => new DateTime(1970, 1, 1)); + Assert.AreEqual( + "Id=3, wheel(s)=Trike, type=Allround, demo=True, state=Disposable, location=Station 5.", + l_oBike.ToString()); + } + } +} diff --git a/TestShareeLib/Model/Bike/BC/TestBikeSerializeJSON.cs b/TestShareeLib/Model/Bike/BC/TestBikeSerializeJSON.cs new file mode 100644 index 0000000..9da9790 --- /dev/null +++ b/TestShareeLib/Model/Bike/BC/TestBikeSerializeJSON.cs @@ -0,0 +1,59 @@ +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using TINK.Model.Bike; +using TINK.Model.State; + +using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; + +namespace TestTINKLib +{ + /// + /// Exclude from build beause serialization... see below. + /// + [TestFixture] + public class TestBikeSerializeJSON + { + [Test, Ignore("Disabled because serialization does no more work due to inheritance since commit ec14b93b.")] + public void TestConstruct_SerializeJSON_Disposable() + { + // Create object to test. + var l_oBikeSource = new BikeInfoMutable(3, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Cargo, p_oDateTimeProvider: () => new DateTime(1970, 1, 1)); + + // Verify prerequisites + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeSource.State.Value); + Assert.IsNull(l_oBikeSource.State.MailAddress); + Assert.IsNull(l_oBikeSource.State.Code); + Assert.IsNull(l_oBikeSource.State.From); + + // Serialize object and verify. + var l_oDetected = JsonConvert.SerializeObject(l_oBikeSource, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); + const string EXPECTED = @" + { + ""Id"": 3, + ""CurrentStation"": null, + ""WheelType"": 1, + ""TypeOfBike"": 1, + ""State"": { + ""StateInfoObject"": { + ""$type"": ""TINK.Model.State.StateAvailableInfo, TINKLib"" + } + } + }"; + + Assert.AreEqual( + TestHelper.PrepareXmlForStringCompare(EXPECTED), + TestHelper.PrepareXmlForStringCompare(l_oDetected)); + + // Deserialize object. + var l_oBikeTarget = JsonConvert.DeserializeObject(l_oDetected, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); + + // Verify state. + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeTarget.State.Value); + Assert.IsNull(l_oBikeTarget.State.MailAddress); + Assert.IsNull(l_oBikeTarget.State.Code); + Assert.IsNull(l_oBikeTarget.State.From); + } + } +} diff --git a/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs b/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs new file mode 100644 index 0000000..f270ef4 --- /dev/null +++ b/TestShareeLib/Model/Bike/BluetoothLock/TestBikeInfo.cs @@ -0,0 +1,47 @@ +using NUnit.Framework; +using System; +using TINK.Model.Bike.BluetoothLock; +using TINK.Model.State; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock +{ + [TestFixture] + public class TestBikeInfo + { + [Test] + public void TestCtorCopyNull() + { + Assert.Throws( + () => new BikeInfo(null, null), + "Verify that no unspecific reference not set to... exception is thrown"); + + Assert.Throws( + () => new BikeInfo(new TINK.Model.Bike.BC.BikeInfo(12,1), null), + "Verify that no unspecific reference not set to... exception is thrown"); + } + + [Test] + public void TestCtorAvailable() + { + Assert.AreEqual (12,new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).Id); + Assert.AreEqual(13, new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).CurrentStation); + Assert.AreEqual(InUseStateEnum.Disposable, new BikeInfo(12, 5200544, new Guid("00000000-0000-0000-0000-000000000001"), 13).State.Value); + } + + [Test] + public void TestCtorRequested() + { + Assert.AreEqual(12, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).Id); + Assert.AreEqual(112, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020,1,1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Reserved, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null, null, dateTimeProvider: () => new DateTime(2019, 1, 1)).State.Value); + } + + [Test] + public void TestCtorBooked() + { + Assert.AreEqual(12, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).Id); + Assert.AreEqual(112, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).LockInfo.Id); + Assert.AreEqual(InUseStateEnum.Booked, new BikeInfo(12, 112, new Guid(), null, null, null, new DateTime(2020, 1, 1), "a@b", 13, null /*operator uri*/).State.Value); + } + } +} diff --git a/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfo.cs b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfo.cs new file mode 100644 index 0000000..71c059d --- /dev/null +++ b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfo.cs @@ -0,0 +1,172 @@ +using NUnit.Framework; +using System; +using TINK.Model.Bike.BluetoothLock; +namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock +{ + [TestFixture] + public class TestLockInfo + { + [Test] + public void TestCtor() + { + Assert.AreEqual( + LockingState.Disconnected, + new LockInfo.Builder { Id = 123 }.Build().State); + + Assert.AreEqual( + 123, + new LockInfo.Builder { Id = 123 }.Build().Id); + } + + [Test] + public void TestEquals() + { + Assert.IsTrue(new LockInfo.Builder { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed}.Build() == new LockInfo.Builder { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_Id() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 3, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_Guid() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 2, + Guid = new Guid("1000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_Seed() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 5, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_UserKey() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 9, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_AdminKey() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 11, 1 }, + State = LockingState.Closed + }.Build()); + } + + [Test] + public void TestEqualsFalse_LockingState() + { + Assert.IsFalse(new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Closed + }.Build() == new LockInfo.Builder + { + Id = 2, + Guid = new Guid("0000f00d-1212-efde-1523-785fef13d123"), + Seed = new byte[] { 1, 2 }, + UserKey = new byte[] { 7, 2 }, + AdminKey = new byte[] { 2, 1 }, + State = LockingState.Open + }.Build()); + } + } +} diff --git a/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoHelper.cs b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoHelper.cs new file mode 100644 index 0000000..9bd2310 --- /dev/null +++ b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoHelper.cs @@ -0,0 +1,53 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using TINK.Model.Bike.BluetoothLock; +using TINK.Services.BluetoothLock.Tdo; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock +{ + [TestFixture] + public class TestLockInfoHelper + { + [Test] + public void TestUpdateById_State() + { + var locksInfo = new List { + new LockInfo.Builder { Id = 12, Seed = new byte[] { 3, 5 }, UserKey = new byte[] {2, 1 }, State = LockingState.Unknown }.Build(), + new LockInfo.Builder { Id = 14, Seed = new byte[] { 3, 1 }, UserKey = new byte[] {2, 7 }, State = LockingState.Open }.Build(), + new LockInfo.Builder { Id = 3, Seed = new byte[] { 1, 5 }, UserKey = new byte[] {2, 9 }, State = LockingState.Closed }.Build(), + }; + + var locksInfoTdo = new List { + new LockInfoTdo.Builder { Id =14, State = LockitLockingState.Closed}.Build() + }; + + var resultList = locksInfo.UpdateById(locksInfoTdo); + + var result = resultList.FirstOrDefault(x => x.Id == 14); + Assert.NotNull(result, "Target element was removed."); + Assert.AreEqual(LockingState.Closed, result.State); + } + + [Test] + public void TestUpdateById_Guid() + { + var locksInfo = new List { + new LockInfo.Builder { Id = 12, Seed = new byte[] { 3, 5 }, UserKey = new byte[] {2, 1 }, State = LockingState.Unknown }.Build(), + new LockInfo.Builder { Id = 14, Seed = new byte[] { 3, 1 }, UserKey = new byte[] {2, 7 }, State = LockingState.Open }.Build(), + new LockInfo.Builder { Id = 3, Seed = new byte[] { 1, 5 }, UserKey = new byte[] {2, 9 }, State = LockingState.Closed }.Build(), + }; + + var locksInfoTdo = new List { + new LockInfoTdo.Builder { Id =14, Guid = new System.Guid("00000000-0000-0000-0000-e57e6b9aee16"), State = LockitLockingState.Open}.Build() + }; + + var resultList = locksInfo.UpdateById(locksInfoTdo); + + var result = resultList.FirstOrDefault(x => x.Id == 14); + Assert.NotNull(result, "Target element was removed."); + Assert.AreEqual(new Guid("00000000-0000-0000-0000-e57e6b9aee16"), result.Guid); + } + } +} diff --git a/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoMutable.cs b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoMutable.cs new file mode 100644 index 0000000..3b64b71 --- /dev/null +++ b/TestShareeLib/Model/Bike/BluetoothLock/TestLockInfoMutable.cs @@ -0,0 +1,66 @@ +using NUnit.Framework; +using System; +using System.Linq; +using TINK.Model.Bike.BluetoothLock; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike.BluetoothLock +{ + [TestFixture] + public class TestLockInfoMutable + { + [Test] + public void TestCtor() + { + var lockInfo = new LockInfoMutable( + 1, + new Guid("00000000-0000-0000-0000-e57e6b9aee16"), + new byte[] { 1, 2, 3 }, // User key + new byte[] { 1, 23 }, // Admin key + new byte[] { 1, 12 }, // Seed + LockingState.Closed); + + Assert.AreEqual(1, lockInfo.Id); + Assert.AreEqual(new Guid("00000000-0000-0000-0000-e57e6b9aee16"), lockInfo.Guid); + Assert.IsTrue((new byte[] { 1, 2, 3 }).SequenceEqual(lockInfo.UserKey)); + Assert.IsTrue((new byte[] { 1, 23 }).SequenceEqual(lockInfo.AdminKey)); + Assert.IsTrue((new byte[] { 1, 12 }).SequenceEqual(lockInfo.Seed)); + Assert.AreEqual(LockingState.Closed, lockInfo.State); + } + + [Test] + public void TestSetGuid() + { + var lockInfo = new LockInfoMutable(1, new Guid(), new byte[] { 1, 2, 3 }, new byte[] { 1, 23 }, new byte[] { 1, 12 }, LockingState.Closed); + + lockInfo.Guid = new Guid("00000000-0000-0000-0000-e57e6b9aee16"); + Assert.AreEqual(new Guid("00000000-0000-0000-0000-e57e6b9aee16"), lockInfo.Guid); + } + + [Test] + public void TestLoad() + { + + var lockInfo = new LockInfoMutable( + 2, + new Guid(), + new byte[] { 5, 4 }, // User key + new byte[] { 14, 223 }, // Admin key + new byte[] { 3, 4, 5 }, // Seed + LockingState.Closed); + + lockInfo.Load( + 1, + new Guid("00000000-0000-0000-0000-e57e6b9aee16"), + new byte[] { 1, 12 }, // Seed + new byte[] { 1, 2, 3 }, // User key) + new byte[] { 1, 23 }); + + Assert.AreEqual(1, lockInfo.Id); + Assert.AreEqual(new Guid("00000000-0000-0000-0000-e57e6b9aee16"), lockInfo.Guid); + Assert.IsTrue((new byte[] { 1, 2, 3 }).SequenceEqual(lockInfo.UserKey)); + Assert.IsTrue((new byte[] { 1, 23 }).SequenceEqual(lockInfo.AdminKey)); + Assert.IsTrue((new byte[] { 1, 12 }).SequenceEqual(lockInfo.Seed)); + Assert.AreEqual(LockingState.Closed, lockInfo.State); + } + } +} diff --git a/TestShareeLib/Model/Bike/TestBike.cs b/TestShareeLib/Model/Bike/TestBike.cs new file mode 100644 index 0000000..773a04b --- /dev/null +++ b/TestShareeLib/Model/Bike/TestBike.cs @@ -0,0 +1,46 @@ +using NUnit.Framework; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike +{ + using TINK.Model.Bike; + + [TestFixture] + public class TestBike + { + [Test] + public void TestConstruct() + { + var l_oBike = new Bike(43); + Assert.AreEqual(43, l_oBike.Id); + Assert.AreEqual(null, l_oBike.TypeOfBike); + Assert.AreEqual(null, l_oBike.WheelType); + + l_oBike = new Bike(43, WheelType.Mono, TypeOfBike.Cargo); + + Assert.AreEqual(43, l_oBike.Id); + Assert.AreEqual(TypeOfBike.Cargo, l_oBike.TypeOfBike); + Assert.AreEqual(WheelType.Mono, l_oBike.WheelType); + } + + [Test] + public void TestCompare() + { + var l_oBike1 = new Bike(43); + Assert.AreEqual(43, l_oBike1.Id); + Assert.AreEqual(null, l_oBike1.TypeOfBike); + Assert.AreEqual(null, l_oBike1.WheelType); + + var l_oBike2 = new Bike(42, WheelType.Two, TypeOfBike.Allround); + Assert.IsFalse(l_oBike1 == l_oBike2); + + l_oBike2 = new Bike(43, WheelType.Mono, TypeOfBike.Allround); + Assert.IsFalse(l_oBike1 == l_oBike2); + + l_oBike2 = new Bike(43, WheelType.Two, TypeOfBike.Cargo); + Assert.IsFalse(l_oBike1 == l_oBike2); + + l_oBike2 = new Bike(43, null, null); + Assert.IsTrue(l_oBike1 == l_oBike2); + } + } +} diff --git a/TestShareeLib/Model/Bike/TestBikeCollection.cs b/TestShareeLib/Model/Bike/TestBikeCollection.cs new file mode 100644 index 0000000..8fb709f --- /dev/null +++ b/TestShareeLib/Model/Bike/TestBikeCollection.cs @@ -0,0 +1,22 @@ +using NUnit.Framework; +using System; +using TINK.Model.Bike; + + +namespace TestTINKLib +{ + + [TestFixture] + public class TestBikeCollection + { + /// Tests the member. + [Test] + public void TestConstruct() + { + var l_oColl = new BikeCollection(); + + Assert.AreEqual(0, l_oColl.Count); + Assert.IsNull(l_oColl.GetById(1)); + } + } +} diff --git a/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs b/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs new file mode 100644 index 0000000..eb5a663 --- /dev/null +++ b/TestShareeLib/Model/Bike/TestBikeCollectionMutable.cs @@ -0,0 +1,109 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using TINK.Model.Bike; +using TINK.Model.Bikes.Bike; +using TINK.Model.State; + +using BikeInfo = TINK.Model.Bike.BC.BikeInfo; +using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; + +namespace TestTINKLib +{ + + [TestFixture] + public class TestBikeCollectionMutable + { + private class BikeInfoMutable : TINK.Model.Bike.BC.BikeInfoMutable + { + public BikeInfoMutable( + int id, + bool isDemo = false, + IEnumerable group = null, + WheelType? wheelType = null, + TypeOfBike? typeOfBike = null, + string description = null, + int? currentStationId = null, + Uri operatorUri = null, + TariffDescription tariffDescription = null, + Func dateTimeProvider = null, + IStateInfo stateInfo = null) : base(id, isDemo, group, wheelType, typeOfBike, description, currentStationId, operatorUri, tariffDescription, dateTimeProvider, stateInfo) + { + } + } + + /// Tests the member. + [Test] + public void TestAdd() + { + var l_oColl = new BikeCollectionMutable(); + + l_oColl.Add(new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround)); + + Assert.Throws(() => l_oColl.Add(new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo))); + } + + [Test] + public void TestUpdate_Null() + { + var l_oBikeRequested = new BikeInfoMutable(20, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); + l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); + + var l_oBikeColl = new BikeCollectionMutable + { + new BikeInfoMutable(63, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), + new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), + l_oBikeRequested, + }; + + // Verify initial state + Assert.NotNull(l_oBikeColl.GetById(63)); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById(57).State.Value); + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById(20).State.Value); + Assert.Null(l_oBikeColl.GetById(33)); + + l_oBikeColl.Update(null); + + // Verify modified state + Assert.Null(l_oBikeColl.GetById(63)); + Assert.Null(l_oBikeColl.GetById(57)); + Assert.Null(l_oBikeColl.GetById(20)); + Assert.Null(l_oBikeColl.GetById(33)); + } + + [Test] + public void TestUpdate() + { + var l_oBikeRequested = new BikeInfoMutable(20, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Allround); + l_oBikeRequested.State.Load(new StateInfo(() => DateTime.Now, DateTime.Now, "john@long", "1234")); + + var l_oBikeColl = new BikeCollectionMutable + { + new BikeInfoMutable(63, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround), + new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Trike, TypeOfBike.Cargo), + l_oBikeRequested, + }; + + // Verify initial state + Assert.NotNull(l_oBikeColl.GetById(63)); // Will be removed + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeColl.GetById(57).State.Value); // Will be requested + Assert.AreEqual(InUseStateEnum.Reserved, l_oBikeColl.GetById(20).State.Value); // Will be booked + Assert.Null(l_oBikeColl.GetById(33)); // + + var l_oBikeResponse = new List + { + new BikeInfo(57, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 7, null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), + new BikeInfo(20, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround, "Test description", 7, null /*operator uri*/, null, DateTime.Now, "john@long,", "1234"), + new BikeInfo(33, 7, null /*operator uri*/, null, false, new List {"TINK" }, WheelType.Trike, TypeOfBike.Allround), + }; + + l_oBikeColl.Update(l_oBikeResponse); + + // Verify modified state + Assert.Null(l_oBikeColl.GetById(63)); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById(57).State.Value); + Assert.AreEqual(InUseStateEnum.Booked, l_oBikeColl.GetById(20).State.Value); + Assert.NotNull(l_oBikeColl.GetById(33)); + } + } +} diff --git a/TestShareeLib/Model/Bike/TestBikeCollectionSerializeJSON.cs b/TestShareeLib/Model/Bike/TestBikeCollectionSerializeJSON.cs new file mode 100644 index 0000000..99d797d --- /dev/null +++ b/TestShareeLib/Model/Bike/TestBikeCollectionSerializeJSON.cs @@ -0,0 +1,67 @@ +using NUnit.Framework; +using TINK.Model.Bike; + +using Newtonsoft.Json; +using TINK.Model.State; +using System.Collections.Generic; + +using BikeInfoMutable = TINK.Model.Bike.BC.BikeInfoMutable; + +namespace TestTINKLib +{ + /// + /// Exclude from build beause serialization... see below. + /// + [TestFixture] + public class TestBikeCollectionSerializeJSON + { + [Test, Ignore("Disabled because serialization does no more work due to inheritance since commit ec14b93b.")] + public void Test_SerializeJSON() + { + var l_oCollSource = new BikeCollectionMutable + { + new BikeInfoMutable(57, false, new List { "TINK" }, WheelType.Two, TypeOfBike.Allround) + }; + + // Verify prerequisites. + Assert.AreEqual(1, l_oCollSource.Count); + Assert.AreEqual(57, l_oCollSource[0].Id); + Assert.AreEqual(InUseStateEnum.Disposable, l_oCollSource[0].State.Value); + Assert.IsNull(l_oCollSource[0].State.MailAddress); + Assert.IsNull(l_oCollSource[0].State.Code); + Assert.IsNull(l_oCollSource[0].State.From); + + // Serialize object and verify json + var l_oDetected = JsonConvert.SerializeObject(l_oCollSource, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); + const string EXPECTED = @" + [ + { + ""Id"": 57, + ""CurrentStation"": null, + ""WheelType"": 1, + ""TypeOfBike"": 0, + ""State"": { + ""StateInfoObject"": { + ""$type"": ""TINK.Model.State.StateAvailableInfo, TINKLib"" + } + } + } + ]"; + + Assert.AreEqual( + TestHelper.PrepareXmlForStringCompare(EXPECTED), + TestHelper.PrepareXmlForStringCompare(l_oDetected)); + + // Deserialize object. + var l_oBikeCollectionTarget = JsonConvert.DeserializeObject(l_oDetected, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); + + // Verify state. + Assert.AreEqual(1, l_oBikeCollectionTarget.Count); + Assert.AreEqual(57, l_oBikeCollectionTarget[0].Id); + Assert.AreEqual(InUseStateEnum.Disposable, l_oBikeCollectionTarget[0].State.Value); + Assert.IsNull(l_oBikeCollectionTarget[0].State.MailAddress); + Assert.IsNull(l_oBikeCollectionTarget[0].State.Code); + Assert.IsNull(l_oBikeCollectionTarget[0].State.From); + } + } +} diff --git a/TestShareeLib/Model/State/TestStateBookedInfoSerializeJSON.cs b/TestShareeLib/Model/State/TestStateBookedInfoSerializeJSON.cs new file mode 100644 index 0000000..1792a0a --- /dev/null +++ b/TestShareeLib/Model/State/TestStateBookedInfoSerializeJSON.cs @@ -0,0 +1,42 @@ +using Newtonsoft.Json; +using NUnit.Framework; +using System; +using TINK.Model.State; + + +namespace TestTINKLib.Fixtures.Bike +{ + + [TestFixture] + public class TestStateBookedInfoSerializeJSON + { + [Test] + public void TestSerializeJSON() + { + // Create object to test. + var l_oInfoSource = new StateOccupiedInfo( + new DateTime(2017, 09, 20, 23, 05, 0), + "Heinz@mueller", + "17 xxb"); + + // Serialize object and verify. + var l_oDetected = JsonConvert.SerializeObject(l_oInfoSource); + const string EXPECTED = @" + { + ""From"":""2017 - 09 - 20T23: 05:00"", + ""MailAddress"":""Heinz@mueller"", + ""Code"":""17 xxb"" + }"; + Assert.AreEqual( + TestHelper.PrepareXmlForStringCompare(EXPECTED.Replace("\n", string.Empty).Replace("\r", string.Empty)), + TestHelper.PrepareXmlForStringCompare(l_oDetected.Replace("\n", string.Empty).Replace("\r", string.Empty))); + + // Deserialize object and verify. + var l_oInfoTarget = JsonConvert.DeserializeObject(l_oDetected); + Assert.AreEqual(InUseStateEnum.Booked, l_oInfoTarget.Value); + Assert.AreEqual("Heinz@mueller", l_oInfoTarget.MailAddress); + Assert.AreEqual("17 xxb", l_oInfoTarget.Code); + Assert.AreEqual(new DateTime(2017, 9, 20, 23, 5, 0), l_oInfoTarget.From); + } + } +} diff --git a/TestShareeLib/Model/TestBikeCollectionFilter.cs b/TestShareeLib/Model/TestBikeCollectionFilter.cs new file mode 100644 index 0000000..d650664 --- /dev/null +++ b/TestShareeLib/Model/TestBikeCollectionFilter.cs @@ -0,0 +1,86 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using TINK.Model; +using TINK.Model.Bike; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike +{ + [TestFixture] + public class TestBikeCollectionFilter + { + [Test] + public void TestGetAtStation() + { + var coll = new BikeCollection( + new Dictionary + { + {3, new TINK.Model.Bike.BC.BikeInfo(7, 3 /* Stadion id */) }, + {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, + {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + }); + + Assert.AreEqual( + 0, + BikeCollectionFilter.GetAtStation(null, 12).Count); + + Assert.AreEqual( + 0, + coll.GetAtStation(null).Count); + + Assert.AreEqual( + 0, + coll.GetAtStation(22).Count); + + Assert.AreEqual( + 1, + coll.GetAtStation(3).Count); + + Assert.AreEqual( + 2, + coll.GetAtStation(12).Count); + } + + [Test] + public void TestGetLockIt_Null() + { + Assert.That( + BikeCollectionFilter.GetLockIt(null).Count, + Is.EqualTo(0)); + } + + [Test] + public void TestGetLockIt_NoLockIt() + { + // Holds no LockIt bikes + var coll = new BikeCollection( + new Dictionary + { + {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, + {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + }); + + Assert.AreEqual( + 0, + coll.GetLockIt().Count); + } + + [Test] + public void TestGetLockIt() + { + + // Holds no LockIt bike with matching station number. + var coll = new BikeCollection( + new Dictionary + { + {7, new TINK.Model.Bike.BC.BikeInfo(8, 12 /* Stadion id */) }, + {11, new TINK.Model.Bike.BluetoothLock.BikeInfo(33, 5200544, new Guid("00000000-0000-0000-0000-000000000001"),12 /* Stadion id */) }, + {12, new TINK.Model.Bike.BC.BikeInfo(33, 12 /* Stadion id */) } + }); + + Assert.AreEqual( + 1, + coll.GetLockIt().Count); + } + } +} diff --git a/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs b/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs new file mode 100644 index 0000000..e912a20 --- /dev/null +++ b/TestShareeLib/Repository/Response/TestBikesAvailableResponse.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; +using NUnit.Framework; +using TINK.Model.Repository.Response; + +namespace TestTINKLib.Fixtures.ObjectTests.Bike +{ + [TestFixture] + public class TestBikesAvailableResponse + { + [Test] + public void TestDeserializeStationEmpty() + { + // Response for bike 231 is a real world answer. + var l_oBikes = JsonConvert.DeserializeObject(@" + { + ""apiserver"" : ""https://app.tink-konstanz.de"", + ""response"" : ""bikes_available"", + ""bikes"" : { + ""231"" : { + ""bike"" : ""231"", + ""description"" : ""Stadtrad"", + ""system"" : ""BC"", + ""bike_group"" : ""Konrad"", + ""station"" : """", + ""state"" : ""available"", + ""gps"" : ""9.1594501, 47.6749928"" + } + }, + ""copri_version"" : ""3.0.0.0"", + ""authcookie"" : """", + ""response_state"" : ""OK"" + } + "); + + Assert.IsNull(l_oBikes.bikes[231].station); + } + + [Test] + public void TestDeserializeStationInfoMissing() + { + // Response for bike 231 might not be real world answer. + var l_oBikes = JsonConvert.DeserializeObject(@" + { + ""apiserver"" : ""https://app.tink-konstanz.de"", + ""response"" : ""bikes_available"", + ""bikes"" : { + ""231"" : { + ""bike"" : ""231"", + ""description"" : ""Stadtrad"", + ""system"" : ""BC"", + ""bike_group"" : ""Konrad"", + ""state"" : ""available"", + ""gps"" : ""9.1594501, 47.6749928"" + } + }, + ""copri_version"" : ""3.0.0.0"", + ""authcookie"" : """", + ""response_state"" : ""OK"" + } + "); + + Assert.IsNull(l_oBikes.bikes[231].station); + + } + } +} diff --git a/TestShareeLib/Repository/Response/TestJsonConvert.cs b/TestShareeLib/Repository/Response/TestJsonConvert.cs new file mode 100644 index 0000000..b3606c4 --- /dev/null +++ b/TestShareeLib/Repository/Response/TestJsonConvert.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using TINK.Repository.Exception; +using TINK.Repository.Response; + +namespace TestShareeLib.Model.Repository.Response +{ + [TestFixture] + public class TestJsonConvert + { + [Test] + public void TestDeserializeObject() + { + Assert.That( + () => JsonConvert.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.00"", + ""abo_eur_per_month"" : ""920.00"", + ""free_hours"" : ""3.00"", + ""number"" : ""5494"", +-- this is not JSON ! + ""name"" : ""Tester Basic"" + }"), + Throws.InstanceOf()); + } + } +} diff --git a/TestShareeLib/Repository/Response/TestTariffDescription.cs b/TestShareeLib/Repository/Response/TestTariffDescription.cs new file mode 100644 index 0000000..6e80a7b --- /dev/null +++ b/TestShareeLib/Repository/Response/TestTariffDescription.cs @@ -0,0 +1,97 @@ +using NUnit.Framework; +using TINK.Repository.Response; + +namespace TestShareeLib.Repository.Response +{ + [TestFixture] + public class TestTariffDescription + { + [Test] + public void TestDeserialize() + { + var tariffDescription = JsonConvert.DeserializeObject( + @"{ + ""eur_per_hour"" : ""10.00"", + ""abo_eur_per_month"" : ""920.00"", + ""free_hours"" : ""3.00"", + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.eur_per_hour, + Is.EqualTo("10.00")); + + Assert.That( + tariffDescription.abo_eur_per_month, + Is.EqualTo("920.00")); + + Assert.That( + tariffDescription.free_hours, + Is.EqualTo("3.00")); + + Assert.That( + tariffDescription.number, + Is.EqualTo("5494")); + + Assert.That( + tariffDescription.name, + Is.EqualTo("Tester Basic")); + } + + /// + /// Verifies that missing elements are initialized correctly. + /// + [Test] + public void TestDeserialize_Missing() + { + var tariffDescription = JsonConvert.DeserializeObject( + @"{ + ""number"" : ""5494"", + ""name"" : ""Tester Basic"" + }"); + + Assert.That( + tariffDescription.eur_per_hour, + Is.Null); + + Assert.That( + tariffDescription.abo_eur_per_month, + Is.Null); + + Assert.That( + tariffDescription.free_hours, + Is.Null); + + Assert.That( + tariffDescription.number, + Is.EqualTo("5494")); + + Assert.That( + tariffDescription.name, + Is.EqualTo("Tester Basic")); + } + + /// + /// Verifies that unknown elemts in JSON to not lead to firing of exceptions. + /// + [Test] + public void TestDeserialize_NewElement() + { + var tariffDescription = JsonConvert.DeserializeObject( + @"{ + ""number"" : ""5494"", + ""name"" : ""Tester Basic"", + ""FancyNewElement"" : ""Yeah"" + }"); + + Assert.That( + tariffDescription.number, + Is.EqualTo("5494")); + + Assert.That( + tariffDescription.name, + Is.EqualTo("Tester Basic")); + } + } +} diff --git a/TestShareeLib/Services/TestServicesContainerMutable.cs b/TestShareeLib/Services/TestServicesContainerMutable.cs new file mode 100644 index 0000000..28f8654 --- /dev/null +++ b/TestShareeLib/Services/TestServicesContainerMutable.cs @@ -0,0 +1,68 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using TINK.Services; + +namespace TestShareeLib.Services +{ + [TestFixture] + public class TestServicesContainerMutable + { + [Test] + public void TestCtor() + { + var typeA = new MyTypeA(); + var typeB = new MyTypeB(); + + var serviceContainer = new ServicesContainerMutable( + new List { typeA, typeB, typeB }, + typeA.GetType().FullName); + + Assert.That( + serviceContainer, + Is.EqualTo(new List { typeA, typeB}).AsCollection); + + Assert.That( + serviceContainer.Active, + Is.EqualTo(typeA)); + } + + [Test] + public void TestSetActive() + { + var typeA = new MyTypeA(); + var typeB = new MyTypeB(); + + var serviceContainer = new ServicesContainerMutable( + new List { typeA, typeB }, + typeA.GetType().FullName); + + serviceContainer.SetActive(typeB.GetType().FullName); + + Assert.That( + serviceContainer.Active, + Is.EqualTo(typeB)); + } + + [Test] + public void TestSetActiveNotExists() + { + var typeA = new MyTypeA(); + var typeB = new MyTypeB(); + + var serviceContainer = new ServicesContainerMutable( + new List { typeA, typeB }, + typeA.GetType().FullName); + + Assert.That( + () => serviceContainer.SetActive(new MyTypeC().GetType().FullName), + Throws.TypeOf()); + } + + private class MyTypeA {} + + private class MyTypeB { } + + private class MyTypeC { } + } +} diff --git a/TestShareeLib/TestHelper.cs b/TestShareeLib/TestHelper.cs new file mode 100644 index 0000000..a119d2f --- /dev/null +++ b/TestShareeLib/TestHelper.cs @@ -0,0 +1,18 @@ +namespace TestTINKLib +{ + /// + /// Provides helper functionality. Used in TestShareeLib and TestTINKLib. + /// + public static class TestHelper + { + /// + /// Eases comparison of xml- files. + /// + /// + /// + public static string PrepareXmlForStringCompare(string p_strXmlFile) + { + return p_strXmlFile.Replace("\r\n", string.Empty).Replace("\t", string.Empty).Replace(" ", string.Empty); + } + } +} diff --git a/TestShareeLib/TestShareeLib.csproj b/TestShareeLib/TestShareeLib.csproj new file mode 100644 index 0000000..cfca1d6 --- /dev/null +++ b/TestShareeLib/TestShareeLib.csproj @@ -0,0 +1,43 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TestShareeLib/TestShareeLib.sln b/TestShareeLib/TestShareeLib.sln new file mode 100644 index 0000000..2a43820 --- /dev/null +++ b/TestShareeLib/TestShareeLib.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestShareeLib", "TestShareeLib.csproj", "{FE15D644-F638-48B8-AD63-A3A9D01A8033}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TINKLib", "..\TINKLib\TINKLib.csproj", "{F3C0288B-7527-42B0-AA43-CC461766D6E1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FE15D644-F638-48B8-AD63-A3A9D01A8033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE15D644-F638-48B8-AD63-A3A9D01A8033}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE15D644-F638-48B8-AD63-A3A9D01A8033}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE15D644-F638-48B8-AD63-A3A9D01A8033}.Release|Any CPU.Build.0 = Release|Any CPU + {F3C0288B-7527-42B0-AA43-CC461766D6E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3C0288B-7527-42B0-AA43-CC461766D6E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3C0288B-7527-42B0-AA43-CC461766D6E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3C0288B-7527-42B0-AA43-CC461766D6E1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C3C5F7B8-9710-4F33-9FD9-D7DE9DE9ED29} + EndGlobalSection +EndGlobal diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs new file mode 100644 index 0000000..96edea6 --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestBookedUnknown.cs @@ -0,0 +1,963 @@ +using NSubstitute; +using NUnit.Framework; +using System; +using System.Threading.Tasks; +using TINK.Model.Bike.BluetoothLock; +using TINK.Model.Bikes.Bike.BluetoothLock; +using TINK.Model.Connector; +using TINK.Model.Repository.Exception; +using TINK.Services.BluetoothLock; +using TINK.Services.BluetoothLock.Exception; +using TINK.Services.BluetoothLock.Tdo; +using TINK.Model.Services.Geolocation; +using TINK.Model.State; +using TINK.View; +using TINK.ViewModel; +using TINK.ViewModel.Bikes; +using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; +using TINK.Model.User; +using TINK.Model.Repository.Request; +using TINK.Model.Repository.Response; +using TINK.Repository.Exception; +using Newtonsoft.Json; + +namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler +{ + [TestFixture] + public class TestBookedUnknown + { + + /// + /// Test construction of object. + /// + [Test] + public void Testctor() + { + var handler = new BookedUnknown( + Substitute.For(), + () => true, // isConnectedDelegate + (isConnexted) => Substitute.For(), + Substitute.For(), + Substitute.For(), + () => Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For()); + + // Verify prerequisites. + Assert.AreEqual("Open lock & continue renting", handler.ButtonText); + Assert.IsTrue(handler.IsButtonVisible); + Assert.AreEqual("Close lock", handler.LockitButtonText); + Assert.IsTrue(handler.IsLockitButtonVisible); + } + + /// + /// Use case: Opens lock + /// Final state: Booked opened. + /// + [Test] + public void TestOpen() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new OutOfReachException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenBoldBlockedException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenBoldBlockedException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Unknown" after action + Assert.AreEqual("Open lock & continue renting", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenInconsistentStateExecptionClosed() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Closed)); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "After try to open lock state closed is reported.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenInconsistentStateExecption() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Unknown)); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock reports unknown bold position.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual("Open lock & continue renting", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, null).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "No web error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Connection error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Status error on updating lock state."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Booked closed. + /// + [Test] + public void TestClose() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Same as initial state. + /// + [Test] + public void TestCloseCloseFailsOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns>(x => throw new OutOfReachException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be closed!", "Lock cannot be closed until bike is near.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked disconnected" after action + Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Same as initial state. + /// + [Test] + public void TestCloseCloseFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns>(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be closed!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "No web error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Connection error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new BookedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Status error on updating lock state."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + } +} + diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs new file mode 100644 index 0000000..449f23f --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedClosed.cs @@ -0,0 +1,1127 @@ +using Newtonsoft.Json; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Threading.Tasks; +using TINK.Model.Bike.BluetoothLock; +using TINK.Model.Bikes.Bike.BluetoothLock; +using TINK.Model.Connector; +using TINK.Model.Repository.Exception; +using TINK.Model.Repository.Response; +using TINK.Services.BluetoothLock; +using TINK.Services.BluetoothLock.Exception; +using TINK.Services.BluetoothLock.Tdo; +using TINK.Model.Services.Geolocation; +using TINK.Model.State; +using TINK.View; +using TINK.ViewModel; +using TINK.ViewModel.Bikes; +using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; +using TINK.Model.User; +using TINK.Model.Repository.Request; +using TINK.Repository.Exception; + +namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler +{ + [TestFixture] + public class TestReservedClosed + { + /// + /// Test construction of object. + /// + [Test] + public void Testctor() + { + var handler = new ReservedClosed( + Substitute.For(), + () => true, // isConnectedDelegate + (isConnexted) => Substitute.For(), + Substitute.For(), + Substitute.For(), + () => Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For()); + + // Verify prerequisites. + Assert.AreEqual("Cancel bike reservation", handler.ButtonText); + Assert.IsTrue(handler.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", handler.LockitButtonText); + Assert.IsTrue(handler.IsLockitButtonVisible); + } + + /// + /// Use case: Cancel reservation. + /// Comment: User deceide to abort cancelling (user is ased whether to really cancel) + /// Final state: Same as initial state. + /// + [Test] + public void TestCancelReservationCancel() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false)); + + var subsequent = handler.HandleRequestOption1().Result; + + locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behaviour + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No"); + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Cancel reservation. + /// Final state: Disposable. + /// + [Test] + public void TestCancelReservation() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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); // Reqesthandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Disconnected); // Requsthandler factory queries lock state to create appropriate request handler object. + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Canceling reservation..."; + connector.Command.DoCancelReservation(bike); // Booking must be performed + bikesViewModel.ActionText = "Disconnecting lock..."; + locks.DisconnectAsync(Arg.Any(), Arg.Any()); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + 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); + } + + /// + /// Use case: Cancel reservation. + /// Comment: Exception is thrown. + /// Final state: Same as initial state. + /// + [Test] + public void TestCancelReservationDoCancelReservationInvalidAuthorizationResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + var response = JsonConvert.DeserializeObject(@" + { + ""response"" : ""authorization"", + ""authcookie"" : ""4da3044c8657a04ba60e2eaa753bc51a"", + ""user_group"" : ""TINK,Konrad"", + ""response_state"" : ""OK"", + ""apiserver"" : ""https://tinkwwp.copri-bike.de"" + }"); + + connector.Command.DoCancelReservation(bike).Returns(x => throw new InvalidAuthorizationResponseException("mustermann@server.de", response)); + bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. + + var subsequent = handler.HandleRequestOption1().Result; + + locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behaviour + 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 = "Canceling reservation..."; + connector.Command.DoCancelReservation(bike); // Booking must be performed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", "Kann Benutzer mustermann@server.de nicht anmelden. Mailadresse unbekannt oder Passwort ungültig.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Cancel reservation. + /// Comment: Canceling reservation fails. + /// Final state: Same as initial state. + /// + [Test] + public void TestCancelReservationDoCancelReservationWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + connector.Command.DoCancelReservation(bike).Returns(x => throw new WebConnectFailureException("Context info.", new Exception("chub"))); + bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. + + var subsequent = handler.HandleRequestOption1().Result; + + locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behaviour + 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 = "Canceling reservation..."; + connector.Command.DoCancelReservation(bike); // Booking must be performed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Verbingungsfehler beim Aufheben der Reservierung!", "Context info.\r\nIst WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Cancel reservation. + /// Comment: Canceling reservation fails. + /// Final state: Same as initial state. + /// + [Test] + public void TestCancelReservationDoCancelReservationException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Cancel reservation for bike {0}?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + connector.Command.DoCancelReservation(bike).Returns(x => throw new Exception("Exception message.")); + bike.State.Value.Returns(InUseStateEnum.Reserved); // Reqesthandler factory queries state to create appropriate request handler object. + bike.LockInfo.State.Returns(LockingState.Closed); // Requsthandler factory queries lock state to create appropriate request handler object. + + var subsequent = handler.HandleRequestOption1().Result; + + locks.DidNotReceive().DisconnectAsync(Arg.Any(), Arg.Any()); + + // Verify behaviour + 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 = "Canceling reservation..."; + connector.Command.DoCancelReservation(bike); // Booking must be performed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Fehler beim Aufheben der Reservierung!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: user cancels operation. + /// Final state: Reserved and closed. + /// + [Test] + public void TestBookAndOpenCancelBook() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(false)); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + Received.InOrder(() => + { + bikesViewModel.Received(1).IsIdle = false; // GUI must be locked + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpen() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: COPRI request to book bike fails (WebConnectFailureException). Lock is opened and closed again. + /// Final state: Reserved closed. + /// + [Test] + public void TestBookAndOpenBookingFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. + + connector.Command.DoBook(bike).Returns(x => throw new WebConnectFailureException("Context info", new Exception("Tst"))); + + bike.State.Value.Returns(InUseStateEnum.Reserved); // State remains reserved because booking request failed. + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = ""; + viewService.DisplayAdvancedAlert( + "Connection error when renting the bike!", + "Ist WLAN verfügbar/ Mobilfunknetz vefügbar und mobile Daten aktiviert / ... ?", + "Context info", + "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "reserved closed" after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: COPRI request to book bike fails (Exception). Lock is opened and closed again. + /// Final state: Reserved closed. + /// + [Test] + public void TestBookAndOpenBookingFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + bike.LockInfo.State.Returns(LockingState.Closed); // Locking state does not change. + + connector.Command.DoBook(bike).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Reserved); // When booking fails state remains reserved. + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = ""; + viewService.DisplayAdvancedAlert( + "Error when renting the bike!", + "", + "Exception message.", + "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "reserved closed" after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & rent bike", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike, + /// Comment: Unlocking bike throws an OutOfReach-Exception. + /// Final state: Reserved unknown. + /// + [Test] + public void TestBookAndOpenOpenFailsExceptionOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + locks[0].OpenAsync() + .Returns>(x => { throw new OutOfReachException(); }); + + bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. + bike.LockInfo.State.Returns(LockingState.Unknown); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Unlocking bike throws an exception. + /// Final state: Reserved unknown. + /// + [Test] + public void TestBookAndOpenOpenFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + viewService.DisplayAlert(string.Empty, string.Format("Rent bike {0} and open lock?", "Nr. 0"), "Yes", "No").Returns(Task.FromResult(true)); + + locks[0].OpenAsync() + .Returns>(x => throw new Exception("Exception message.")); // Return lock state indicating success + + bike.State.Value.Returns(TINK.Model.State.InUseStateEnum.Reserved); // Booking call leads to setting of state to booked. + bike.LockInfo.State.Returns(LockingState.Unknown); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Unlocking bike does not return state bike opened. + /// Final state: Reserved unknown. + /// + [Test] + public void TestBookAndOpenOpenFailsNotOpen() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)null)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Reserved); // Booking state does not change. + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Cancel bike reservation", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpenBatteryPercentageOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + locks[0].GetBatteryPercentageAsync().Returns>(x => throw new OutOfReachException()); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Battery status can only be read when bike is nearby."; + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpenBatteryPercentageException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + locks[0].GetBatteryPercentageAsync().Returns>(x => throw new Exception()); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Battery status cannot be read."; + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpenUpdateLockingStateFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + connector.Command.UpdateLockingStateAsync(bike, null).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "No web error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpenUpdateLockingStateFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Connection error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock and book bike. + /// Comment: Bike gets opened. + /// Final state: Booked open. + /// + [Test] + public void TestBookAndOpenUpdateLockingStateFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedClosed( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + 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.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); // Booking call leads to setting of state to booked. + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Renting bike..."; + connector.Command.DoBook(bike); // Booking must be performed + bikesViewModel.ActionText = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be opened + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Status error on updating lock state."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + } +} diff --git a/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs new file mode 100644 index 0000000..d174a43 --- /dev/null +++ b/TestShareeLib/ViewModel/Bikes/Bike/BluetoothLock/RequestHandler/TestReservedUnknown.cs @@ -0,0 +1,961 @@ +using NSubstitute; +using NUnit.Framework; +using System; +using System.Threading.Tasks; +using TINK.Model.Bike.BluetoothLock; +using TINK.Model.Bikes.Bike.BluetoothLock; +using TINK.Model.Connector; +using TINK.Model.Repository.Exception; +using TINK.Services.BluetoothLock; +using TINK.Services.BluetoothLock.Exception; +using TINK.Services.BluetoothLock.Tdo; +using TINK.Model.Services.Geolocation; +using TINK.Model.State; +using TINK.View; +using TINK.ViewModel; +using TINK.ViewModel.Bikes; +using TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler; +using TINK.Model.User; +using TINK.Model.Repository.Request; +using TINK.Model.Repository.Response; +using TINK.Repository.Exception; +using Newtonsoft.Json; + +namespace TestShareeLib.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler +{ + public class TestReservedUnknown + { + [TestFixture] + public class TestBookedUnknown + { + + /// + /// Test construction of object. + /// + [Test] + public void Testctor() + { + var handler = new ReservedUnknown( + Substitute.For(), + () => true, // isConnectedDelegate + (isConnexted) => Substitute.For(), + Substitute.For(), + Substitute.For(), + () => Substitute.For(), + Substitute.For(), + Substitute.For(), + Substitute.For()); + + // Verify prerequisites. + Assert.AreEqual("Open lock & rent bike", handler.ButtonText); + Assert.IsFalse(handler.IsButtonVisible); + Assert.AreEqual("Close lock", handler.LockitButtonText); + Assert.IsTrue(handler.IsLockitButtonVisible); + } + + /// + /// Use case: Opens lock + /// Final state: Booked opened. + /// + [Test] + public void TestOpen() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Open)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new OutOfReachException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock cannot be opened until bike is near.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenBoldBlockedException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenBoldBlockedException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock is blocked. Please ensure that no obstacle prevents lock from opening and try again.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Unknown" after action + Assert.AreEqual("Open lock & continue renting", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenInconsistentStateExecptionClosed() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Closed)); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "After try to open lock state closed is reported.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsCouldntOpenInconsistentStateExecption() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new CouldntOpenInconsistentStateExecption(LockingState.Unknown)); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Lock reports unknown bold position.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual("Open lock & continue renting", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Same as initial state. + /// + [Test] + public void TestOpenOpenFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns>(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Closed); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be opened!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual(nameof(BookedDisconnected), subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, null).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, null); + bikesViewModel.ActionText = "No web error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Connection error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Open lock + /// Final state: Booked open. + /// + [Test] + public void TestOpenUpdateLockingStateFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].OpenAsync() + .Returns(LockitLockingState.Open); + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption1().Result; + + // Verify behaviour + 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 = "Opening lock..."; + locks.Received()[0].OpenAsync(); // Lock must be closed + bikesViewModel.ActionText = "Reading charging level..."; + locks[bike.Id].GetBatteryPercentageAsync(); + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Status error on updating lock state."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Close lock & return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Close lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Booked closed. + /// + [Test] + public void TestClose() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(Task.FromResult((LockitLockingState?)LockitLockingState.Closed)); // Return lock state indicating success + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + [Test] + public void TestCloseCloseFailsOutOfReachException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns>(x => throw new OutOfReachException()); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be closed!", "Lock cannot be closed until bike is near.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked disconnected" after action + Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: Close lock + /// Final state: Same as initial state. + /// + [Test] + public void TestCloseCloseFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns>(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + bike.LockInfo.State.Returns(LockingState.Open); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = ""; + viewService.DisplayAlert("Lock can not be closed!", "Exception message.", "OK"); + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Disconnected" after action + Assert.AreEqual("BookedDisconnected", subsequent.ButtonText); + Assert.IsFalse(subsequent.IsButtonVisible); + Assert.AreEqual("Search lock", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + } + + /// + /// Use case: Close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsWebConnectFailureException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new WebConnectFailureException("Context info", new System.Exception("hoppla"))); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "No web error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => throw new Exception("Exception message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Connection error on updating locking status."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + + /// + /// Use case: close lock + /// Final state: Booked closed. + /// + [Test] + public void TestCloseUpdateLockingStateFailsResponseException() + { + var bike = Substitute.For(); + var connector = Substitute.For(); + var command = Substitute.For(); + var geolocation = Substitute.For(); + var locks = Substitute.For(); + var pollingManager = Substitute.For(); + var viewService = Substitute.For(); + var bikesViewModel = Substitute.For(); + var activeUser = Substitute.For(); + + var handler = new ReservedUnknown( + bike, + () => true, // isConnectedDelegate + (isConnexted) => connector, + geolocation, + locks, + () => pollingManager, + viewService, + bikesViewModel, + activeUser); + + locks[0].CloseAsync() + .Returns(LockitLockingState.Closed); // Return lock state indicating success + + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()).Returns(x => + throw new ReturnBikeException(JsonConvert.DeserializeObject(@"{ ""response_text"" : ""Some invalid data received!""}"), "Outer message.")); + + bike.State.Value.Returns(InUseStateEnum.Booked); + + var subsequent = handler.HandleRequestOption2().Result; + + // Verify behaviour + 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 = "Closing lock..."; + locks.Received()[0].CloseAsync(); // Lock must be closed + bikesViewModel.ActionText = "Updating lock state..."; + connector.Command.UpdateLockingStateAsync(bike, Arg.Any()); + bikesViewModel.ActionText = "Status error on updating lock state."; + bikesViewModel.ActionText = "Updating..."; + pollingManager.StartUpdateAyncPeridically(); // polling must be restarted again + bikesViewModel.ActionText = ""; + bikesViewModel.Received(1).IsIdle = true; // GUI must be unlocked + }); + + // Verify state "Booked Closed" after action + Assert.AreEqual("Return bike", subsequent.ButtonText); + Assert.IsTrue(subsequent.IsButtonVisible); + Assert.AreEqual("Open lock & continue renting", subsequent.LockitButtonText); + Assert.IsTrue(subsequent.IsLockitButtonVisible); + } + } +} diff --git a/TestShareeLib/ViewModel/Settings/TestServicesViewModel.cs b/TestShareeLib/ViewModel/Settings/TestServicesViewModel.cs new file mode 100644 index 0000000..884a7ae --- /dev/null +++ b/TestShareeLib/ViewModel/Settings/TestServicesViewModel.cs @@ -0,0 +1,121 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace TINK.ViewModel.Settings +{ + [TestFixture] + public class TestServicesViewModel + { + [Test] + public void TestCtor() + { + var servicesViewModel = new ServicesViewModel( + new List { + "ServiceA", + "ServiceB", + "ServiceB", // Dupes are removed + "ServiceC" }, + new Dictionary { + { "ServiceA", "Top service of type A" }, + { "ServiceB", "Supertop service of type B" }, + { "OldsService", "This entry is discarded" }}, + "ServiceB"); + + Assert.That( + servicesViewModel.ServicesTextList, + Is.EqualTo(new List { + "ServiceC", + "Supertop service of type B", + "Top service of type A" }).AsCollection); + + Assert.That( + servicesViewModel.Active, + Is.EqualTo("ServiceB")); + } + + [Test] + public void TestCtorInvalidActive() + { + Assert.That( + () => new ServicesViewModel( + new List { "ServiceA", "ServiceB", "ServiceB", "ServiceC" }, + new Dictionary { { "ServiceA", "Top service of type A" }, { "ServiceB", "Supertop service of type B" } }, + "ServiceX"), + Throws.TypeOf()); + } + + [Test] + public void TestCtorInvalidServiceNames() + { + Assert.That( + () => new ServicesViewModel( + new List { "ServiceA", "ServiceB", "ServiceC" }, + new Dictionary { { "ServiceA", "Top fast" }, { "ServiceB", "Top fast" } }, + "ServiceB"), + Throws.TypeOf()); + } + + [Test] + public void TestGetActiveTextDisplayTextAvailable() + { + var servicesViewModel = new ServicesViewModel( + new List { + "ServiceB"}, + new Dictionary { + { "ServiceB", "Supertop service of type B" }}, + "ServiceB"); + + Assert.That( + servicesViewModel.ActiveText, + Is.EqualTo("Supertop service of type B")); + } + + [Test] + public void TestGetActiveTextNoDisplayText() + { + var servicesViewModel = new ServicesViewModel( + new List { + "ServiceB"}, + new Dictionary(), + "ServiceB"); + + Assert.That( + servicesViewModel.ActiveText, + Is.EqualTo("ServiceB")); + } + + [Test] + public void TestSetActiveText() + { + var servicesViewModel = new ServicesViewModel( + new List { + "ServiceA", + "ServiceB" }, + new Dictionary(), + "ServiceB") + { + ActiveText = "ServiceA" + }; + + Assert.That( + servicesViewModel.ActiveText, + Is.EqualTo("ServiceA")); + } + + [Test] + public void TestSetActiveTextInvalid() + { + var servicesViewModel = new ServicesViewModel( + new List { + "ServiceA", + "ServiceB" }, + new Dictionary(), + "ServiceB"); + + Assert.That( + () => servicesViewModel.ActiveText = "ServiceX", + Throws.InstanceOf()); + } + } +}