Skip to content

Commit

Permalink
Add SomeNullHandlingTests
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed May 25, 2020
1 parent 11d9e32 commit d01be6c
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<Compile Include="PicklerTests.fs" />
<Compile Include="UnionConverterTests.fs" />
<Compile Include="VerbatimUtf8ConverterTests.fs" />
<Compile Include="SomeNullHandlingTests.fs" />
</ItemGroup>

<ItemGroup>
Expand Down
55 changes: 55 additions & 0 deletions tests/FsCodec.NewtonsoftJson.Tests/SomeNullHandlingTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module FsCodec.NewtonsoftJson.Tests.SomeNullHandlingTests

open FsCodec.NewtonsoftJson
open Swensen.Unquote
open Xunit

let def = Settings.CreateDefault()

let [<Fact>] ``Settings.CreateDefault roundtrips null string option, but rendering is ugly`` () =
let value : string option = Some null
let ser = Serdes.Serialize(value, def)
test <@ ser = "{\"Case\":\"Some\",\"Fields\":[null]}" @>
test <@ value = Serdes.Deserialize(ser, def) @>

let [<Fact>] ``Settings.Create does not roundtrip Some null`` () =
let value : string option = Some null
let ser = Serdes.Serialize value
"null" =! ser
// But it doesn't roundtrip
value <>! Serdes.Deserialize ser

let hasSomeNull value = TypeShape.Generic.exists(fun (x : string option) -> x = Some null) value
let replaceSomeNullsWithNone value = TypeShape.Generic.map (function Some (null : string) -> None | x -> x) value

let [<Fact>] ``Workaround is to detect and/or substitute such non-roundtrippable values`` () =

let value : string option = Some null
// So we detect the condition (we could e.g. exclude such cases from the tests)
test <@ hasSomeNull value @>
// Or we can plough on, replacing the input with a roundtrippable value
let value : string option = replaceSomeNullsWithNone value
None =! value
test <@ (not << hasSomeNull) value @>
let ser = Serdes.Serialize value
ser =! "null"
// ... and validate that the [substituted] value did roundtrip
test <@ value = Serdes.Deserialize ser @>

type RecordWithStringOptions = { x : int; y : Nested }
and Nested = { z : string option }

let [<Fact>] ``Can detect and/or substitute null string option when using Settings.Create`` () =
let value : RecordWithStringOptions = { x = 9; y = { z = Some null } }
test <@ hasSomeNull value @>
let value = replaceSomeNullsWithNone value
test <@ (not << hasSomeNull) value @>
let ser = Serdes.Serialize value
ser =! """{"x":9,"y":{"z":null}}"""
test <@ value = Serdes.Deserialize ser @>

// As one might expect, the ignoreNulls setting is also honored
let ignoreNullsSettings = Settings.Create(ignoreNulls=true)
let ser = Serdes.Serialize(value,ignoreNullsSettings)
ser =! """{"x":9,"y":{}}"""
test <@ value = Serdes.Deserialize ser @>
20 changes: 9 additions & 11 deletions tests/FsCodec.NewtonsoftJson.Tests/UnionConverterTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ open global.Xunit

// TODO support [<Struct>]
type TestRecordPayload =
{
test: string
{ test: string
}

// TODO support [<Struct>]
type TrickyRecordPayload =
{
Item: string
{ Item: string
}

[<JsonConverter(typeof<TypeSafeEnumConverter>)>]
Expand Down Expand Up @@ -272,14 +270,14 @@ let roundtripProperty ignoreNulls (profile : JsonSerializerSettings) value =
deserialized =! value

let includeNullsProfile = Settings.CreateDefault(OptionConverter())
[<DomainPropertyAttribute(MaxTest=1000)>]
[<DomainProperty(MaxTest=1000)>]
let ``UnionConverter ignoreNulls Profile roundtrip property test`` (x: TestDU) =
let ignoreNulls, profile = false, includeNullsProfile
profile.NullValueHandling =! NullValueHandling.Include
roundtripProperty ignoreNulls profile x

let defaultProfile = Settings.Create()
[<DomainPropertyAttribute(MaxTest=1000)>]
[<DomainProperty(MaxTest=1000)>]
let ``UnionConverter opinionated Profile roundtrip property test`` (x: TestDU) =
let ignoreNulls, profile = false, defaultProfile
profile.NullValueHandling =! NullValueHandling.Include
Expand Down Expand Up @@ -311,8 +309,8 @@ module ``Unmatched case handling`` =

[<JsonConverter(typeof<UnionConverter>, "case", "Catchall")>]
type DuWithCatchAll =
| Known
| Catchall
| Known
| Catchall

[<Fact>]
let ``UnionConverter supports a nominated catchall`` () =
Expand All @@ -323,7 +321,7 @@ module ``Unmatched case handling`` =

[<JsonConverter(typeof<UnionConverter>, "case", "CatchAllThatCantBeFound")>]
type DuWithMissingCatchAll =
| Known
| Known

[<Fact>]
let ``UnionConverter explains if nominated catchAll not found`` () =
Expand All @@ -336,8 +334,8 @@ module ``Unmatched case handling`` =
[<NoComparison>] // Forced by usage of JObject
[<JsonConverter(typeof<UnionConverter>, "case", "Catchall")>]
type DuWithCatchAllWithFields =
| Known
| Catchall of Newtonsoft.Json.Linq.JObject
| Known
| Catchall of Newtonsoft.Json.Linq.JObject

[<Fact>]
let ``UnionConverter can feed unknown values into a JObject for logging or post processing`` () =
Expand Down

0 comments on commit d01be6c

Please sign in to comment.