diff --git a/tests/FSharp.Data.Tests/FSharp.Data.Tests.fsproj b/tests/FSharp.Data.Tests/FSharp.Data.Tests.fsproj index d757c46b2..0b65efeeb 100755 --- a/tests/FSharp.Data.Tests/FSharp.Data.Tests.fsproj +++ b/tests/FSharp.Data.Tests/FSharp.Data.Tests.fsproj @@ -10,6 +10,9 @@ true + + + PreserveNewest diff --git a/tests/FSharp.Data.Tests/Http.fs b/tests/FSharp.Data.Tests/Http.fs index cf51ccbcc..30b240e7a 100644 --- a/tests/FSharp.Data.Tests/Http.fs +++ b/tests/FSharp.Data.Tests/Http.fs @@ -8,17 +8,78 @@ open System.Net open FSharp.Data open FSharp.Data.HttpRequestHeaders open System.Text +open System.Threading.Tasks +open Microsoft.AspNetCore.Builder +open Microsoft.AspNetCore.Http +open System.Net.NetworkInformation + +type ITestHttpServer = + inherit IDisposable + abstract member BaseAddress: string + abstract member WorkerTask: Task + +let startHttpLocalServer() = + let app = WebApplication.CreateBuilder().Build() + app.Map("/{status}", (fun (ctx: HttpContext) -> + async { + match ctx.Request.RouteValues.TryGetValue("status") with + | true, (:? string as status) -> + let status = status |> int + + match ctx.Request.Query.TryGetValue("sleep") with + | true, values when values.Count = 1 -> + let value = values[0] |> int + do! Async.Sleep value + | _ -> () + + if ctx.Request.Body <> null then + let buffer = Array.create 8192 (byte 0) + let mutable read = -1 + while read <> 0 do + let! x = ctx.Request.Body.ReadAsync(buffer, 0, 8192) |> Async.AwaitTask + read <- x + + Results.StatusCode(status).ExecuteAsync(ctx) + |> Async.AwaitTask + |> ignore + | _ -> failwith "Unexpected request." + + } |> Async.StartAsTask :> Task + )) |> ignore + + let freePort = + let mutable port = 55555 // base listener port for the tests + while + IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners() + |> Array.map (fun x -> x.Port) + |> Array.contains port do + port <- port + 1 + port + + let baseAddress = $"http://localhost:{freePort}" + + let workerTask = app.RunAsync(baseAddress) + printfn $"Started local http server with address {baseAddress}" + + { new ITestHttpServer with + member this.Dispose() = + app.StopAsync() |> Async.AwaitTask |> ignore + printfn $"Stopped local http server with address {baseAddress}" + member this.WorkerTask = workerTask + member this.BaseAddress = baseAddress } [] let ``Don't throw exceptions on http error`` () = - let response = Http.Request("http://httpstat.us/401", silentHttpErrors = true) + use localServer = startHttpLocalServer() + let response = Http.Request(localServer.BaseAddress + "/401", silentHttpErrors = true) response.StatusCode |> should equal 401 [] let ``Throw exceptions on http error`` () = + use localServer = startHttpLocalServer() let exceptionThrown = try - Http.RequestString("http://api.themoviedb.org/3/search/movie") |> ignore + Http.RequestString(localServer.BaseAddress + "/401") |> ignore false with e -> true @@ -129,20 +190,25 @@ let ``Cookies is not added in cookieContainer but is still returned when addCook [] let ``Web request's timeout is used`` () = + use localServer = startHttpLocalServer() let exc = Assert.Throws (fun () -> - Http.Request("http://httpstat.us/200?sleep=1000", customizeHttpRequest = (fun req -> req.Timeout <- 1; req)) |> ignore) - Assert.AreEqual(typeof, exc.InnerException.GetType()) + Http.Request(localServer.BaseAddress + "/200?sleep=1000", customizeHttpRequest = (fun req -> req.Timeout <- 1; req)) |> ignore) + + exc.Status |> should equal WebExceptionStatus.Timeout [] let ``Timeout argument is used`` () = + use localServer = startHttpLocalServer() let exc = Assert.Throws (fun () -> - Http.Request("http://httpstat.us/200?sleep=1000", timeout = 1) |> ignore) - Assert.AreEqual(typeof, exc.InnerException.GetType()) + Http.Request(localServer.BaseAddress + "/200?sleep=1000", timeout = 1) |> ignore) + + exc.Status |> should equal WebExceptionStatus.Timeout [] let ``Setting timeout in customizeHttpRequest overrides timeout argument`` () = + use localServer = startHttpLocalServer() let response = - Http.Request("http://httpstat.us/401?sleep=1000", silentHttpErrors = true, + Http.Request(localServer.BaseAddress + "/401?sleep=1000", silentHttpErrors = true, customizeHttpRequest = (fun req -> req.Timeout <- Threading.Timeout.Infinite; req), timeout = 1) response.StatusCode |> should equal 401 @@ -156,18 +222,20 @@ let testFormDataSizesInBytes = [ ] [] -let testFormDataBodySize (size: int) = +let testFormDataBodySize (size: int) = + use localServer = startHttpLocalServer() let bodyString = seq {for i in 0..size -> "x\n"} |> String.concat "" let body = FormValues([("input", bodyString)]) - Assert.DoesNotThrowAsync(fun () -> Http.AsyncRequest (url="http://httpstat.us/200", httpMethod="POST", body=body, timeout = 10000) |> Async.Ignore |> Async.StartAsTask :> _) + Assert.DoesNotThrowAsync(fun () -> Http.AsyncRequest (url= localServer.BaseAddress + "/200", httpMethod="POST", body=body, timeout = 10000) |> Async.Ignore |> Async.StartAsTask :> _) [] -let testMultipartFormDataBodySize (size: int) = +let testMultipartFormDataBodySize (size: int) = + use localServer = startHttpLocalServer() let bodyString = seq {for i in 0..size -> "x\n"} |> String.concat "" let multipartItem = [ MultipartItem("input", "input.txt", new MemoryStream(Encoding.UTF8.GetBytes(bodyString)) :> Stream) ] let body = Multipart(Guid.NewGuid().ToString(), multipartItem) - Assert.DoesNotThrowAsync(fun () -> Http.AsyncRequest (url="http://httpstat.us/200", httpMethod="POST", body=body, timeout = 10000) |> Async.Ignore |> Async.StartAsTask :> _) + Assert.DoesNotThrowAsync(fun () -> Http.AsyncRequest (url= localServer.BaseAddress + "/200", httpMethod="POST", body=body, timeout = 10000) |> Async.Ignore |> Async.StartAsTask :> _) [] let ``escaping of url parameters`` () =