Source
https://forum.exercism.org/t/restapi-and-system-text-json-issue/15946
Description
The issue has been initially identified as part of the work on rest-api exercise for F# language. All tests are green locally, however exercism platform test runner considers all tests as failed 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 local debug showed that the issue is produced by the Rewrite.fs file. After the rewrite, the output of the test code has every " inside the string escaped with additional \, that makes the json inside the triple-quoted string invalid.
Here is the full error output of the test in JetBrains Rider:
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)
The entire content of the malformed file is:
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
Quick Fix
The replacement of triple-quoted strings """ with a single quote ones " in the test file helps to address the issue. Here is the example of a test file that successfully passes the rewrite:
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
Source
https://forum.exercism.org/t/restapi-and-system-text-json-issue/15946
Description
The issue has been initially identified as part of the work on
rest-apiexercise for F# language. All tests are green locally, howeverexercismplatform test runner considers all tests as failed with the following error:The local debug showed that the issue is produced by the
Rewrite.fsfile. After the rewrite, the output of the test code has every"inside the string escaped with additional\, that makes the json inside the triple-quoted string invalid.Here is the full error output of the test in JetBrains Rider:
The entire content of the malformed file is:
Quick Fix
The replacement of triple-quoted strings
"""with a single quote ones"in the test file helps to address the issue. Here is the example of a test file that successfully passes the rewrite: