Add PureGym tests (#13)

This commit is contained in:
Patrick Stevens
2023-12-27 20:26:21 +00:00
committed by GitHub
parent 5144ba2c17
commit 23d31d67bb
5 changed files with 984 additions and 0 deletions

View File

@@ -17,6 +17,10 @@
<Compile Include="JsonRecord.fs" />
<Compile Include="GeneratedJson.fs"> <!--1-->
<MyriadFile>JsonRecord.fs</MyriadFile> <!--2-->
</Compile>
<Compile Include="PureGymDto.fs" />
<Compile Include="GeneratedPureGymDto.fs">
<MyriadFile>PureGymDto.fs</MyriadFile> <!--2-->
</Compile>
<None Include="..\runmyriad.sh">
<Link>runmyriad.sh</Link>

View File

@@ -0,0 +1,350 @@
//------------------------------------------------------------------------------
// This code was generated by myriad.
// Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
namespace PureGym
/// Module containing JSON parsing methods for the GymOpeningHours type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymOpeningHours =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymOpeningHours =
let OpeningHours =
node.["openingHours"].AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> List.ofSeq
let IsAlwaysOpen = node.["isAlwaysOpen"].AsValue().GetValue<bool> ()
{
IsAlwaysOpen = IsAlwaysOpen
OpeningHours = OpeningHours
}
namespace PureGym
/// Module containing JSON parsing methods for the GymAccessOptions type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAccessOptions =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAccessOptions =
let QrCodeAccess = node.["qrCodeAccess"].AsValue().GetValue<bool> ()
let PinAccess = node.["pinAccess"].AsValue().GetValue<bool> ()
{
PinAccess = PinAccess
QrCodeAccess = QrCodeAccess
}
namespace PureGym
/// Module containing JSON parsing methods for the GymLocation type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymLocation =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymLocation =
let Latitude =
try
node.["latitude"].AsValue().GetValue<float> ()
with :? System.InvalidOperationException as exc ->
if exc.Message.Contains "cannot be converted to" then
if
System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString
then
node.["latitude"].AsValue().GetValue<string> () |> System.Double.Parse
else
reraise ()
else
reraise ()
let Longitude =
try
node.["longitude"].AsValue().GetValue<float> ()
with :? System.InvalidOperationException as exc ->
if exc.Message.Contains "cannot be converted to" then
if
System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString
then
node.["longitude"].AsValue().GetValue<string> () |> System.Double.Parse
else
reraise ()
else
reraise ()
{
Longitude = Longitude
Latitude = Latitude
}
namespace PureGym
/// Module containing JSON parsing methods for the GymAddress type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAddress =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAddress =
let Postcode = node.["postcode"].AsValue().GetValue<string> ()
let County =
match node.["county"] with
| null -> None
| v -> v.AsValue().GetValue<string> () |> Some
let Town = node.["town"].AsValue().GetValue<string> ()
let AddressLine3 =
match node.["addressLine3"] with
| null -> None
| v -> v.AsValue().GetValue<string> () |> Some
let AddressLine2 =
match node.["addressLine2"] with
| null -> None
| v -> v.AsValue().GetValue<string> () |> Some
let AddressLine1 = node.["addressLine1"].AsValue().GetValue<string> ()
{
AddressLine1 = AddressLine1
AddressLine2 = AddressLine2
AddressLine3 = AddressLine3
Town = Town
County = County
Postcode = Postcode
}
namespace PureGym
/// Module containing JSON parsing methods for the Gym type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Gym =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Gym =
let ReopenDate = node.["reopenDate"].AsValue().GetValue<string> ()
let TimeZone = node.["timeZone"].AsValue().GetValue<string> ()
let Location = GymLocation.jsonParse node.["location"]
let AccessOptions = GymAccessOptions.jsonParse node.["accessOptions"]
let GymOpeningHours = GymOpeningHours.jsonParse node.["gymOpeningHours"]
let EmailAddress = node.["emailAddress"].AsValue().GetValue<string> ()
let PhoneNumber = node.["phoneNumber"].AsValue().GetValue<string> ()
let Address = GymAddress.jsonParse node.["address"]
let Status = node.["status"].AsValue().GetValue<int> ()
let Id = node.["id"].AsValue().GetValue<int> ()
let Name = node.["name"].AsValue().GetValue<string> ()
{
Name = Name
Id = Id
Status = Status
Address = Address
PhoneNumber = PhoneNumber
EmailAddress = EmailAddress
GymOpeningHours = GymOpeningHours
AccessOptions = AccessOptions
Location = Location
TimeZone = TimeZone
ReopenDate = ReopenDate
}
namespace PureGym
/// Module containing JSON parsing methods for the Member type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Member =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Member =
let MemberStatus = node.["memberStatus"].AsValue().GetValue<int> ()
let SuspendedReason = node.["suspendedReason"].AsValue().GetValue<int> ()
let MembershipLevel = node.["membershipLevel"].AsValue().GetValue<int> ()
let MembershipName = node.["membershipName"].AsValue().GetValue<string> ()
let Postcode = node.["postCode"].AsValue().GetValue<string> ()
let MobileNumber = node.["mobileNumber"].AsValue().GetValue<string> ()
let DateOfBirth =
node.["dateofBirth"].AsValue().GetValue<string> () |> System.DateOnly.Parse
let GymAccessPin = node.["gymAccessPin"].AsValue().GetValue<string> ()
let EmailAddress = node.["emailAddress"].AsValue().GetValue<string> ()
let HomeGymName = node.["homeGymName"].AsValue().GetValue<string> ()
let HomeGymId = node.["homeGymId"].AsValue().GetValue<int> ()
let LastName = node.["lastName"].AsValue().GetValue<string> ()
let FirstName = node.["firstName"].AsValue().GetValue<string> ()
let CompoundMemberId = node.["compoundMemberId"].AsValue().GetValue<string> ()
let Id = node.["id"].AsValue().GetValue<int> ()
{
Id = Id
CompoundMemberId = CompoundMemberId
FirstName = FirstName
LastName = LastName
HomeGymId = HomeGymId
HomeGymName = HomeGymName
EmailAddress = EmailAddress
GymAccessPin = GymAccessPin
DateOfBirth = DateOfBirth
MobileNumber = MobileNumber
Postcode = Postcode
MembershipName = MembershipName
MembershipLevel = MembershipLevel
SuspendedReason = SuspendedReason
MemberStatus = MemberStatus
}
namespace PureGym
/// Module containing JSON parsing methods for the GymAttendance type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAttendance =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAttendance =
let MaximumCapacity = node.["maximumCapacity"].AsValue().GetValue<int> ()
let LastRefreshedPeopleInClasses =
node.["lastRefreshedPeopleInClasses"].AsValue().GetValue<string> ()
|> System.DateTime.Parse
let LastRefreshed =
node.["lastRefreshed"].AsValue().GetValue<string> () |> System.DateTime.Parse
let AttendanceTime =
node.["attendanceTime"].AsValue().GetValue<string> () |> System.DateTime.Parse
let IsApproximate = node.["isApproximate"].AsValue().GetValue<bool> ()
let TotalPeopleSuffix =
match node.["totalPeopleSuffix"] with
| null -> None
| v -> v.AsValue().GetValue<string> () |> Some
let TotalPeopleInClasses = node.["totalPeopleInClasses"].AsValue().GetValue<int> ()
let TotalPeopleInGym = node.["totalPeopleInGym"].AsValue().GetValue<int> ()
let Description = node.["description"].AsValue().GetValue<string> ()
{
Description = Description
TotalPeopleInGym = TotalPeopleInGym
TotalPeopleInClasses = TotalPeopleInClasses
TotalPeopleSuffix = TotalPeopleSuffix
IsApproximate = IsApproximate
AttendanceTime = AttendanceTime
LastRefreshed = LastRefreshed
LastRefreshedPeopleInClasses = LastRefreshedPeopleInClasses
MaximumCapacity = MaximumCapacity
}
namespace PureGym
/// Module containing JSON parsing methods for the MemberActivityDto type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MemberActivityDto =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : MemberActivityDto =
let LastRefreshed =
node.["lastRefreshed"].AsValue().GetValue<string> () |> System.DateTime.Parse
let IsEstimated = node.["isEstimated"].AsValue().GetValue<bool> ()
let TotalClasses = node.["totalClasses"].AsValue().GetValue<int> ()
let TotalVisits = node.["totalVisits"].AsValue().GetValue<int> ()
let AverageDuration = node.["averageDuration"].AsValue().GetValue<int> ()
let TotalDuration = node.["totalDuration"].AsValue().GetValue<int> ()
{
TotalDuration = TotalDuration
AverageDuration = AverageDuration
TotalVisits = TotalVisits
TotalClasses = TotalClasses
IsEstimated = IsEstimated
LastRefreshed = LastRefreshed
}
namespace PureGym
/// Module containing JSON parsing methods for the SessionsAggregate type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsAggregate =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsAggregate =
let Duration = node.["duration"].AsValue().GetValue<int> ()
let Visits = node.["visits"].AsValue().GetValue<int> ()
let Activities = node.["activities"].AsValue().GetValue<int> ()
{
Activities = Activities
Visits = Visits
Duration = Duration
}
namespace PureGym
/// Module containing JSON parsing methods for the VisitGym type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module VisitGym =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : VisitGym =
let Status = node.["status"].AsValue().GetValue<string> ()
let Name = node.["name"].AsValue().GetValue<string> ()
let Id = node.["id"].AsValue().GetValue<int> ()
{
Id = Id
Name = Name
Status = Status
}
namespace PureGym
/// Module containing JSON parsing methods for the Visit type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Visit =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit =
let Gym = VisitGym.jsonParse node.["gym"]
let Duration = node.["duration"].AsValue().GetValue<int> ()
let StartTime =
node.["startTime"].AsValue().GetValue<string> () |> System.DateTime.Parse
let IsDurationEstimated = node.["isDurationEstimated"].AsValue().GetValue<bool> ()
{
IsDurationEstimated = IsDurationEstimated
StartTime = StartTime
Duration = Duration
Gym = Gym
}
namespace PureGym
/// Module containing JSON parsing methods for the SessionsSummary type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsSummary =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary =
let ThisWeek = SessionsAggregate.jsonParse node.["thisWeek"]
let Total = SessionsAggregate.jsonParse node.["total"]
{
Total = Total
ThisWeek = ThisWeek
}
namespace PureGym
/// Module containing JSON parsing methods for the Sessions type
[<RequireQualifiedAccess>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Sessions =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Sessions =
let Visits =
node.["visits"].AsArray ()
|> Seq.map (fun elt -> Visit.jsonParse elt)
|> List.ofSeq
let Summary = SessionsSummary.jsonParse node.["summary"]
{
Summary = Summary
Visits = Visits
}

317
ConsumePlugin/PureGymDto.fs Normal file
View File

@@ -0,0 +1,317 @@
// Copied from https://gitea.patrickstevens.co.uk/patrick/puregym-unofficial-dotnet/src/commit/2741c5e36cf0bdb203b12b78a8062e25af9d89c7/PureGym/Api.fs
namespace PureGym
open System
open System.Text.Json.Serialization
/// Describes the opening hours of a given gym.
[<MyriadPlugin.JsonParse>]
type GymOpeningHours =
{
/// If this is true, there should be no OpeningHours (but nothing enforces that).
IsAlwaysOpen : bool
/// This is a pretty unstructured list, which is in general not really parseable: it's human-readable only.
OpeningHours : string list
}
/// Human-readable representation
override this.ToString () =
if this.IsAlwaysOpen then
"always open"
else
this.OpeningHours |> String.concat ", "
/// How a human can authenticate with a gym when they physically try to enter it
[<MyriadPlugin.JsonParse>]
type GymAccessOptions =
{
/// This gym has PIN entry pads
PinAccess : bool
/// This gym has a QR code scanner. QR codes can be generated with the PureGym app.
QrCodeAccess : bool
}
/// Where a gym is on the Earth
[<MyriadPlugin.JsonParse>]
type GymLocation =
{
/// Measured in degrees
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
Longitude : float
/// Measured in degrees
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
Latitude : float
}
/// The postal address of a gym
[<MyriadPlugin.JsonParse>]
type GymAddress =
{
/// E.g. "Canterbury Court"
[<JsonRequired>]
AddressLine1 : string
/// E.g. "Units 4, 4A, 5 And 5A"
AddressLine2 : string option
/// E.g. "Kennington Park"
AddressLine3 : string option
/// E.g. "LONDON"
[<JsonRequired>]
Town : string
County : string option
/// E.g. "SW9 6DE"
[<JsonRequired>]
Postcode : string
}
/// Human-readable statement of the address
override this.ToString () =
[
yield Some this.AddressLine1
yield this.AddressLine2
yield this.AddressLine3
match this.County with
| None -> yield Some $"%s{this.Town} %s{this.Postcode}"
| Some county ->
yield Some this.Town
yield Some $"%s{county} %s{this.Postcode}"
]
|> Seq.choose id
|> String.concat "\n"
/// Metadata about a physical gym
[<MyriadPlugin.JsonParse>]
type Gym =
{
// The following fields are returned but are always null
// ReasonsToJoin : string
// VirtualTourUrl : Uri
// PersonalTrainersUrl : Uri
// WebViewUrl : Uri
// FloorPlanUrl : Uri
// StaffMembers : string
/// The name of this gym, e.g. "London Oval"
[<JsonRequired>]
Name : string
/// This gym's ID in the PureGym system, e.g. 19
[<JsonRequired>]
Id : int
/// I don't know what this status is. Please tell me if you know!
[<JsonRequired>]
Status : int
/// Postal address of this gym
[<JsonRequired>]
Address : GymAddress
/// Phone number of this gym, e.g. "+44 1234 567890"
[<JsonRequired>]
PhoneNumber : string
/// Contact email address for this gym's staff
[<JsonRequired>]
EmailAddress : string
/// When this gym is open
[<JsonRequired>]
GymOpeningHours : GymOpeningHours
/// How a human can physically authenticate when they physically enter this gym
[<JsonRequired>]
AccessOptions : GymAccessOptions
/// Where this gym is physically located
[<JsonRequired>]
Location : GymLocation
/// The IANA time zone this gym observes, e.g. "Europe/London"
[<JsonRequired>]
TimeZone : string
/// This is a date-time in the format yyyy-MM-ddTHH:mm:ss+01 Europe/London
ReopenDate : string
}
/// Human-readable representation of the most important information about this gym
override this.ToString () =
$"""%s{this.Name} (%i{this.Id})
{this.Address}
%s{this.EmailAddress} %s{this.PhoneNumber}
Opening hours: %s{string<GymOpeningHours> this.GymOpeningHours}
%s{string<GymAccessOptions> this.AccessOptions}
"""
/// A human member of PureGym
[<MyriadPlugin.JsonParse>]
type Member =
{
/// This member's ID. This is a fairly large number.
Id : int
/// No idea what this is - please tell me if you know!
CompoundMemberId : string
/// First name, e.g. "Patrick"
FirstName : string
/// Last name, e.g. "Stevens"
LastName : string
/// ID of the gym designated as this user's home gym. This is also the "Id" field of the appropriate Gym object.
HomeGymId : int
/// The name of the gym designated as this user's home gym. This is also the "Name" field of the appropriate
/// Gym object.
HomeGymName : string
/// This user's email address
EmailAddress : string
/// This user's gym access pin, probably 8 digits
GymAccessPin : string
/// This user's recorded date of birth
[<JsonPropertyName "dateofBirth">]
DateOfBirth : DateOnly
/// This user's phone number, human-readable
MobileNumber : string
/// This user's registered home postcode
[<JsonPropertyName "postCode">]
Postcode : string
/// E.g. "Corporate"
MembershipName : string
MembershipLevel : int
SuspendedReason : int
MemberStatus : int
}
/// Statistics for how many people are currently at a gym
[<MyriadPlugin.JsonParse>]
type GymAttendance =
{
/// This appears always to be just equal to TotalPeopleInGym, but a string.
[<JsonRequired>]
Description : string
/// How many people are in the gym as of this statistics snapshot
[<JsonRequired>]
TotalPeopleInGym : int
/// How many people are in classes at the gym as of this statistics snapshot
[<JsonRequired>]
TotalPeopleInClasses : int
/// E.g. " or fewer"
TotalPeopleSuffix : string option
[<JsonRequired>]
IsApproximate : bool
/// When the query was received (I think)
AttendanceTime : DateTime
/// When the "total people in gym" snapshot was taken that is reported here
LastRefreshed : DateTime
/// When the "number of people in classes" snapshot was taken that is reported here
LastRefreshedPeopleInClasses : DateTime
/// Maximum capacity of the gym, or 0 if no listed capacity
MaximumCapacity : int
}
/// The visit statistics for a particular human to a particular gym.
/// The semantics of this class are basically unknown.
type MemberActivityThisMonth =
{
/// How many minutes, including classes, have been logged so far this month
TotalDurationMinutes : int
/// How long, in minutes, each visit has been on average this month
AverageDurationMinutes : int
/// How many visits have been made this month, excluding classes
TotalVisits : int
/// How many classes have been attended this month
TotalClasses : int
/// Whether this block of statistics is estimated rather than exact
IsEstimated : bool
/// When this data was constructed
LastRefreshed : DateTime
}
/// Don't use this type. It's public because System.Text.Json can't do private types.
[<MyriadPlugin.JsonParse>]
type MemberActivityDto =
{
[<JsonRequired>]
TotalDuration : int
[<JsonRequired>]
AverageDuration : int
[<JsonRequired>]
TotalVisits : int
[<JsonRequired>]
TotalClasses : int
[<JsonRequired>]
IsEstimated : bool
[<JsonRequired>]
LastRefreshed : DateTime
}
member this.ToMemberActivity () =
{
TotalDurationMinutes = this.TotalDuration
AverageDurationMinutes = this.AverageDuration
TotalVisits = this.TotalVisits
TotalClasses = this.TotalClasses
IsEstimated = this.IsEstimated
LastRefreshed = this.LastRefreshed
}
[<MyriadPlugin.JsonParse>]
type SessionsAggregate =
{
/// Number of gym "activities" within some query-defined time period; presumably this is like classes?
/// It's always 0 for me.
Activities : int
/// Number of visits to the gym within some query-defined time period.
Visits : int
/// In minutes: total time spent in gym during the query-defined time period.
Duration : int
}
/// The DTO for gym info returned from the Sessions endpoint.
[<MyriadPlugin.JsonParse>]
type VisitGym =
{
// Omitting Location, GymAccess, ContactInfo, TimeZone because these were all null for me
/// The PureGym ID of this gym, e.g. 19
Id : int
/// E.g. "London Oval", the canonical name of this gym
Name : string
/// For some reason this always seems to be "Blocked"
Status : string
}
/// Summary of a single visit to a gym.
[<MyriadPlugin.JsonParse>]
type Visit =
{
// Omitted Name because it always was null for me
/// Whether the Duration field is estimated.
IsDurationEstimated : bool
/// When the visit began.
StartTime : DateTime
/// In minutes.
Duration : int
/// Which gym was visited
Gym : VisitGym
}
/// Human-readable non-round-trip representation.
override this.ToString () =
let startTime = this.StartTime.ToString "yyyy-MM-dd HH:mm"
$"%s{this.Gym.Name}: %s{startTime} (%i{this.Duration} minutes)"
/// Aggregate statistics for gym visits across a time period.
[<MyriadPlugin.JsonParse>]
type SessionsSummary =
{
/// Aggregate stats for gym visits within the query-dependent time period.
Total : SessionsAggregate
/// Aggregate stats for gym visits "this week", whatever that means to PureGym.
ThisWeek : SessionsAggregate
}
/// Human-readable non-round-trip representation.
override this.ToString () =
$"%i{this.Total.Visits} visits, totalling %i{this.Total.Duration} minutes"
[<MyriadPlugin.JsonParse>]
type Sessions =
{
Summary : SessionsSummary
Visits : Visit list
}
/// Human-readable non-round-trip representation.
override this.ToString () =
let summary = string<SessionsSummary> this.Summary
let visits = this.Visits |> Seq.map string<Visit> |> String.concat "\n"
$"%s{summary}\n%s{visits}"

View File

@@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="TestPureGymJson.fs" />
<Compile Include="TestSurface.fs" />
<Compile Include="TestRemoveOptions.fs" />
<Compile Include="TestJsonParse.fs" />

View File

@@ -0,0 +1,312 @@
namespace PureGym.Test
open System
open System.Text.Json.Nodes
open NUnit.Framework
open FsUnitTyped
open PureGym
[<TestFixture>]
module TestPureGymJson =
let gymOpeningHoursCases =
[
"""{"openingHours": [], "isAlwaysOpen": false}""",
{
GymOpeningHours.OpeningHours = []
IsAlwaysOpen = false
}
"""{"openingHours": ["something"], "isAlwaysOpen": false}""",
{
GymOpeningHours.OpeningHours = [ "something" ]
IsAlwaysOpen = false
}
]
|> List.map TestCaseData
[<TestCaseSource(nameof (gymOpeningHoursCases))>]
let ``GymOpeningHours JSON parse`` (json : string, expected : GymOpeningHours) =
JsonNode.Parse json |> GymOpeningHours.jsonParse |> shouldEqual expected
let gymAccessOptionsCases =
List.allPairs [ true ; false ] [ true ; false ]
|> List.map (fun (a, b) ->
let s = sprintf """{"pinAccess": %b, "qrCodeAccess": %b}""" a b
s,
{
GymAccessOptions.PinAccess = a
QrCodeAccess = b
}
)
|> List.map TestCaseData
[<TestCaseSource(nameof (gymAccessOptionsCases))>]
let ``GymAccessOptions JSON parse`` (json : string, expected : GymAccessOptions) =
JsonNode.Parse json |> GymAccessOptions.jsonParse |> shouldEqual expected
let gymLocationCases =
[
"""{"latitude": 1.0, "longitude": 3.0}""",
{
GymLocation.Latitude = 1.0
Longitude = 3.0
}
]
|> List.map TestCaseData
[<TestCaseSource(nameof (gymLocationCases))>]
let ``GymLocation JSON parse`` (json : string, expected : GymLocation) =
JsonNode.Parse json |> GymLocation.jsonParse |> shouldEqual expected
let gymAddressCases =
[
"""{"addressLine1": "", "postCode": "hi", "town": ""}""",
{
GymAddress.AddressLine1 = ""
AddressLine2 = None
AddressLine3 = None
County = None
Postcode = "hi"
Town = ""
}
"""{"addressLine1": "", "addressLine2": null, "postCode": "hi", "town": ""}""",
{
GymAddress.AddressLine1 = ""
AddressLine2 = None
AddressLine3 = None
County = None
Postcode = "hi"
Town = ""
}
]
|> List.map TestCaseData
[<TestCaseSource(nameof (gymAddressCases))>]
let ``GymAddress JSON parse`` (json : string, expected : GymAddress) =
JsonNode.Parse (json, Nullable (JsonNodeOptions (PropertyNameCaseInsensitive = true)))
|> GymAddress.jsonParse
|> shouldEqual expected
let gymCases =
let ovalJson =
"""{"name":"London Oval","id":19,"status":2,"address":{"addressLine1":"Canterbury Court","addressLine2":"Units 4, 4A, 5 And 5A","addressLine3":"Kennington Park","town":"LONDON","county":null,"postcode":"SW9 6DE"},"phoneNumber":"+44 3444770005","emailAddress":"info.londonoval@puregym.com","staffMembers":null,"gymOpeningHours":{"isAlwaysOpen":true,"openingHours":[]},"reasonsToJoin":null,"accessOptions":{"pinAccess":true,"qrCodeAccess":true},"virtualTourUrl":null,"personalTrainersUrl":null,"webViewUrl":null,"floorPlanUrl":null,"location":{"longitude":"-0.110252","latitude":"51.480401"},"timeZone":"Europe/London","reopenDate":"2021-04-12T00:00:00+01 Europe/London"}"""
let oval =
{
Gym.Name = "London Oval"
Id = 19
Status = 2
Address =
{
AddressLine1 = "Canterbury Court"
AddressLine2 = Some "Units 4, 4A, 5 And 5A"
AddressLine3 = Some "Kennington Park"
Town = "LONDON"
County = None
Postcode = "SW9 6DE"
}
PhoneNumber = "+44 3444770005"
EmailAddress = "info.londonoval@puregym.com"
GymOpeningHours =
{
IsAlwaysOpen = true
OpeningHours = []
}
AccessOptions =
{
PinAccess = true
QrCodeAccess = true
}
Location =
{
Longitude = -0.110252
Latitude = 51.480401
}
TimeZone = "Europe/London"
ReopenDate = "2021-04-12T00:00:00+01 Europe/London"
}
[ ovalJson, oval ] |> List.map TestCaseData
[<TestCaseSource(nameof (gymCases))>]
let ``Gym JSON parse`` (json : string, expected : Gym) =
JsonNode.Parse json |> Gym.jsonParse |> shouldEqual expected
let memberCases =
let me =
{
Id = 1234567
CompoundMemberId = "12A123456"
FirstName = "Patrick"
LastName = "Stevens"
HomeGymId = 19
HomeGymName = "London Oval"
EmailAddress = "someone@somewhere"
GymAccessPin = "00000000"
DateOfBirth = DateOnly (1994, 01, 02)
MobileNumber = "+44 1234567"
Postcode = "W1A 1AA"
MembershipName = "Corporate"
MembershipLevel = 12
SuspendedReason = 0
MemberStatus = 2
}
let meJson =
"""{
"id": 1234567,
"compoundMemberId": "12A123456",
"firstName": "Patrick",
"lastName": "Stevens",
"homeGymId": 19,
"homeGymName": "London Oval",
"emailAddress": "someone@somewhere",
"gymAccessPin": "00000000",
"dateofBirth": "1994-01-02",
"mobileNumber": "+44 1234567",
"postCode": "W1A 1AA",
"membershipName": "Corporate",
"membershipLevel": 12,
"suspendedReason": 0,
"memberStatus": 2
}"""
[ meJson, me ] |> List.map TestCaseData
[<TestCaseSource(nameof memberCases)>]
let ``Member JSON parse`` (json : string, expected : Member) =
json |> JsonNode.Parse |> Member.jsonParse |> shouldEqual expected
let gymAttendanceCases =
let json =
"""{
"description": "65",
"totalPeopleInGym": 65,
"totalPeopleInClasses": 2,
"totalPeopleSuffix": null,
"isApproximate": false,
"attendanceTime": "2023-12-27T18:54:09.5101697",
"lastRefreshed": "2023-12-27T18:54:09.5101697Z",
"lastRefreshedPeopleInClasses": "2023-12-27T18:50:26.0782286Z",
"maximumCapacity": 0
}"""
let expected =
{
Description = "65"
TotalPeopleInGym = 65
TotalPeopleInClasses = 2
TotalPeopleSuffix = None
IsApproximate = false
AttendanceTime =
DateTime (2023, 12, 27, 18, 54, 09, 510, 169, DateTimeKind.Utc)
+ TimeSpan.FromTicks 7L
LastRefreshed =
DateTime (2023, 12, 27, 18, 54, 09, 510, 169, DateTimeKind.Utc)
+ TimeSpan.FromTicks 7L
LastRefreshedPeopleInClasses =
DateTime (2023, 12, 27, 18, 50, 26, 078, 228, DateTimeKind.Utc)
+ TimeSpan.FromTicks 6L
MaximumCapacity = 0
}
[ json, expected ] |> List.map TestCaseData
[<TestCaseSource(nameof gymAttendanceCases)>]
let ``GymAttendance JSON parse`` (json : string, expected : GymAttendance) =
json |> JsonNode.Parse |> GymAttendance.jsonParse |> shouldEqual expected
let memberActivityDtoCases =
let json =
"""{"totalDuration":2217,"averageDuration":48,"totalVisits":46,"totalClasses":0,"isEstimated":false,"lastRefreshed":"2023-12-27T19:00:56.0309892Z"}"""
let value =
{
TotalDuration = 2217
AverageDuration = 48
TotalVisits = 46
TotalClasses = 0
IsEstimated = false
LastRefreshed =
DateTime (2023, 12, 27, 19, 00, 56, 030, 989, DateTimeKind.Utc)
+ TimeSpan.FromTicks 2L
}
[ json, value ] |> List.map TestCaseData
[<TestCaseSource(nameof memberActivityDtoCases)>]
let ``MemberActivityDto JSON parse`` (json : string, expected : MemberActivityDto) =
json |> JsonNode.Parse |> MemberActivityDto.jsonParse |> shouldEqual expected
let sessionsCases =
let json =
"""{
"Summary":{"Total":{"Activities":0,"Visits":10,"Duration":445},"ThisWeek":{"Activities":0,"Visits":0,"Duration":0}},
"Visits":[
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-21T10:12:00","Duration":50,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-20T12:05:00","Duration":80,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-17T19:37:00","Duration":46,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-16T12:19:00","Duration":37,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-15T11:14:00","Duration":47,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-13T10:30:00","Duration":36,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-10T16:18:00","Duration":32,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-05T22:36:00","Duration":40,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-03T17:59:00","Duration":48,"Name":null},
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-01T21:41:00","Duration":29,"Name":null}],
"Activities":[]}
"""
let singleVisit startTime duration =
{
IsDurationEstimated = false
Gym =
{
Id = 19
Name = "London Oval"
Status = "Blocked"
}
StartTime = startTime
Duration = duration
}
let expected =
{
Summary =
{
Total =
{
Activities = 0
Visits = 10
Duration = 445
}
ThisWeek =
{
Activities = 0
Visits = 0
Duration = 0
}
}
Visits =
[
singleVisit (DateTime (2023, 12, 21, 10, 12, 00)) 50
singleVisit (DateTime (2023, 12, 20, 12, 05, 00)) 80
singleVisit (DateTime (2023, 12, 17, 19, 37, 00)) 46
singleVisit (DateTime (2023, 12, 16, 12, 19, 00)) 37
singleVisit (DateTime (2023, 12, 15, 11, 14, 00)) 47
singleVisit (DateTime (2023, 12, 13, 10, 30, 00)) 36
singleVisit (DateTime (2023, 12, 10, 16, 18, 00)) 32
singleVisit (DateTime (2023, 12, 05, 22, 36, 00)) 40
singleVisit (DateTime (2023, 12, 03, 17, 59, 00)) 48
singleVisit (DateTime (2023, 12, 01, 21, 41, 00)) 29
]
}
[ json, expected ] |> List.map TestCaseData
[<TestCaseSource(nameof sessionsCases)>]
let ``Sessions JSON parse`` (json : string, expected : Sessions) =
json
|> fun o -> JsonNode.Parse (o, Nullable (JsonNodeOptions (PropertyNameCaseInsensitive = true)))
|> Sessions.jsonParse
|> shouldEqual expected