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