Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .fantomasignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
samples
samples/**
43 changes: 43 additions & 0 deletions samples/web/Web.Sample.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{969848AF-660B-4C8F-A732-86958A0A3F9D}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Web.Sample", "src\Web.Sample\Web.Sample.fsproj", "{09C4FAA7-792D-48F0-9AD0-77D99B5B337D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D3FA0092-6D80-4228-A143-E35105DAD823}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Web.Sample.Test", "test\Web.Sample.Test\Web.Sample.Test.fsproj", "{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Sample.Csharp.Test", "test\Web.Sample.Csharp.Test\Web.Sample.Csharp.Test.csproj", "{F9662931-A238-4FAD-81D9-50D81885EC55}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{09C4FAA7-792D-48F0-9AD0-77D99B5B337D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09C4FAA7-792D-48F0-9AD0-77D99B5B337D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09C4FAA7-792D-48F0-9AD0-77D99B5B337D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09C4FAA7-792D-48F0-9AD0-77D99B5B337D}.Release|Any CPU.Build.0 = Release|Any CPU
{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569}.Release|Any CPU.Build.0 = Release|Any CPU
{F9662931-A238-4FAD-81D9-50D81885EC55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9662931-A238-4FAD-81D9-50D81885EC55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9662931-A238-4FAD-81D9-50D81885EC55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9662931-A238-4FAD-81D9-50D81885EC55}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{09C4FAA7-792D-48F0-9AD0-77D99B5B337D} = {969848AF-660B-4C8F-A732-86958A0A3F9D}
{16F1F95C-D971-4F35-B1BA-D0CD6A7F5569} = {D3FA0092-6D80-4228-A143-E35105DAD823}
{F9662931-A238-4FAD-81D9-50D81885EC55} = {D3FA0092-6D80-4228-A143-E35105DAD823}
EndGlobalSection
EndGlobal
83 changes: 83 additions & 0 deletions samples/web/src/Web.Sample/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
namespace Web.Sample

#nowarn 20 // to avoid |> ignore everywhere in aspnet config files

open System
open Microsoft.AspNetCore.Builder
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.DependencyInjection
open System.Threading.Tasks
open Microsoft.AspNetCore.Http

module Clients =
open System.Net.Http
open System.Net.Http.Json

module Routes =
let name = "/hello/name"
let age = "/hello/age"

let ``localhost:5000`` (httpClient: HttpClient) =
httpClient.BaseAddress <- "http://localhost" |> Uri

type ClientOne(httpClient: HttpClient) =
member this.GetNameAsync() =
httpClient.GetFromJsonAsync<{| Name: string |}>(Routes.name)

type ClientTwo(httpClient: HttpClient) =
member this.GetAgeAsync() =
httpClient.GetFromJsonAsync<{| Age: int |}>(Routes.age)


module Services =
open Clients

let routeOne = "/service-one"

type ServiceOne(clientOne: ClientOne, clientTwo: ClientTwo) =
member this.GetAndPrintAsync() =
task {
let! name = clientOne.GetNameAsync()
let! age = clientTwo.GetAgeAsync()

return $"name: {name}, age:{age}"
}

// IMPORTANT: needed for WebApplicationFactory<T>
type Program() = class end

// entry point is allowed only in let function bindings, so we need to also have this
module Program =

[<EntryPoint>]
let main args =

let builder =
WebApplication.CreateBuilder(args)
|> fun x ->
x.Services.AddHttpClient<Clients.ClientOne>(Clients.``localhost:5000``)
x.Services.AddHttpClient<Clients.ClientTwo>(Clients.``localhost:5000``)
x.Services.AddTransient<Services.ServiceOne>()
x

let app = builder.Build()

// our app service is invoked in this route
app.MapPost(Services.routeOne, Func<HttpContext, _>(fun c ->
task {
let s = c.RequestServices.GetRequiredService<Services.ServiceOne>()

let! r = s.GetAndPrintAsync()

return r
})
)

// test api client against these endpoints to avoid extra server hosting in app / containers etc
app.MapGet("/hello/name", Func<{| Name: string |}>(fun () -> {| Name = "john" |}))
app.MapGet("/hello/age", Func<{| Age: int |}>(fun () -> {| Age = 25 |}))

app.Run()

0 // Exit code

23 changes: 23 additions & 0 deletions samples/web/src/Web.Sample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5003",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7222;http://localhost:5003",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
12 changes: 12 additions & 0 deletions samples/web/src/Web.Sample/Web.Sample.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
<!-- <InternalsVisibleTo Include="../../test/Web.Sample.Test" /> not working in F# -->
</ItemGroup>

</Project>
8 changes: 8 additions & 0 deletions samples/web/src/Web.Sample/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions samples/web/src/Web.Sample/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
41 changes: 41 additions & 0 deletions samples/web/test/Web.Sample.Csharp.Test/CSharpTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Web.Sample.Csharp.Test;

using System;
using Xunit;
using ApiStub.FSharp;
using System.Threading.Tasks;
using static ApiStub.FSharp.CE;
using Web.Sample;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;

public class CSharpTests
{
private static WebApplicationFactory<Web.Sample.Program> getWebAppFactory()
{
// create an instance of the test client builder
var b = new TestClient<Web.Sample.Program>();

return
b.GetJson(b, Clients.Routes.name, new { Name = "Peter" })
.GetJson(b, Clients.Routes.age, new { Age = 100 })
.GetFactory();
}

// one app factory instance is oke for all tests
private static readonly WebApplicationFactory<Program> webAppFactory = getWebAppFactory();

[Fact]
public async Task CsharpTest_Peter_is_100_years_old()
{
var client = webAppFactory.CreateClient();

var response = await client.PostAsJsonAsync<object>(Services.routeOne, new { });

var responseText = await response.Content.ReadAsStringAsync();

Assert.True(response.IsSuccessStatusCode);
Assert.Contains("Peter", responseText);
Assert.Contains("100", responseText);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\ApiStub.FSharp\ApiStub.FSharp.fsproj" />
<ProjectReference Include="..\..\src\Web.Sample\Web.Sample.fsproj" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions samples/web/test/Web.Sample.Csharp.Test/Web.Sample.Csharp.Test.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Sample.Csharp.Test", "Web.Sample.Csharp.Test.csproj", "{D3F1722C-8D94-E4DB-11A4-2B388C9C4387}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D3F1722C-8D94-E4DB-11A4-2B388C9C4387}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3F1722C-8D94-E4DB-11A4-2B388C9C4387}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3F1722C-8D94-E4DB-11A4-2B388C9C4387}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3F1722C-8D94-E4DB-11A4-2B388C9C4387}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C9FE6498-48D8-4424-8EEB-EFAF6CDD9104}
EndGlobalSection
EndGlobal
4 changes: 4 additions & 0 deletions samples/web/test/Web.Sample.Test/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Program

[<EntryPoint>]
let main _ = 0
30 changes: 30 additions & 0 deletions samples/web/test/Web.Sample.Test/Tests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Tests

open System
open Xunit
open ApiStub.FSharp
open System.Threading.Tasks
open ApiStub.FSharp.CE
open Web.Sample
open System.Net.Http.Json

let webAppFactory =
new TestClient<Web.Sample.Program>()
|> fun x -> x {
GETJ Web.Sample.Clients.Routes.name {| Name = "Peter" |}
GETJ Web.Sample.Clients.Routes.age {| Age = 100 |}
}
|> _.GetFactory()

[<Fact>]
let ``Peter is 100 years old`` () =
task {
let c = webAppFactory.CreateClient()

let! res = c.PostAsJsonAsync(Web.Sample.Services.routeOne, {| |})
let! responseText = res.Content.ReadAsStringAsync()

Assert.True(res.IsSuccessStatusCode)
Assert.Contains("Peter", responseText)
Assert.Contains("100", responseText)
}
26 changes: 26 additions & 0 deletions samples/web/test/Web.Sample.Test/Web.Sample.Test.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateProgramFile>false</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Tests.fs" />
<Compile Include="Program.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Web.Sample\Web.Sample.fsproj" />
<ProjectReference Include="..\..\..\..\ApiStub.FSharp\ApiStub.FSharp.fsproj" />
</ItemGroup>

</Project>
Loading