RestApi and System.Text.Json issue

Hello everyone,
Would like to raise an issue I face with the exercise so far.

As part of the implementation, I utilized System.Text.Json package to work with JSON. Even though all the tests are green locally on my machine, when I submit the solution, all my tests fail with the following error:

System.Text.Json.JsonException : '\' is an invalid start of a property name. Expected a '"'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
---- System.Text.Json.JsonReaderException : '\' is an invalid start of a property name. Expected a '"'. LineNumber: 0 | BytePositionInLine: 1.

The screenshot of the failed test could be found below.

My assumption so far is that something might be wrong with the platform runner, even though I’m not so familiar with the Exercism platform.

So,

  1. Does anybody have any idea what might be the source of the issue?
  2. I could possibly create a pull request myself, just not sure where to start looking for an issue.

Thanks.

Hi @rakkattakka :wave:

Welcome to the Exercism forums!

I suspect the reason for your error is that the System.Text.Json package is not installed in the F-Sharp test runner. Our test runners do not have access to the internet, so any packages not part of a core language installation have to be pre-installed into the container.

If your package is not part of core F# and does not appear on the list here, then it is not available for use in solutions on the website.

It can be added, but needs to be assessed/approved by the maintainers. Here is a bit more on the criteria they use.

cc: @ErikSchierboom

@BethanyG That should not be it, as that package is part of the core runtime so should be available. Could you show your code?

Hello @BethanyG , @ErikSchierboom ,
Thanks a lot for the quick responses. Do not have time to dive deep on the package issue, will try to do in the evening.

@ErikSchierboom System.Text.Json is not part of the core installation, this is a separate nuget package provided by Microsoft.

The full code snippet of my solution is

module RestApi

open System.Text.Json
open System.Text.Json.Serialization

type UserDocument =
    { [<JsonPropertyName("name")>]
      Name: string
      [<JsonPropertyName("owes")>]
      Owes: Map<string, decimal>
      [<JsonPropertyName("owed_by")>]
      OwedBy: Map<string, decimal>
      [<JsonPropertyName("balance")>]
      Balance: decimal }

type UsersDocument =
    { [<JsonPropertyName("users")>]
      Users: UserDocument list }

type UsersQuery =
    { [<JsonPropertyName("users")>]
      Users: string list }

type CreateUserDocument =
    { [<JsonPropertyName("user")>]
      User: string }

type CreateIOUDocument =
    { [<JsonPropertyName("lender")>]
      Lender: string
      [<JsonPropertyName("borrower")>]
      Borrower: string
      [<JsonPropertyName("amount")>]
      Amount: decimal }

type RestApi(database: string) =
    let mutable userStorage = JsonSerializer.Deserialize<UsersDocument>(database)

    let addUser createUserDocument =
        let existingUser =
            userStorage.Users
            |> List.tryFind (fun user -> user.Name = createUserDocument.User)

        match existingUser with
        | Some usr -> usr
        | None ->
            let newUser =
                { Name = createUserDocument.User
                  Owes = Map.empty
                  OwedBy = Map.empty
                  Balance = 0.0m }

            userStorage <- { Users = newUser :: userStorage.Users }
            newUser

    let findUsersByNames names : UsersDocument =
        let matchingUsers =
            userStorage.Users
            |> List.filter (fun user -> List.contains user.Name names)
            |> List.sortBy _.Name

        { Users = matchingUsers }

    let lendAmountTo lender borrower amount =
        let lenderToBorrower = Map.tryFind borrower.Name lender.Owes

        match lenderToBorrower with
        | Some lenderToBorrowerDebt when lenderToBorrowerDebt < amount ->
            let lenderOwes = Map.remove borrower.Name lender.Owes

            let lenderOwedBy =
                Map.add borrower.Name (amount - lenderToBorrowerDebt) lender.OwedBy

            let lenderBalance = lender.Balance + amount

            let updatedLender =
                { lender with
                    Owes = lenderOwes
                    OwedBy = lenderOwedBy
                    Balance = lenderBalance }

            let borrowerOwes =
                Map.add lender.Name (amount - lenderToBorrowerDebt) borrower.OwedBy

            let borrowerOwedBy = Map.remove lender.Name borrower.OwedBy
            let borrowerBalance = borrower.Balance - amount

            let updatedBorrower =
                { borrower with
                    Owes = borrowerOwes
                    OwedBy = borrowerOwedBy
                    Balance = borrowerBalance }

            (updatedLender, updatedBorrower)

        | Some lenderToBorrowerDebt when lenderToBorrowerDebt > amount ->
            let lenderOwes = Map.add borrower.Name (lenderToBorrowerDebt - amount) lender.OwedBy
            let lenderBalance = lender.Balance + amount

            let updatedLender =
                { lender with
                    Owes = lenderOwes
                    Balance = lenderBalance }

            let borrowerOwedBy =
                Map.add lender.Name (lenderToBorrowerDebt - amount) borrower.OwedBy

            let borrowerBalance = borrower.Balance - amount

            let updatedBorrower =
                { borrower with
                    OwedBy = borrowerOwedBy
                    Balance = borrowerBalance }

            (updatedLender, updatedBorrower)

        | Some lenderToBorrowerDebt when lenderToBorrowerDebt = amount ->
            let lenderOwes = Map.remove borrower.Name lender.Owes
            let lenderBalance = lender.Balance + amount

            let updatedLender =
                { lender with
                    Owes = lenderOwes
                    Balance = lenderBalance }

            let borrowerOwedBy = Map.remove lender.Name borrower.OwedBy
            let borrowerBalance = borrower.Balance - amount

            let updatedBorrower =
                { borrower with
                    OwedBy = borrowerOwedBy
                    Balance = borrowerBalance }

            (updatedLender, updatedBorrower)

        | None ->
            let lenderOwedBy = Map.add borrower.Name amount lender.OwedBy
            let lenderBalance = lender.Balance + amount

            let updatedLender =
                { lender with
                    OwedBy = lenderOwedBy
                    Balance = lenderBalance }

            let borrowerOwes = Map.add lender.Name amount borrower.Owes
            let borrowerBalance = borrower.Balance - amount

            let updatedBorrower =
                { borrower with
                    Owes = borrowerOwes
                    Balance = borrowerBalance }

            (updatedLender, updatedBorrower)
        
        | _ ->
            failwith "Case is not supported"

    let executeIOU createIOUDocument : UsersDocument =
        let lender =
            userStorage.Users
            |> List.find (fun user -> user.Name = createIOUDocument.Lender)

        let borrower =
            userStorage.Users
            |> List.find (fun user -> user.Name = createIOUDocument.Borrower)

        let updatedLender, updatedBorrower =
            lendAmountTo lender borrower createIOUDocument.Amount

        let newUsers =
            userStorage.Users
            |> List.filter (fun user -> user.Name <> lender.Name && user.Name <> borrower.Name)
            |> List.append [ updatedLender; updatedBorrower ]

        userStorage <- { userStorage with Users = newUsers }

        { Users = [ updatedLender; updatedBorrower ] |> List.sortBy _.Name }

    member this.Get(url: string) =
        match url with
        | "/users" -> JsonSerializer.Serialize(userStorage)
        | _ -> System.String.Empty

    member this.Get(url: string, payload: string) =
        match url with
        | "/users" ->
            let usersQuery = JsonSerializer.Deserialize<UsersQuery>(payload)
            let users = findUsersByNames usersQuery.Users
            JsonSerializer.Serialize(users)
        | _ -> System.String.Empty

    member this.Post(url: string, payload: string) =
        match url with
        | "/add" ->
            let createUserDocument = JsonSerializer.Deserialize<CreateUserDocument>(payload)
            let user = addUser createUserDocument
            JsonSerializer.Serialize(user)
        | "/iou" ->
            let createIOUDocument = JsonSerializer.Deserialize<CreateIOUDocument>(payload)
            let users = executeIOU createIOUDocument
            JsonSerializer.Serialize(users)
        | _ -> System.String.Empty

Regarding .fsproj file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="RestApi.fs" />
    <Compile Include="RestApiTests.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
    <PackageReference Include="System.Text.Json" Version="8.0.5" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
    <PackageReference Include="FsUnit.xUnit" Version="4.0.4" />
  </ItemGroup>

</Project>

So as you can see, I installed System.Text.Json separately.

As I mentioned, all tests are green locally. Here is the Rider test launch:

Thank you.

And if you remove the System.Text.Json package reference , you get an error?

Hey @ErikSchierboom ,
My bad here. It is included in the stdlib. So it works perfectly fine when I remove the explicit reference to the nuget package.
I could not submit the exercise, though. It seems that exercism does not send .fsproj files.
The error is

exercism submit
Error: No files you submitted have changed since your last submissio

@BethanyG @ErikSchierboom Ok, I cloned the test-runner GitHub - exercism/fsharp-test-runner.
The attempts to launch run-in-docker.ps failed on my Apple Silicon mac due to the issues with the image architecture. With no changes build hangs on RUN dotnet new console, with architecture changes it fails with rosetta error: failed to open elf at /lib/ld-musl-x86_64.so.1.

The launch of run.sh resulted in the same error I see on the platform:

    {
      "name": "No users",
      "status": "fail",
      "message": "System.Text.Json.JsonException : \u0027\\\u0027 is an invalid start of a property name. Expected a \u0027\u0022\u0027. Path: $ | LineNumber: 0 | BytePositionInLine: 1.\n---- System.Text.Json.JsonReaderException : \u0027\\\u0027 is an invalid start of a property name. Expected a \u0027\u0022\u0027. LineNumber: 0 | BytePositionInLine: 1.",
      "test_code": "let database = \u0022\u0022\u0022{\\\u0022users\\\u0022:[]}\u0022\u0022\u0022\nlet url = \u0022/users\u0022\nlet expected = \u0022\u0022\u0022{\\\u0022users\\\u0022:[]}\u0022\u0022\u0022\nlet api = RestApi(database)\napi.Get url |\u003E should equal expected"
    },

So, most likely the issue sits somewhere inside the runner. I could not attach the entire content here, so passing the link to the test output:

Ok, spent some time debugging the runner itself.

It seems that the issue Rewrite.fs file, fsharp-test-runner/src/Exercism.TestRunner.FSharp/Rewrite.fs at main · exercism/fsharp-test-runner · GitHub this line more precisely.
After the rewrite happens, the output of the test code is the following. So every " inside the string is escaped with additional \, that makes the json invalid.

And all tests start to fail with the error about the incorrect json format.

Here is the Rider output for one of the tests:

System.Text.Json.JsonException: '\' is an invalid start of a property name. Expected a '"'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.

System.Text.Json.JsonException
'\' is an invalid start of a property name. Expected a '"'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
   at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, JsonReaderException ex)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)
   at RestApi.RestApi..ctor(String database) in /Users/Rakkatakka/Exercism/fsharp/rest-api/RestApi.fs:line 37
   at RestApiTests.Add user() in /Users/Rakkatakka/Exercism/fsharp/rest-api/RestApiTests.fs:line 24
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

System.Text.Json.JsonReaderException
'\' is an invalid start of a property name. Expected a '"'. LineNumber: 0 | BytePositionInLine: 1.
   at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
   at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
   at System.Text.Json.Utf8JsonReader.Read()
   at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)

And here is the entire malformed test file.

module RestApiTests

open FsUnit.Xunit
open Xunit
open RestApi

[<Fact>]
let ``No users`` () =
    let database = """{\\"users\\":[]}"""
    let url = "/users"
    let expected = """{\\"users\\":[]}"""
    let api = RestApi(database)
    api.Get url |> should equal expected

[<Fact>]
let ``Add user`` () =
    let database = """{\\"users\\":[]}"""
    let payload = """{\\"user\\":\\"Adam\\"}"""
    let url = "/add"

    let expected =
        """{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Get single user`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0}]}"""

    let payload = """{\\"users\\":[\\"Bob\\"]}"""
    let url = "/users"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0}]}"""

    let api = RestApi(database)
    api.Get(url, payload) |> should equal expected

[<Fact>]
let ``Both users have 0 balance`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0}]}"""

    let payload =
        """{\\"lender\\":\\"Adam\\",\\"borrower\\":\\"Bob\\",\\"amount\\":3.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{\\"Bob\\":3.0},\\"balance\\":3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Adam\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Borrower has negative balance`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Chuck\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Chuck\\",\\"owes\\":{},\\"owed_by\\":{\\"Bob\\":3.0},\\"balance\\":3.0}]}"""

    let payload =
        """{\\"lender\\":\\"Adam\\",\\"borrower\\":\\"Bob\\",\\"amount\\":3.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{\\"Bob\\":3.0},\\"balance\\":3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Adam\\":3.0,\\"Chuck\\":3.0},\\"owed_by\\":{},\\"balance\\":-6.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Lender has negative balance`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Chuck\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Chuck\\",\\"owes\\":{},\\"owed_by\\":{\\"Bob\\":3.0},\\"balance\\":3.0}]}"""

    let payload =
        """{\\"lender\\":\\"Bob\\",\\"borrower\\":\\"Adam\\",\\"amount\\":3.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{\\"Bob\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Chuck\\":3.0},\\"owed_by\\":{\\"Adam\\":3.0},\\"balance\\":0.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Lender owes borrower`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{\\"Bob\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{\\"Adam\\":3.0},\\"balance\\":3.0}]}"""

    let payload =
        """{\\"lender\\":\\"Adam\\",\\"borrower\\":\\"Bob\\",\\"amount\\":2.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{\\"Bob\\":1.0},\\"owed_by\\":{},\\"balance\\":-1.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{\\"Adam\\":1.0},\\"balance\\":1.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Lender owes borrower less than new loan`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{\\"Bob\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{\\"Adam\\":3.0},\\"balance\\":3.0}]}"""

    let payload =
        """{\\"lender\\":\\"Adam\\",\\"borrower\\":\\"Bob\\",\\"amount\\":4.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{\\"Bob\\":1.0},\\"balance\\":1.0},{\\"name\\":\\"Bob\\",\\"owes\\":{\\"Adam\\":1.0},\\"owed_by\\":{},\\"balance\\":-1.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact>]
let ``Lender owes borrower same as new loan`` () =
    let database =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{\\"Bob\\":3.0},\\"owed_by\\":{},\\"balance\\":-3.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{\\"Adam\\":3.0},\\"balance\\":3.0}]}"""

    let payload =
        """{\\"lender\\":\\"Adam\\",\\"borrower\\":\\"Bob\\",\\"amount\\":3.0}"""

    let url = "/iou"

    let expected =
        """{\\"users\\":[{\\"name\\":\\"Adam\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0},{\\"name\\":\\"Bob\\",\\"owes\\":{},\\"owed_by\\":{},\\"balance\\":0.0}]}"""

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

@BethanyG @ErikSchierboom One more thing.

I changed the test file locally so instead of triple-quoted strings """ for json it now relies on a single quoted ones ", launched the runner locally and all tests passed.

Even though this does not address the issue with code rewrite, it might be considered as a quick fix to address the failure we see now. I could create a PR for the fsharp/exercises/practice/rest-api/RestApiTests.fs at main · exercism/fsharp · GitHub if you are fine with this.

Here is the full test file content:

module RestApiTests

open FsUnit.Xunit
open Xunit
open RestApi

[<Fact>]
let ``No users`` () =
    let database = "{\"users\":[]}"
    let url = "/users"
    let expected = "{\"users\":[]}"
    let api = RestApi(database)
    api.Get url |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Add user`` () =
    let database = "{\"users\":[]}"
    let payload = "{\"user\":\"Adam\"}"
    let url = "/add"
    let expected = "{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0}"
    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Get single user`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{},\"balance\":0.0}]}"

    let payload = "{\"users\":[\"Bob\"]}"
    let url = "/users"

    let expected =
        "{\"users\":[{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{},\"balance\":0.0}]}"

    let api = RestApi(database)
    api.Get(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Both users have 0 balance`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{},\"balance\":0.0}]}"

    let payload = "{\"lender\":\"Adam\",\"borrower\":\"Bob\",\"amount\":3.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{\"Bob\":3.0},\"balance\":3.0},{\"name\":\"Bob\",\"owes\":{\"Adam\":3.0},\"owed_by\":{},\"balance\":-3.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Borrower has negative balance`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0},{\"name\":\"Bob\",\"owes\":{\"Chuck\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Chuck\",\"owes\":{},\"owed_by\":{\"Bob\":3.0},\"balance\":3.0}]}"

    let payload = "{\"lender\":\"Adam\",\"borrower\":\"Bob\",\"amount\":3.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{\"Bob\":3.0},\"balance\":3.0},{\"name\":\"Bob\",\"owes\":{\"Adam\":3.0,\"Chuck\":3.0},\"owed_by\":{},\"balance\":-6.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Lender has negative balance`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0},{\"name\":\"Bob\",\"owes\":{\"Chuck\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Chuck\",\"owes\":{},\"owed_by\":{\"Bob\":3.0},\"balance\":3.0}]}"

    let payload = "{\"lender\":\"Bob\",\"borrower\":\"Adam\",\"amount\":3.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{\"Bob\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Bob\",\"owes\":{\"Chuck\":3.0},\"owed_by\":{\"Adam\":3.0},\"balance\":0.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Lender owes borrower`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{\"Bob\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{\"Adam\":3.0},\"balance\":3.0}]}"

    let payload = "{\"lender\":\"Adam\",\"borrower\":\"Bob\",\"amount\":2.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{\"Bob\":1.0},\"owed_by\":{},\"balance\":-1.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{\"Adam\":1.0},\"balance\":1.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Lender owes borrower less than new loan`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{\"Bob\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{\"Adam\":3.0},\"balance\":3.0}]}"

    let payload = "{\"lender\":\"Adam\",\"borrower\":\"Bob\",\"amount\":4.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{\"Bob\":1.0},\"balance\":1.0},{\"name\":\"Bob\",\"owes\":{\"Adam\":1.0},\"owed_by\":{},\"balance\":-1.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected

[<Fact(Skip = "Remove this Skip property to run this test")>]
let ``Lender owes borrower same as new loan`` () =
    let database =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{\"Bob\":3.0},\"owed_by\":{},\"balance\":-3.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{\"Adam\":3.0},\"balance\":3.0}]}"

    let payload = "{\"lender\":\"Adam\",\"borrower\":\"Bob\",\"amount\":3.0}"
    let url = "/iou"

    let expected =
        "{\"users\":[{\"name\":\"Adam\",\"owes\":{},\"owed_by\":{},\"balance\":0.0},{\"name\":\"Bob\",\"owes\":{},\"owed_by\":{},\"balance\":0.0}]}"

    let api = RestApi(database)
    api.Post(url, payload) |> should equal expected
1 Like

That’s not really an issue though as we run the image as x86-64 images.

Ah, that is most unfortunate. Could you open an issue?

Hello @ErikSchierboom ,
Here is the issue Test runner Rewrite operation does not handle triple-quoted strings correctly · Issue #272 · exercism/fsharp-test-runner · GitHub. It was automatically closed by the automation, though.

Does it makes sense to create a PR for fsharp/exercises/practice/rest-api/RestApiTests.fs at main · exercism/fsharp · GitHub as a quick fix or the issue will wait for the proper fix?

Thanks.

1 Like

I think this would be great.

Hello @ErikSchierboom ,
Here is the pull-request with the quick fix - rest-api: replace triple-quote strings with single-quote ones to mitigate the issue with the test runner by RamanBut-Husaim · Pull Request #1308 · exercism/fsharp · GitHub.
Thanks.

1 Like

Hello everyone,
The PR rest-api: replace triple-quote strings with single-quote ones to mitigate the issue with the test runner by RamanBut-Husaim · Pull Request #1308 · exercism/fsharp · GitHub has been merged. The tests for RestApi exercise are passing on the exercism runners.
Thanks.

2 Likes