namespace WoofWare.Myriad.Plugins.Test open System open System.Net open System.Net.Http open NUnit.Framework open PureGym open FsUnitTyped [] module TestPureGymRestApi = // several of these, to check behaviour around treatment of initial slashes let baseUris = [ // Everything is relative to the root: "https://example.com" // Everything is also relative to the root, because `foo` is not a subdir: "https://example.com/foo" // Everything is relative to `foo`, because `foo` is a subdir "https://example.com/foo/" ] |> List.map Uri let gymsCases = PureGymDtos.gymCases |> List.collect (fun (json, gym) -> [ $"[%s{json}]", [ gym ] ; $"[%s{json}, %s{json}]", [ gym ; gym ] ]) |> List.allPairs baseUris |> List.map TestCaseData [] let ``Test GetGyms`` (baseUri : Uri, (json : string, expected : Gym list)) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // URI is relative in the attribute on the IPureGymApi member, // so this never gets redirected let expectedUri = match baseUri.ToString () with | "https://example.com/" -> "https://example.com/v1/gyms/" | "https://example.com/foo" -> "https://example.com/v1/gyms/" | "https://example.com/foo/" -> "https://example.com/foo/v1/gyms/" | s -> failwith $"Unrecognised base URI: %s{s}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetGyms().Result |> shouldEqual expected let gymAttendanceCases = PureGymDtos.gymAttendanceCases |> List.allPairs baseUris |> List.map TestCaseData [] let ``Test GetGymAttendance`` (baseUri : Uri, (json : string, expected : GymAttendance)) = let requestedGym = 3 let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // URI is relative in the attribute on the IPureGymApi member, // so this never gets redirected let expectedUri = match baseUri.ToString () with | "https://example.com/" -> $"https://example.com/v1/gyms/%i{requestedGym}/attendance" | "https://example.com/foo" -> $"https://example.com/v1/gyms/%i{requestedGym}/attendance" | "https://example.com/foo/" -> $"https://example.com/foo/v1/gyms/%i{requestedGym}/attendance" | s -> failwith $"Unrecognised base URI: %s{s}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetGymAttendance(requestedGym).Result |> shouldEqual expected api.GetGymAttendance'(requestedGym).Result |> shouldEqual expected let memberCases = PureGymDtos.memberCases |> List.allPairs baseUris |> List.map TestCaseData [] let ``Test GetMember`` (baseUri : Uri, (json : string, expected : Member)) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // URI is relative in the attribute on the IPureGymApi member, // so this never gets redirected let expectedUri = match baseUri.ToString () with | "https://example.com/" -> "https://example.com/v1/member" | "https://example.com/foo" -> "https://example.com/v1/member" | "https://example.com/foo/" -> "https://example.com/foo/v1/member" | s -> failwith $"Unrecognised base URI: %s{s}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetMember().Result |> shouldEqual expected let gymCases = PureGymDtos.gymCases |> List.allPairs baseUris |> List.map TestCaseData [] let ``Test GetGym`` (baseUri : Uri, (json : string, expected : Gym)) = let requestedGym = 3 let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // URI is relative in the attribute on the IPureGymApi member, // so this never gets redirected let expectedUri = match baseUri.ToString () with | "https://example.com/" -> $"https://example.com/v1/gyms/%i{requestedGym}" | "https://example.com/foo" -> $"https://example.com/v1/gyms/%i{requestedGym}" | "https://example.com/foo/" -> $"https://example.com/foo/v1/gyms/%i{requestedGym}" | s -> failwith $"Unrecognised base URI: %s{s}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetGym(requestedGym).Result |> shouldEqual expected let memberActivityCases = PureGymDtos.memberActivityDtoCases |> List.allPairs baseUris |> List.map TestCaseData [] let ``Test GetMemberActivity`` (baseUri : Uri, (json : string, expected : MemberActivityDto)) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // URI is relative in the attribute on the IPureGymApi member, // so this never gets redirected let expectedUri = match baseUri.ToString () with | "https://example.com/" -> "https://example.com/v1/member/activity" | "https://example.com/foo" -> "https://example.com/v1/member/activity" | "https://example.com/foo/" -> "https://example.com/foo/v1/member/activity" | s -> failwith $"Unrecognised base URI: %s{s}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetMemberActivity().Result |> shouldEqual expected let dates = [ for month = 1 to 3 do // span the number 12, to catch muddling up month and day for day = 11 to 13 do yield DateOnly (2023, month, day) ] let sessionsCases = PureGymDtos.sessionsCases |> List.allPairs dates |> List.allPairs dates |> List.allPairs baseUris |> List.map TestCaseData let inline dateOnlyToString (d : DateOnly) : string = let month = if d.Month < 10 then $"0%i{d.Month}" else $"%i{d.Month}" let day = if d.Day < 10 then $"0%i{d.Day}" else $"%i{d.Day}" $"{d.Year}-{month}-{day}" [] let ``Test GetSessions`` (baseUri : Uri, (startDate : DateOnly, (endDate : DateOnly, (json : string, expected : Sessions)))) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // This one is specified as being absolute, in its attribute on the IPureGymApi type let expectedUri = let fromDate = dateOnlyToString startDate let toDate = dateOnlyToString endDate $"https://example.com/v2/gymSessions/member?fromDate=%s{fromDate}&toDate=%s{toDate}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetSessions(startDate, endDate).Result |> shouldEqual expected [] let ``Test GetSessionsWithQuery`` (baseUri : Uri, (startDate : DateOnly, (endDate : DateOnly, (json : string, expected : Sessions)))) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get // This one is specified as being absolute, in its attribute on the IPureGymApi type let expectedUri = let fromDate = dateOnlyToString startDate let toDate = dateOnlyToString endDate $"https://example.com/v2/gymSessions/member?foo=1&fromDate=%s{fromDate}&toDate=%s{toDate}" message.RequestUri.ToString () |> shouldEqual expectedUri let content = new StringContent (json) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.make baseUri proc let api = PureGymApi.make client api.GetSessionsWithQuery(startDate, endDate).Result |> shouldEqual expected [] let ``URI example`` () = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Get message.RequestUri.ToString () |> shouldEqual "https://whatnot.com/some/url" let content = new StringContent ("""{"someUri": "https://patrick@en.wikipedia.org/wiki/foo"}""") let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.makeNoUri proc let api = PureGymApi.make client let uri = api.GetUrl().Result.SomeUri uri.ToString () |> shouldEqual "https://patrick@en.wikipedia.org/wiki/foo" uri.UserInfo |> shouldEqual "patrick" uri.Host |> shouldEqual "en.wikipedia.org" [] [] let ``Map option example`` (isSome : bool) = let proc (message : HttpRequestMessage) : HttpResponseMessage Async = async { message.Method |> shouldEqual HttpMethod.Post message.RequestUri.ToString () |> shouldEqual "https://whatnot.com/some/url" let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask if isSome then content |> shouldEqual """{"hi":"bye"}""" else content |> shouldEqual "null" let content = new StringContent (content) let resp = new HttpResponseMessage (HttpStatusCode.OK) resp.Content <- content return resp } use client = HttpClientMock.makeNoUri proc let api = PureGymApi.make client let expected = if isSome then [ "hi", "bye" ] |> Map.ofList |> Some else None let actual = api.PostStringToString(expected).Result actual |> shouldEqual expected