Tests fixed.

This commit is contained in:
Oliver Hauff 2021-11-08 23:11:56 +01:00
parent 4df8aa98aa
commit 8aa3089f32
15 changed files with 779 additions and 82 deletions

View file

@ -2,9 +2,7 @@
using System;
using TINK.Model;
using TINK.Model.Connector;
using TINK.Repository;
using TINK.Model.Services.CopriApi.ServerUris;
using static TINK.Repository.CopriCallsMemory;
using TINK.Services;
using NSubstitute;
using TINK.Model.Services.Geolocation;
@ -12,8 +10,8 @@ using TINK.Services.BluetoothLock;
using TINK.Model.Device;
using TINK.Model.User.Account;
using Plugin.Permissions.Abstractions;
using System.Threading.Tasks;
using System.Collections.Generic;
using TestShareeLib.Repository;
namespace TestTINKLib.Fixtures.UseCases.Logout
{
@ -32,9 +30,8 @@ namespace TestTINKLib.Fixtures.UseCases.Logout
accountStore.Load().Returns(account);
account.Mail.Returns("javaminister@gmail.com");
account.Pwd.Returns("javaminister");
account.SessionCookie.Returns("4da3044c8657a04ba60e2eaa753bc51a");
account.Group.Returns(new List<string> { "TINK" });
account.Pwd.Returns("*********");
account.SessionCookie.Returns("6103_112e96b36ba33de245943c5ffaf369cd_");
// No user logged in is initial state to verify.
var l_oTinkApp = new TinkApp(
@ -44,8 +41,8 @@ namespace TestTINKLib.Fixtures.UseCases.Logout
activeUri: new Uri(CopriServerUriList.TINK_DEVEL)),
accountStore,
(isConnected, uri, sessionCookie, mail, expiresAfter) => string.IsNullOrEmpty(sessionCookie)
? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1))
: new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1, sessionCookie)),
? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory001())
: new ConnectorCache(sessionCookie, mail, new CopriCallsMemory001(sessionCookie)),
Substitute.For<IServicesContainer<IGeolocation>>(),
locksService,
device,
@ -57,17 +54,29 @@ namespace TestTINKLib.Fixtures.UseCases.Logout
lastVersion: new Version(3, 0, 173)); // Current app version. Must be larger or equal 3.0.173 to
Assert.IsTrue(l_oTinkApp.ActiveUser.IsLoggedIn);
Assert.AreEqual(13, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count);
Assert.AreEqual(2, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count);
Assert.AreEqual("4da3044c8657a04ba60e2eaa753bc51a", l_oTinkApp.GetConnector(true).Command.SessionCookie);
// There are 6 bikes available and 2, one reserved and one rented by javaminsiter.
Assert.AreEqual(
8,
l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count,
"Sum of bikes is 6 occupied plus 2 occupied.");
Assert.AreEqual(2,
l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count,
"Javaminster occupies 2 bikes.");
Assert.AreEqual("6103_112e96b36ba33de245943c5ffaf369cd_", l_oTinkApp.GetConnector(true).Command.SessionCookie);
// Log user out.
l_oTinkApp.GetConnector(true).Command.DoLogout().Wait();
l_oTinkApp.ActiveUser.Logout();
Assert.IsFalse(l_oTinkApp.ActiveUser.IsLoggedIn);
Assert.AreEqual(11, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count);
Assert.AreEqual(0, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count);
Assert.AreEqual(
6,
l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count,
"Sum of bikes is 6 occupied, no one occupied because no user is logged in");
Assert.AreEqual(
0,
l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count,
"If no user is logged in no occupied bikes are shown.");
Assert.IsNull(l_oTinkApp.GetConnector(true).Command.SessionCookie);
}
}

View file

@ -13,10 +13,10 @@ using TINK.Model.Device;
using TINK.Model.User.Account;
using Plugin.Permissions.Abstractions;
using System.Threading.Tasks;
using TestShareeLib.Repository;
namespace TestTINKLib.Fixtures.UseCases.Login
{
[TestFixture]
public class TestTinkApp
{
@ -37,8 +37,8 @@ namespace TestTINKLib.Fixtures.UseCases.Login
activeUri: new Uri(CopriServerUriList.TINK_DEVEL)),
accountStore,
(isConnected, uri, sessionCookie, mail, expiresAfter) => string.IsNullOrEmpty(sessionCookie)
? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1)) as IConnector
: new ConnectorCache(sessionCookie, mail, new CopriCallsMemory(SampleSets.Set2, 1, sessionCookie)),
? new ConnectorCache(sessionCookie, mail, new CopriCallsMemory001()) as IConnector
: new ConnectorCache(sessionCookie, mail, new CopriCallsMemory001(sessionCookie)),
Substitute.For<IServicesContainer<IGeolocation>>(),
locksService,
device,
@ -50,18 +50,30 @@ namespace TestTINKLib.Fixtures.UseCases.Login
lastVersion: new Version(3, 0, 173) /* Current app version. Must be larger or equal 3.0.173 to lastVersion*/);
Assert.IsFalse(l_oTinkApp.ActiveUser.IsLoggedIn);
Assert.AreEqual(11, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count);
Assert.AreEqual(0, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count);
Assert.AreEqual(
6,
l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count,
"Sum of bikes is 6 occupied, no one occupied because no user is logged in");
Assert.AreEqual(
0,
l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count,
"If no user is logged in no occupied bikes are shown.");
Assert.IsNull(l_oTinkApp.GetConnector(true).Command.SessionCookie);
// Log user out.
var l_oAccount = l_oTinkApp.GetConnector(true).Command.DoLogin("javaminister@gmail.com", "javaminister", "HwId1000000000000").Result;
var l_oAccount = l_oTinkApp.GetConnector(true).Command.DoLogin("javaminister@gmail.com", "*********", "HwId1000000000000").Result;
await l_oTinkApp.ActiveUser.Login(l_oAccount);
Assert.IsTrue(l_oTinkApp.ActiveUser.IsLoggedIn);
Assert.AreEqual(13, l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count);
Assert.AreEqual(2, l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count);
Assert.AreEqual("4da3044c8657a04ba60e2eaa753bc51a", l_oTinkApp.GetConnector(true).Command.SessionCookie);
Assert.AreEqual(
8,
l_oTinkApp.GetConnector(true).Query.GetBikesAsync().Result.Response.Count,
"Sum of bikes is 6 occupied plus 2 occupied.");
Assert.AreEqual(
2,
l_oTinkApp.GetConnector(true).Query.GetBikesOccupiedAsync().Result.Response.Count,
"Javaminster occupies 2 bikes.");
Assert.AreEqual("6103_112e96b36ba33de245943c5ffaf369cd_", l_oTinkApp.GetConnector(true).Command.SessionCookie);
}
}
}

View file

@ -0,0 +1,283 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using TINK.Model;
using TINK.Model.Device;
using TINK.Repository;
using TINK.Repository.Request;
using TINK.Repository.Response;
namespace TestShareeLib.Repository
{
/// <summary> Provides functionality for keeping a set of COPRI responses. </summary>
public abstract class CopriCallMemoryBase
{
private string BikesAvailableResponse { get; }
private string BikesOccupiedResponse { get; }
private string AuthResponse { get; }
private string AuthOutResponse { get; }
private string Stations { get; }
private string BookingRequestResponse { get; }
private string CancelBookingRequestResponse { get; }
private IRequestBuilder requestBuilder;
public CopriCallMemoryBase(
string bikesAvailableResponse = null,
string bikesOccupiedResponse = null,
string authResponse = null,
string authOutResponse = null,
string stations = null,
string bookingRequestResponse = null,
string cancelBookingRequestResponse = null,
string sessionCookie = null)
{
SessionCookie = sessionCookie;
BikesAvailableResponse = bikesAvailableResponse;
BikesOccupiedResponse = bikesOccupiedResponse;
AuthResponse = authResponse;
AuthOutResponse = authOutResponse;
BookingRequestResponse = bookingRequestResponse;
CancelBookingRequestResponse = cancelBookingRequestResponse;
Stations = stations;
requestBuilder = string.IsNullOrEmpty(sessionCookie)
? new RequestBuilder(MerchantId) as IRequestBuilder
: new RequestBuilderLoggedIn(MerchantId, sessionCookie);
}
/// <summary> Holds the session id of the logged in user, null otherwise. </summary>
public string SessionCookie { get; private set; }
/// <summary> Logs user in. </summary>
/// <param name="p_oUser">User to log in.</param>
/// <param name="deviceId">Id specifying user and hardware.</param>
/// <param name="mailAddress">Mailaddress of user to log in.</param>
/// <param name="password">Password to log in.</param>
/// <remarks>Response which holds auth cookie <see cref="ResponseBase.authcookie"/></remarks>
public async Task<AuthorizationResponse> DoAuthorizationAsync(
string mailAddress,
string password,
string deviceId)
=> await Task.Run(() => DoAuthorize(AuthResponse, mailAddress, password, deviceId));
/// <summary> Logs user out. </summary>
/// <param name="sessionCookie">User to log in.</param>
/// <remarks>Response which holds auth cookie <see cref="ResponseBase.authcookie"/></remarks>
public async Task<AuthorizationoutResponse> DoAuthoutAsync()
=> await Task.Run(() => DoAuthout(AuthOutResponse, SessionCookie));
/// <summary>
/// Gets list of bikes from memory.
/// </summary>
/// <returns></returns>
public async Task<BikesAvailableResponse> GetBikesAvailableAsync()
=> await Task.Run(() => GetBikesAvailable(BikesAvailableResponse, null, SessionCookie));
/// <summary>
/// Gets a list of bikes reserved/ booked by acctive user from Copri.
/// </summary>
/// <param name="p_strSessionCookie">Cookie to authenticate user.</param>
/// <returns>Response holding list of bikes.</returns>
public async Task<BikesReservedOccupiedResponse> GetBikesOccupiedAsync()
{
try
{
requestBuilder.GetBikesOccupied(); // Non mock implementation if ICopriServer call this member as well. To ensure comparable behaviour this member is called here as well.
}
catch (NotSupportedException)
{
// No user logged in.
await Task.CompletedTask;
return ResponseHelper.GetBikesOccupiedNone();
}
return await Task.Run(() => GetBikesOccupied(BikesOccupiedResponse, SessionCookie));
}
/// <summary>
/// Get list of stations from file.
/// </summary>
/// <param name="p_strCookie">Auto cookie of user if user is logged in.</param>
/// <returns>List of files.</returns>
public async Task<StationsAvailableResponse> GetStationsAsync()
=> await Task.Run(() => GetStationsAll(Stations, null, SessionCookie));
/// <summary>
/// Gets booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike to book.</param>
/// <returns>Booking response.</returns>
public async Task<ReservationBookingResponse> DoReserveAsync(string bikeId, Uri operatorUri)
=> await Task.Run(() => DoReserve(BookingRequestResponse, bikeId, SessionCookie));
/// <summary>
/// Gets canel booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike to book.</param>
/// <param name="p_strCookie">Cookie of the logged in user.</param>
/// <returns>Response on cancel booking request.</returns>
public async Task<ReservationCancelReturnResponse> DoCancelReservationAsync(string bikeId, Uri operatorUri)
=> await Task.Run(() => DoCancelReservation(CancelBookingRequestResponse, bikeId, SessionCookie));
/// <summary> Gets the merchant id.</summary>
public string MerchantId => TinkApp.MerchantId;
/// <summary> Returns false because cached values are returned. </summary>
public bool IsConnected => false;
/// <summary> Logs user in. </summary>
/// <param name="p_oUser">User to log in.</param>
/// <param name="p_strDeviceId">Id specifying user and hardware.</param>
/// <param name="p_strMailAddress">Mailaddress of user to log in.</param>
/// <param name="p_strPassword">Password to log in.</param>
/// <remarks>Response which holds auth cookie <see cref="ResponseBase.authcookie"/></remarks>
public static AuthorizationResponse DoAuthorize(
string DoAuthResponse,
string p_strMailAddress,
string p_strPassword,
string p_strDeviceId)
{
return p_strMailAddress == "javaminister@gmail.com"
&& p_strPassword == "*********" &&
p_strDeviceId == "HwId1000000000000"
? JsonConvertRethrow.DeserializeObject<ResponseContainer<AuthorizationResponse>>(DoAuthResponse).shareejson
: JsonConvertRethrow.DeserializeObject<ResponseContainer<AuthorizationResponse>>(DO_AUTH_Unknown_User_FILE).shareejson;
}
/// <summary> Logs user in. </summary>
/// <remarks>Response which holds auth cookie <see cref="ResponseBase.authcookie"/></remarks>
public static AuthorizationoutResponse DoAuthout(
string authOutResponse,
string sessionCookie)
{
// Response contains auth cookie of user "JavaministerHardwareNr1"
// For this reason do not return answer if mail and pwd do not match.
return !string.IsNullOrEmpty(sessionCookie)
? JsonConvertRethrow.DeserializeObject<ResponseContainer<AuthorizationoutResponse>>(authOutResponse).shareejson
: throw new NotSupportedException();
}
/// <summary>
/// Gets list of bikes from memory.
/// </summary>
/// <param name="p_strMerchantId">Id of the merchant.</param>
/// <param name="p_strSessionCookie">Auto cookie of user if user is logged in.</param>
/// <param name="p_eSampleSet">Set of samples.</param>
/// <param name="p_lStageIndex">Index of the stage.</param>
/// <returns></returns>
public static BikesAvailableResponse GetBikesAvailable(
string BikesAvailableResponse,
string p_strMerchantId,
string p_strSessionCookie = null) => CopriCallsStatic.DeserializeResponse<BikesAvailableResponse>(BikesAvailableResponse);
/// <summary>
/// Gets stations response.
/// </summary>
/// <param name="merchantId">Id of the merchant.</param>
/// <param name="cookie">Auto cookie of user if user is logged in.</param>
/// <param name="p_eSampleSet"></param>
/// <param name="p_lStageIndex"></param>
/// <returns></returns>
public static StationsAvailableResponse GetStationsAll(
string stations,
string merchantId,
string cookie = null)
=> JsonConvertRethrow.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(stations).shareejson;
/// <summary>
/// Gets booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike.</param>
/// <param name="sessionCookie">Identifies the logged in user.</param>
/// <param name="sampleSet">Sample set to use.</param>
/// <param name="stageIndex">Index of the stage.</param>
/// <returns></returns>
public static ReservationBookingResponse DoReserve(
string bookingRequestResponse,
string bikeId,
string sessionCookie)
=> JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationBookingResponse>>(bookingRequestResponse).shareejson;
/// <summary>
/// Gets canel booking request response.
/// </summary>
/// <param name="bikeId">Id of the bike to book.</param>
/// <param name="cookie">Cookie of the logged in user.</param>
/// <returns>Response on cancel booking request.</returns>
public static ReservationCancelReturnResponse DoCancelReservation(
string cancelBookingRequestResponse,
string bikeId,
string cookie)
=> JsonConvertRethrow.DeserializeObject<ResponseContainer<ReservationCancelReturnResponse>>(cancelBookingRequestResponse).shareejson;
public Task<ReservationBookingResponse> CalculateAuthKeysAsync(string bikeId, Uri operatorUri)
=> null;
public Task<ReservationBookingResponse> UpdateLockingStateAsync(
string bikeId,
LocationDto geolocation,
lock_state state,
double batteryLevel,
Uri operatorUri)
=> null;
public Task<ReservationBookingResponse> DoBookAsync(string bikeId, Guid guid, double batteryPercentage, Uri operatorUri)
=> null;
public Task<ReservationCancelReturnResponse> DoReturn(
string bikeId,
LocationDto geolocation,
ISmartDevice smartDevice,
Uri operatorUri)
=> null;
public Task<SubmitFeedbackResponse> DoSubmitFeedback(string bikeId, string message, bool isBikeBroken, Uri operatorUri)
=> null;
/// <summary> Submits mini survey to copri server. </summary>
/// <param name="answers">Collection of answers.</param>
public Task<ResponseBase> DoSubmitMiniSurvey(IDictionary<string, string> answers)
=> null;
/// <summary>
/// Gets a list of bikes reserved/ booked by acctive user from Copri.
/// </summary>
/// <param name="sessionCookie">Cookie to authenticate user.</param>
/// <param name="SampleSet">Sample set to use.</param>
/// <param name="p_lStageIndex">Index of the stage.</param>
/// <returns>Response holding list of bikes.</returns>
public static BikesReservedOccupiedResponse GetBikesOccupied(
string bikesOccupied,
string sessionCookie = null)
{
var response = CopriCallsStatic.DeserializeResponse<BikesReservedOccupiedResponse>(bikesOccupied);
return sessionCookie != null && (response?.authcookie?.Contains(sessionCookie) ?? false)
? response
: ResponseHelper.GetBikesOccupiedNone(sessionCookie);
}
public const string DO_AUTH_Unknown_User_FILE = @"
{
""shareejson"" : {
""response"" : ""authorization"",
""authcookie"" : 0,
""response_state"" : ""Failure: cannot generate authcookie"",
""apiserver"" : ""https://tinkwwp.copri-bike.de""
}
}";
}
}

View file

@ -0,0 +1,403 @@
using TINK.Repository;
namespace TestShareeLib.Repository
{
/// <summary>
/// Holds some COPRI responses for testing purposes.
/// </summary>
/// <remarks> Holds some demo Meinkonrad and LastenradBayern bikes
/// </remarks>
public class CopriCallsMemory001 : CopriCallMemoryBase, ICopriServer
{
public CopriCallsMemory001(string sessionCookie = null) : base(
bikesAvailableResponse: BikesAvailableResponse,
bikesOccupiedResponse: BikesOccupiedResponse,
authResponse: AuthResponse,
authOutResponse: AuthOutResponse,
sessionCookie: sessionCookie)
{ }
private static string AuthResponse => @"{
""shareejson"": {
""clearing_cache"": ""0"",
""privacy_html"": ""site/privacy.html"",
""user_id"": ""javaminister@gmail.com"",
""impress_html"": ""site/impress.html"",
""tariff_info_html"": ""site/tariff_info_1.html"",
""lang"": ""DE"",
""last_used_operator"": {
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_hours"": ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_phone"": ""+49 761 45370097"",
""operator_email"": ""hotline@sharee.bike"",
""operator_color"": ""#009699""
},
""response"": ""authorization"",
""agb_checked"": ""0"",
""agb_html"": ""site/agb.html"",
""response_text"": ""Herzlich willkommen im Fahrradmietsystem"",
""bike_info_html"": ""site/bike_info.html"",
""debuglevel"": ""1"",
""uri_primary"": ""https://shareeapp-primary.copri.eu"",
""response_state"": ""OK, nothing todo"",
""new_authcoo"": ""1"",
""user_tour"": [],
""authcookie"": ""6103_112e96b36ba33de245943c5ffaf369cd_oiF2kahH"",
""copri_version"": ""4.1.8.21"",
""apiserver"": ""https://shareeapp-fr01.copri.eu"",
""user_group"": []
}
}";
private static string AuthOutResponse = @"{
""shareejson"": {
""copri_version"": ""4.1.8.21"",
""authcookie"": ""1"",
""user_tour"": [],
""user_group"": null,
""apiserver"": ""https://shareeapp-fr01.copri.eu"",
""debuglevel"": ""1"",
""uri_primary"": ""https://shareeapp-primary.copri.eu"",
""bike_info_html"": ""site/bike_info.html"",
""response_state"": ""OK, logout"",
""new_authcoo"": ""0"",
""lang"": ""DE"",
""last_used_operator"": {
""operator_hours"": ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_email"": ""hotline@sharee.bike"",
""operator_phone"": ""+49 761 45370097"",
""operator_color"": ""#009699""
},
""tariff_info_html"": ""site/tariff_info_1.html"",
""impress_html"": ""site/impress.html"",
""response_text"": ""Auf Wiedersehen."",
""agb_html"": ""site/agb.html"",
""response"": ""authout"",
""agb_checked"": ""0"",
""privacy_html"": ""site/privacy.html"",
""clearing_cache"": ""0"",
""user_id"": ""javaminister@gmail.com""
}
}";
private static string BikesOccupiedResponse => @"{
""shareejson"": {
""authcookie"": ""6103_112e96b36ba33de245943c5ffaf369cd_oiF2kahH"",
""copri_version"": ""4.1.8.21"",
""user_tour"": [],
""user_group"": [
""FR300103"",
""FR300101""
],
""bikes_occupied"": {
""157056"": {
""K_seed"": ""[-20, -104, -112, -49, 3, -74, -43, -115, -53, 34, -48, -29, -64, -90, -26, -74]"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""bike"": ""FR1544"",
""unit_price"": ""3.00"",
""description"": ""Contributor-Paul"",
""station"": ""FR103"",
""request_time"": ""2021-11-06 18:57:20.034438+01"",
""Ilockit_ID"": ""ISHAREIT-2200544"",
""total_price"": ""25.50"",
""bike_group"": [
""FR300103""
],
""K_u"": ""[43, -16, 72, -5, 23, -117, 43, 57, 124, -106, -115, 97, -93, -30, -34, -7, -21, 119, 109, 92, 0, 0, 0, 0]"",
""computed_hours"": ""8.50"",
""end_time"": ""2021-11-08 21:14:35"",
""state"": ""occupied"",
""tariff_description"": {
""eur_per_hour"": ""3.00"",
""number"": ""5494"",
""max_eur_per_day"": ""10.00"",
""name"": ""Tester Basic"",
""free_hours"": ""0.50"",
""track_info"": ""Ich stimme der Speicherung (Tracking) meiner Fahrstrecke zwecks wissenschaftlicher Auswertung und Berechnung der CO2-Einsparung zu!"",
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""lock_state"": ""locked"",
""system"": ""Ilockit"",
""gps"": {
""latitude"": ""47.9994661873206"",
""longitude"": ""7.7904340904206""
},
""real_hours"": ""50.2833333333333"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-dc969f648732"",
""start_time"": ""2021-11-06 18:57:25.445447+01""
},
""157072"": {
""bike"": ""FR1004"",
""K_seed"": ""[-31, -81, -41, 95, 112, -113, -78, -22, 84, -112, -73, 31, -125, -49, 125, 10]"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""bike_group"": [
""FR300103""
],
""total_price"": ""0.00"",
""description"": ""Contributor-Recumbent"",
""station"": ""FR103"",
""request_time"": ""2021-11-08 21:10:24.829395+01"",
""Ilockit_ID"": ""ISHAREIT-2302373"",
""unit_price"": ""3.00"",
""end_time"": ""2021-11-08 21:10:00+01"",
""state"": ""requested"",
""tariff_description"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB)."",
""free_hours"": ""0.50"",
""name"": ""Tester Basic"",
""max_eur_per_day"": ""10.00"",
""eur_per_hour"": ""3.00"",
""number"": ""5494""
},
""computed_hours"": ""0"",
""K_u"": ""[126, -125, 125, 83, 104, -121, -80, 40, 77, -35, 81, 27, 89, -124, -37, 57, 118, -113, 71, -37, 0, 0, 0, 0]"",
""start_time"": ""2021-11-08 21:10:24.829395+01"",
""gps"": {
""latitude"": ""47.9980777"",
""longitude"": ""7.7848769""
},
""lock_state"": ""locked"",
""system"": ""Ilockit"",
""real_hours"": ""0"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-fe3962c08bcc""
}
},
""apiserver"": ""https://shareeapp-fr01.copri.eu"",
""uri_primary"": ""https://shareeapp-primary.copri.eu"",
""debuglevel"": ""1"",
""bike_info_html"": ""site/bike_info.html"",
""new_authcoo"": ""0"",
""response_state"": ""OK, nothing todo"",
""last_used_operator"": {
""operator_color"": ""#008dd2"",
""operator_phone"": ""+49 089 / 111111111"",
""operator_email"": ""hotline@lastenraddemo.bayern"",
""operator_hours"": ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_name"": ""Lastenrad Bayern"",
""operator_logo"": """"
},
""lang"": ""DE"",
""impress_html"": ""site/impress.html"",
""tariff_info_html"": ""site/tariff_info_1.html"",
""agb_html"": ""site/agb.html"",
""response"": ""user_bikes_occupied"",
""agb_checked"": ""1"",
""privacy_html"": ""site/privacy.html"",
""clearing_cache"": ""0"",
""user_id"": ""ohauff@posteo.de""
}
}";
private static string BikesAvailableResponse => @"{
""shareejson"": {
""agb_checked"": ""1"",
""response"": ""bikes_available"",
""agb_html"": ""site/agb.html"",
""impress_html"": ""site/impress.html"",
""tariff_info_html"": ""site/tariff_info_1.html"",
""lang"": ""DE"",
""last_used_operator"": {
""operator_color"": ""#008dd2"",
""operator_email"": ""hotline@lastenraddemo.bayern"",
""operator_phone"": ""+49 089 / 111111111"",
""operator_name"": ""Lastenrad Bayern"",
""operator_hours"": ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_logo"": """"
},
""user_id"": ""ohauff@posteo.de"",
""clearing_cache"": ""0"",
""privacy_html"": ""site/privacy.html"",
""bikes"": {
""FR1543"": {
""system"": ""Ilockit"",
""gps"": {
""longitude"": ""7.8255321"",
""latitude"": ""47.9767121""
},
""lock_state"": ""locked"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-cc141a6f68bb"",
""state"": ""available"",
""tariff_description"": {
""free_hours"": ""0.50"",
""name"": ""Tester Basic"",
""eur_per_hour"": ""3.00"",
""number"": ""5494"",
""1543"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""max_eur_per_day"": ""10.00""
},
""bike_group"": [
""FR300103""
],
""station"": ""FR101"",
""description"": ""Contributor-bike Dominik"",
""Ilockit_ID"": ""ISHAREIT-2200543"",
""authed"": ""1"",
""bike"": ""FR1543"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu""
},
""FR1003"": {
""bike"": ""FR1003"",
""authed"": ""1"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""bike_group"": [
""FR300101""
],
""Ilockit_ID"": ""ISHAREIT-2200545"",
""station"": ""FR101"",
""description"": ""Stadtrad"",
""tariff_description"": {
""max_eur_per_day"": ""10.00"",
""number"": ""5491"",
""eur_per_hour"": ""2.00"",
""1003"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""free_hours"": ""0.50"",
""name"": ""Vauban Basic""
},
""state"": ""available"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-e38bf9d32234"",
""system"": ""Ilockit"",
""gps"": {
""longitude"": ""7.8255772"",
""latitude"": ""47.9765188""
},
""lock_state"": ""locked""
},
""FR1540"": {
""bike_group"": [
""FR300103""
],
""Ilockit_ID"": ""ISHAREIT-2200540"",
""description"": ""Contributor-bike Dieter"",
""station"": ""FR101"",
""bike"": ""FR1540"",
""authed"": ""1"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-fc3c002a2add"",
""system"": ""Ilockit"",
""gps"": {
""longitude"": ""7.8256267"",
""latitude"": ""47.976803""
},
""lock_state"": ""locked"",
""tariff_description"": {
""1540"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""free_hours"": ""0.50"",
""name"": ""Tester Basic"",
""max_eur_per_day"": ""10.00"",
""eur_per_hour"": ""3.00"",
""number"": ""5494""
},
""state"": ""available""
},
""FR1002"": {
""bike_group"": [
""FR300101""
],
""description"": ""Lasten-Dreirad"",
""station"": ""FR101"",
""Ilockit_ID"": ""ISHAREIT-2200539"",
""authed"": ""1"",
""bike"": ""FR1002"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""gps"": {
""latitude"": ""47.976552"",
""longitude"": ""7.8255068""
},
""system"": ""Ilockit"",
""lock_state"": ""locked"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-f0b4a692e169"",
""state"": ""available"",
""tariff_description"": {
""max_eur_per_day"": ""10.00"",
""1002"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""eur_per_hour"": ""2.00"",
""number"": ""5491"",
""free_hours"": ""0.50"",
""name"": ""Vauban Basic""
}
},
""FR1538"": {
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""authed"": ""1"",
""bike"": ""FR1538"",
""station"": ""FR105"",
""description"": ""Contributor-bike Rainer"",
""Ilockit_ID"": ""ISHAREIT-2200538"",
""bike_group"": [
""FR300103""
],
""state"": ""available"",
""tariff_description"": {
""max_eur_per_day"": ""10.00"",
""eur_per_hour"": ""3.00"",
""number"": ""5494"",
""1538"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
},
""free_hours"": ""0.50"",
""name"": ""Tester Basic""
},
""gps"": {
""latitude"": ""47.9275957"",
""longitude"": ""7.973976""
},
""lock_state"": ""locked"",
""system"": ""Ilockit"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-db0319a2555b""
},
""FR1001"": {
""bike_group"": [
""FR300101""
],
""station"": ""FR101"",
""description"": ""Lastenrad"",
""Ilockit_ID"": ""ISHAREIT-2200536"",
""authed"": ""1"",
""bike"": ""FR1001"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""lock_state"": ""locked"",
""gps"": {
""latitude"": ""47.9765091"",
""longitude"": ""7.8255631""
},
""system"": ""Ilockit"",
""Ilockit_GUID"": ""00000000-0000-0000-0000-caa87760e53e"",
""state"": ""available"",
""tariff_description"": {
""free_hours"": ""0.50"",
""name"": ""Vauban Basic"",
""eur_per_hour"": ""2.00"",
""number"": ""5491"",
""max_eur_per_day"": ""10.00"",
""1001"": {
""operator_agb"": ""Mit der Mietrad Anmietung wird folgender Betreiber <a href='https://shareeapp-fr01.copri.eu/site/agb.html'>AGB</a> zugestimmt (als Demo sharee AGB).""
}
}
}
},
""user_group"": [
""FR300103"",
""FR300101""
],
""apiserver"": ""https://shareeapp-fr01.copri.eu"",
""user_tour"": [],
""authcookie"": ""6103_112e96b36ba33de245943c5ffaf369cd_oiF2kahH"",
""copri_version"": ""4.1.8.21"",
""response_state"": ""OK, nothing todo"",
""new_authcoo"": ""0"",
""bike_info_html"": ""site/bike_info.html"",
""debuglevel"": ""1"",
""uri_primary"": ""https://shareeapp-primary.copri.eu""
}
}";
}
}