forked from microsoft/fsharplu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
JsonTests.fs
389 lines (346 loc) · 22 KB
/
JsonTests.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
module Microsoft.FSharpLu.Json.Tests
open Microsoft.VisualStudio.TestTools.UnitTesting
open FsCheck
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
type WithFields = SomeField of int * int
type SimpleDu = Foo | FooBar | Bar
type ComplexDu = ComplexDu of WithFields | SimpleDU | AString of string
type 'a RecursiveList = RecListLeaf of 'a | RecListCons of 'a RecursiveList
type OptionOfBase = int option
type OptionOfDu = SimpleDu option
type Color = Red | Blue
type Shape = Circle of int * int | Rectangle
type 'a Tree = Leaf of 'a | Node of 'a Tree * 'a Tree
type 'a Test = Case1 | Case2 of int | Case3 of int * string * 'a
type MapType = Map<string,Color>
type 'a NestedOptions = 'a option option option option
type ConsecutiveUppercaseRecord = { BAR : int ; BAZNumber : int }
type ConsecutiveUppercaseDu = FOO | FOOWithRecord of ConsecutiveUppercaseRecord
type 'a Wrapper = { WrappedField : 'a }
type NestedStructure = { subField : int }
type NestedOptionStructure = { field : NestedStructure option }
type SomeTupleType = int * string list * int * int64 * OptionOfDu * Color * int Tree
/// Test cases for possible ambiguity between option types and other DU or records with a 'Some' field.
module SomeAmbiguity =
type 'a RecordWithFieldNamedSome = { Some : 'a }
type DUWithFieldlessCaseNamedSome = Some of string | Bla
type DUWithCaseWithFieldNamedSome = Some | Bla
type 'a Ambiguous1 = 'a RecordWithFieldNamedSome option
type Ambiguous2 = DUWithFieldlessCaseNamedSome option
type Ambiguous3 = DUWithCaseWithFieldNamedSome option
let inline defaultSerialize< ^T> (x: ^T) = Compact.serialize< ^T> x
let inline reciprocal< ^T when ^T:equality> (serialize: ^T->string) (deserialize: string-> ^T) (x: ^T) =
// theoretically one round trip is sufficient; we perform
// two round trips here to test for possible side-effects
x |> serialize |> deserialize |> serialize |> deserialize = x
let inline canBeSerialized< ^T when ^T:equality> (serialize: ^T->string) (deserialize: string-> ^T) (x: ^T) =
serialize x |> printfn "%A"
let inline areReciprocal< ^T when ^T:equality> (serialize: ^T->string) (deserialize: string-> ^T) (x: ^T) =
let s = x |> serialize
let sds = s |> deserialize |> serialize
Assert.AreEqual(s, sds, sprintf "Inconsistent serialization: 1st call: <%s> 2nd call <%s>. Type %A" s sds (typeof< ^T>))
let sdsd = sds |> deserialize
Assert.AreEqual(sdsd, x, sprintf "Did not get the same object back: <%A> gave back <%A> for type %A" x sdsd (typeof< ^T>))
/// Check that given object serializes to the specified Json string (using default serializer)
let inline serializedAs json o =
let s = defaultSerialize o
Assert.AreEqual(json, s, sprintf "Object was not serialized to the expected format")
/// Check that deserialization coincides with NewtonSoft's default serializer.
/// That is: when the Json is deserializable by both deserializers Union and Default
/// they produce the same output object.
let inline coincidesWithDefault< ^T when ^T:equality> (x: ^T) =
let deserializationMustCoincide json =
match Default.tryDeserialize< ^T> json, Compact.tryDeserialize< ^T> json with
| Choice2Of2 error1, Choice2Of2 error2->
Assert.IsTrue(true, "Json not parseable by either deserializer: no ambiguity")
| Choice1Of2 _, Choice2Of2 error
| Choice2Of2 error, Choice1Of2 _ ->
Assert.IsTrue(true, "Json parseable by exactly one deserializer: no ambiguity")
| Choice1Of2 v1, Choice1Of2 v2 when v1 <> v2 ->
Assert.Fail(sprintf "Deserializers do not coincide: %A <> %A" v1 v2)
| Choice1Of2 v1, Choice1Of2 v2 ->
Assert.IsTrue(true)
x |> Default.serialize |> deserializationMustCoincide
x |> Compact.serialize |> deserializationMustCoincide
/// Check that output format of Default Json.Net serializer can be parsed by
/// the BackwardCompatible deserializer
let inline backwardCompatibleWithDefault< ^T when ^T:equality> (x: ^T) =
let json = x |> Default.serialize
let o1 = json |> Default.deserialize : ^T
let o2 = json |> BackwardCompatible.deserialize : ^T
Assert.AreEqual(o1, o2,
sprintf "BackwardCompatible should coincide with Json.Net when deserializing default Json format. %A <> %A" o1 o2)
type CamelCaseSettings =
static member settings =
let s =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Error,
ContractResolver = Serialization.CamelCasePropertyNamesContractResolver())
s.Converters.Add(CompactUnionJsonConverter(true))
s
static member formatting = Formatting.None
type CamelCaseSerializer = With<CamelCaseSettings>
type ReciprocalityCompact () =
static member x1 = reciprocal<ComplexDu> Compact.serialize Compact.deserialize
static member x2 = reciprocal<ComplexDu RecursiveList> Compact.serialize Compact.deserialize
static member x3 = reciprocal<WithFields> Compact.serialize Compact.deserialize
static member x4 = reciprocal<SimpleDu> Compact.serialize Compact.deserialize
static member x5 = reciprocal<ComplexDu> Compact.serialize Compact.deserialize
static member x6 = reciprocal<OptionOfBase> Compact.serialize Compact.deserialize
static member x7 = reciprocal<OptionOfDu> Compact.serialize Compact.deserialize
static member x8 = reciprocal<Color> Compact.serialize Compact.deserialize
static member x9 = reciprocal<Shape> Compact.serialize Compact.deserialize
static member x10 = reciprocal<int Tree> Compact.serialize Compact.deserialize
static member x11 = reciprocal<int Tree Test> Compact.serialize Compact.deserialize
static member x12 = reciprocal<int Test> Compact.serialize Compact.deserialize
static member x13 = reciprocal<int list Tree> Compact.serialize Compact.deserialize
static member x14 = reciprocal<string NestedOptions> Compact.serialize Compact.deserialize
static member x15 = reciprocal<string> Compact.serialize Compact.deserialize
static member x16 = reciprocal<string option> Compact.serialize Compact.deserialize
static member x17 = reciprocal<string option option> Compact.serialize Compact.deserialize
static member x18 = reciprocal<string option option option option> Compact.serialize Compact.deserialize
static member x19 = reciprocal<int NestedOptions> Compact.serialize Compact.deserialize
static member x20 = reciprocal<SomeAmbiguity.Ambiguous1<string>> Compact.serialize Compact.deserialize
static member x21 = reciprocal<SomeAmbiguity.Ambiguous1<SimpleDu>> Compact.serialize Compact.deserialize
static member x22 = reciprocal<NestedOptionStructure> Compact.serialize Compact.deserialize
static member x23 = reciprocal<SomeAmbiguity.Ambiguous2> Compact.serialize Compact.deserialize
static member x24 = reciprocal<SomeAmbiguity.Ambiguous3> Compact.serialize Compact.deserialize
static member x25 = reciprocal<int list> Compact.serialize Compact.deserialize
static member x26 = reciprocal<SomeTupleType> Compact.serialize Compact.deserialize
type ReciprocalityCamelCase () =
static member x1 = reciprocal<ComplexDu> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x2 = reciprocal<ComplexDu RecursiveList> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x3 = reciprocal<WithFields> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x4 = reciprocal<SimpleDu> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x5 = reciprocal<ComplexDu> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x6 = reciprocal<OptionOfBase> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x7 = reciprocal<OptionOfDu> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x8 = reciprocal<Color> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x9 = reciprocal<Shape> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x10 = reciprocal<int Tree> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x11 = reciprocal<int Tree Test> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x12 = reciprocal<int Test> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x13 = reciprocal<int list Tree> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x14 = reciprocal<string NestedOptions> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x15 = reciprocal<string> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x16 = reciprocal<string option> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x17 = reciprocal<string option option> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x18 = reciprocal<string option option option option> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x19 = reciprocal<int NestedOptions> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x20 = reciprocal<SomeAmbiguity.Ambiguous1<string>> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x21 = reciprocal<SomeAmbiguity.Ambiguous1<SimpleDu>> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x22 = reciprocal<NestedOptionStructure> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x23 = reciprocal<SomeAmbiguity.Ambiguous2> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x24 = reciprocal<SomeAmbiguity.Ambiguous3> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x25 = reciprocal<int list> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
static member x26 = reciprocal<SomeTupleType> CamelCaseSerializer.serialize CamelCaseSerializer.deserialize
type CoincidesWithJsonNetOnDeserialization () =
static member x1 = coincidesWithDefault<ComplexDu>
static member x2 = coincidesWithDefault<ComplexDu RecursiveList>
static member x3 = coincidesWithDefault<WithFields>
static member x4 = coincidesWithDefault<SimpleDu>
static member x5 = coincidesWithDefault<ComplexDu>
static member x6 = coincidesWithDefault<OptionOfBase>
static member x7 = coincidesWithDefault<OptionOfDu>
static member x8 = coincidesWithDefault<Color>
static member x9 = coincidesWithDefault<Shape>
static member x10 = coincidesWithDefault<int Tree>
static member x11 = coincidesWithDefault<int Tree Test>
static member x12 = coincidesWithDefault<int Test>
static member x13 = coincidesWithDefault<int list Tree>
static member x14 = coincidesWithDefault<string NestedOptions>
static member x15 = coincidesWithDefault<string>
static member x16 = coincidesWithDefault<string option>
static member x17 = coincidesWithDefault<string option option>
static member x18 = coincidesWithDefault<string option option option option>
static member x19 = coincidesWithDefault<int NestedOptions>
static member x20 = coincidesWithDefault<SomeAmbiguity.Ambiguous1<string>>
static member x21 = coincidesWithDefault<SomeAmbiguity.Ambiguous1<SimpleDu>>
static member x22 = coincidesWithDefault<NestedOptionStructure>
static member x23 = coincidesWithDefault<SomeAmbiguity.Ambiguous2>
static member x24 = coincidesWithDefault<SomeAmbiguity.Ambiguous3>
static member x25 = coincidesWithDefault<int list>
static member x26 = coincidesWithDefault<SomeTupleType>
type BackwardCompatibility () =
static member x1 = backwardCompatibleWithDefault<ComplexDu>
static member x2 = backwardCompatibleWithDefault<ComplexDu RecursiveList>
static member x3 = backwardCompatibleWithDefault<WithFields>
static member x4 = backwardCompatibleWithDefault<SimpleDu>
static member x5 = backwardCompatibleWithDefault<ComplexDu>
static member x6 = backwardCompatibleWithDefault<OptionOfBase>
static member x7 = backwardCompatibleWithDefault<OptionOfDu>
static member x8 = backwardCompatibleWithDefault<Color>
static member x9 = backwardCompatibleWithDefault<Shape>
static member x10 = backwardCompatibleWithDefault<int Tree>
static member x11 = backwardCompatibleWithDefault<int Tree Test>
static member x12 = backwardCompatibleWithDefault<int Test>
static member x13 = backwardCompatibleWithDefault<int list Tree>
static member x14 = backwardCompatibleWithDefault<string NestedOptions>
static member x15 = backwardCompatibleWithDefault<string>
static member x16 = backwardCompatibleWithDefault<string option>
static member x17 = backwardCompatibleWithDefault<string option option>
static member x18 = backwardCompatibleWithDefault<string option option option option>
static member x19 = backwardCompatibleWithDefault<int NestedOptions>
static member x20 = backwardCompatibleWithDefault<SomeAmbiguity.Ambiguous1<string>>
static member x21 = backwardCompatibleWithDefault<SomeAmbiguity.Ambiguous1<SimpleDu>>
static member x22 = backwardCompatibleWithDefault<NestedOptionStructure>
static member x23 = backwardCompatibleWithDefault<SomeAmbiguity.Ambiguous2>
static member x24 = backwardCompatibleWithDefault<SomeAmbiguity.Ambiguous3>
static member x25 = backwardCompatibleWithDefault<int list>
static member x26 = backwardCompatibleWithDefault<SomeTupleType>
let inline ``Run using all serializers``< ^T when ^T:equality> (test: (^T->string)->(string-> ^T)-> ^T->unit) (input: ^T) =
[
Compact.serialize, Compact.deserialize
Default.serialize, Default.deserialize
CamelCaseSerializer.serialize, CamelCaseSerializer.deserialize
BackwardCompatible.serialize, BackwardCompatible.deserialize
] |> List.iter (fun (s, d) -> test s d input)
let inline testBackwardCompat< ^T when ^T:equality> (x: ^T) =
let y =
x
|> Compact.Legacy.serialize< ^T>
|> Compact.deserialize< ^T>
Assert.AreEqual(x, y, "Tuple deserialization is not backward compatible!")
[<TestClass>]
type JsonSerializerTests() =
[<ClassInitialize>]
static member Init(context : TestContext) = ()
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Serialize field-less DU`` () =
FooBar |> serializedAs "\"FooBar\""
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Dont' touch Fsharp lists`` () =
[1;2;3] |> serializedAs (Default.serialize [1;2;3])
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Handles Some`` () =
Some "test" |> serializedAs "\"test\""
[<TestMethod>]
[<TestCategory("FSharpLu..Json")>]
member __.``Handles None`` () =
None |> serializedAs "null"
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Handles just the expected types``() =
let conv = CompactUnionJsonConverter(true)
Assert.IsTrue(conv.CanConvert(Color.Red.GetType()))
Assert.IsTrue(conv.CanConvert(typeof<Color>))
Assert.IsTrue(conv.CanConvert(typeof<_ option>))
Assert.IsTrue(conv.CanConvert(typeof<_ Tree>))
Assert.IsTrue(conv.CanConvert(typeof<Shape>))
Assert.IsFalse(conv.CanConvert(typeof<_ list>))
Assert.IsFalse(conv.CanConvert(typeof<Map<_,_>>))
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Serialization does not raise exceptions for basic types``() =
``Run using all serializers`` canBeSerialized <| Red
``Run using all serializers`` canBeSerialized <| Blue
``Run using all serializers`` canBeSerialized <| Circle (8,99)
``Run using all serializers`` canBeSerialized <| Some 8
``Run using all serializers`` canBeSerialized <| Some (Circle(5,120))
``Run using all serializers`` canBeSerialized <| None
``Run using all serializers`` canBeSerialized <| Some (Node(Leaf 1,Leaf 9))
``Run using all serializers`` canBeSerialized <| Case1
``Run using all serializers`` canBeSerialized <| Case2 (3)
``Run using all serializers`` canBeSerialized <| Case3 (3,"s", "Foo")
``Run using all serializers`` canBeSerialized <| Circle (8,10)
``Run using all serializers`` canBeSerialized <| Leaf ["s";"s"]
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``Serialization and deserialization are reciprocal``() =
``Run using all serializers`` areReciprocal <| Some 8
``Run using all serializers`` areReciprocal <| Leaf "s"
``Run using all serializers`` areReciprocal <| Leaf ["s";"s"]
``Run using all serializers`` areReciprocal <| Leaf "s"
``Run using all serializers`` areReciprocal <| Some 8
``Run using all serializers`` areReciprocal <| Red
``Run using all serializers`` areReciprocal <| Circle (8,10)
``Run using all serializers`` areReciprocal <| Node((Leaf "s"),(Leaf "s"))
``Run using all serializers`` areReciprocal <| Leaf ["s";"s"]
``Run using all serializers`` areReciprocal <| Node((Leaf 1),(Leaf 9))
``Run using all serializers`` areReciprocal <| Case1
``Run using all serializers`` areReciprocal <| Case2 (3)
``Run using all serializers`` areReciprocal <| Case3 (3,"s", "Foo")
``Run using all serializers`` areReciprocal <| (["test", [3;3;4]] |> Map.ofSeq)
``Run using all serializers`` areReciprocal <| ["test", [3;3;4]]
``Run using all serializers`` areReciprocal <| Some (Some (Some None))
``Run using all serializers`` areReciprocal <| Some (Some None)
``Run using all serializers`` areReciprocal <| Some null
``Run using all serializers`` areReciprocal <| Some None
``Run using all serializers`` areReciprocal <| Some (Some (Some None))
``Run using all serializers`` areReciprocal <| (1,2,3,4,5,6,7,8,9,10)
[<TestMethod>]
[<TestCategory("FSharpLu.Json")>]
member __.``No ambiguity between records and Option type``() =
``Run using all serializers`` areReciprocal <| Some (Some (Some None))
``Run using all serializers`` areReciprocal <| { SomeAmbiguity.Some = null }
``Run using all serializers`` areReciprocal <| { SomeAmbiguity.Some = SimpleDu.Foo }
``Run using all serializers`` areReciprocal <| { SomeAmbiguity.Some = "test" }
``Run using all serializers`` areReciprocal <| { SomeAmbiguity.Some = 123 }
``Run using all serializers`` areReciprocal <| (Option.Some { SomeAmbiguity.Some = 345 })
``Run using all serializers`` areReciprocal <| (Option.Some <| SomeAmbiguity.DUWithFieldlessCaseNamedSome.Some "ambiguous")
``Run using all serializers`` areReciprocal <| (Option.Some { SomeAmbiguity.RecordWithFieldNamedSome.Some = 8 })
``Run using all serializers`` areReciprocal <| (Option.Some <| SomeAmbiguity.DUWithCaseWithFieldNamedSome.Some)
[<TestMethod>]
[<TestCategory("FSharpLu.Json.CamelCase")>]
member __.``CamelCaseSerializer handles discriminated unions`` () =
let du = ComplexDu <| SomeField (2, 3)
let str = CamelCaseSerializer.serialize du
Assert.AreEqual("""{"complexDu":{"someField":[2,3]}}""", str)
[<TestMethod>]
[<TestCategory("FSharpLu.Json.CamelCase")>]
member __.``CamelCaseSerializer handles discriminated unions with consecutive uppercase characters`` () =
let du = [ FOO ; FOOWithRecord { BAR=2; BAZNumber=3 } ]
let str = CamelCaseSerializer.serialize du
Assert.AreEqual("""["foo",{"fooWithRecord":{"bar":2,"bazNumber":3}}]""", str)
[<TestMethod>]
[<TestCategory("FSharpLu.Json.CamelCase")>]
member __.``CamelCaseSerializer handles option type`` () =
let du = { WrappedField = Some Red }
let str = CamelCaseSerializer.serialize du
Assert.AreEqual("""{"wrappedField":"red"}""", str)
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Fuzzing")>]
member __.``Fuzzing Reciprocal Compact`` () =
Check.VerboseThrowOnFailureAll<ReciprocalityCompact>()
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Fuzzing")>]
member __.``Fuzzing Reciprocal CamelCase`` () =
Check.VerboseThrowOnFailureAll<ReciprocalityCamelCase>()
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Fuzzing")>]
member __.``Deserialization coincides with JSon.Net (Fuzzing)`` () =
Check.VerboseThrowOnFailureAll<CoincidesWithJsonNetOnDeserialization>()
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Fuzzing")>]
member __.``BackwardCompatible deserializes default Json.Net format and returns same object`` () =
Check.VerboseThrowOnFailureAll<BackwardCompatibility>()
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Tuples")>]
member __.``Serialize tuples as list`` () =
(1, 2) |> serializedAs (defaultSerialize [1; 2])
(1, 2, 3) |> serializedAs (defaultSerialize [1; 2; 3])
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) |> serializedAs (defaultSerialize [1; 2; 3; 4; 5; 6; 7; 8; 9; 10])
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Tuples")>]
member __.``Tuple serialization is backward compatible`` () =
(1, 2) |> testBackwardCompat
(1, "test", 5) |> testBackwardCompat
(1, ["foo"; "bar"]) |> testBackwardCompat
(1, ["foo"; "bar"], 4, "hello", ("bird", 3), 2, 3, 2, 4, 7) |> testBackwardCompat
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) |> testBackwardCompat
// Check for nested serialization: legacy JSON.net serialization breaks down tuples
// in buckets of 7 elements maximum. Each additional bucket gets nested under a
// "Rest" JSON field.
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) |> testBackwardCompat
[<TestMethod>]
[<TestCategory("FSharpLu.Json.Tuples")>]
member __.``Legacy tuple deserialization handles property reordering`` () =
let r =
"""{ "Item3": 3, "Item2": 2, "Item1": 1 }"""
|> Compact.deserialize<int*int*int>
Assert.AreEqual(r, (1,2,3), "Tuple deserialization should handle JSON properties in any order")