-
Notifications
You must be signed in to change notification settings - Fork 36
/
UnionContractTests.fs
134 lines (108 loc) · 4.08 KB
/
UnionContractTests.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
module TypeShape.Tests.UnionContract
open System
open System.Collections.Generic
open System.Runtime.Serialization
open FSharp.Reflection
open Xunit
open FsCheck.Xunit
open Newtonsoft.Json.Linq
open Swensen.Unquote
open TypeShape.Empty
open TypeShape.UnionContract
// Placeholder event records emulating an imaginary domain
type CartCreated = { id: string ; client_id: string }
type AddressUpdated = { id: string ; address: string }
type ItemReturnWaived = { id : string ; item_id : string ; value: bool }
type CashUsed = { id: string ; value: bool }
type CreditsUsed = { id: string ; value: bool }
type CartItemRemoved = { id : string ; retail_sku_id : string ; date : DateTimeOffset }
type JsonEncoder() =
interface IEncoder<JToken> with
member _.Empty = JValue.CreateNull() :> _
member _.Encode t = match box t with null -> JValue.CreateNull() :> _ | o -> JToken.FromObject o
member _.Decode t = t.ToObject()
type BasicEventSum =
| NullaryEvent
| EventWithNonRecord of int
| EventWithUnit of unit
| CartCreated of CartCreated
| AddressUpdated of AddressUpdated
| ItemReturnWaived of ItemReturnWaived
| CashUsed of CashUsed
| CreditsUsed of CreditsUsed
| CartItemRemoved of CartItemRemoved
| [<DataMember(Name = "Legacy")>] Old_Stuff of CartItemRemoved
with
interface IUnionContract
let mkEncoder<'T when 'T :> IUnionContract> = UnionContractEncoder.Create<'T,_>(JsonEncoder())
let encoder = lazy(mkEncoder<BasicEventSum>)
[<Property>]
let ``Basic EventSum enc/dec roundtrip`` (sum : BasicEventSum) =
let e = encoder.Value
e.Decode(e.Encode sum) = sum
[<Property>]
let ``Should use correct event types in encodings`` (sum : BasicEventSum) =
let e = encoder.Value.Encode sum
let expectedLabel =
match sum with
| Old_Stuff _ -> "Legacy"
| _ ->
let uci, _ = FSharpValue.GetUnionFields(sum, typeof<BasicEventSum>, true)
uci.Name
test <@ expectedLabel = e.CaseName @>
[<Fact>]
let ``Should throw FormatException on Decode of unrecognized event types`` () =
let enc = encoder.Value
let e = enc.Encode (CartCreated empty)
let e' = { e with CaseName = "__UNKNOWN_TYPE__" }
raises<FormatException> <@ enc.Decode e' @>
[<Fact>]
let ``Should return None on TryDecode of unrecognized event types`` () =
let enc = encoder.Value
let e = enc.Encode (CartCreated empty)
let e' = { e with CaseName = "__UNKNOWN_TYPE__" }
test <@ None = enc.TryDecode e' @>
[<Fact>]
let ``Should fail when requiring record fields`` () =
raises<ArgumentException> <@ UnionContractEncoder.Create<BasicEventSum,_>(BoxEncoder(), requireRecordFields = true) @>
type private PrivateEventSum = A of CartCreated
with interface IUnionContract
[<Fact>]
let ``Should work with private types`` () =
let enc = mkEncoder<PrivateEventSum>
let value = A empty
test <@ value = enc.Decode(enc.Encode value) @>
type EventSumWithNullaryCase =
| Nullary
| A of CartCreated
with
interface IUnionContract
[<Fact>]
let ``Should fail on nullary union cases`` () =
raises<ArgumentException> <@ UnionContractEncoder.Create<EventSumWithNullaryCase,_>(BoxEncoder(), allowNullaryCases = false) @>
[<Fact>]
let ``Should succeed on nullary union cases`` () =
let enc = UnionContractEncoder.Create<EventSumWithNullaryCase,_>(BoxEncoder(), allowNullaryCases = true)
()
type EventSumWithLargeArity =
| A of CartCreated
| B of CartCreated * CartCreated
with
interface IUnionContract
[<Fact>]
let ``Should fail on union case arities > 1`` () =
raises<ArgumentException> <@ mkEncoder<EventSumWithLargeArity> @>
type NonUnion = { x : int }
with interface IUnionContract
[<Fact>]
let ``Should fail on non-union inputs`` () =
raises<ArgumentException> <@ mkEncoder<NonUnion> @>
type EventSumWithDuplicateLabels =
| A of CartCreated
| B of CashUsed
| [<DataMember(Name = "A")>] C of string
with
interface IUnionContract
[<Fact>]
let ``Should fail on event sums with duplicate labels`` () =
raises<ArgumentException> <@ mkEncoder<EventSumWithDuplicateLabels> @>