an atproto pds written in F# (.NET 9) 馃
pds
fsharp
giraffe
dotnet
atproto
1module MstTests
2
3open Xunit
4open PDSharp.Core
5open PDSharp.Core.Mst
6
7[<Fact>]
8let ``Serialization Roundtrip`` () =
9 let cid1 = Cid(Crypto.sha256Str "val1")
10
11 let e1 = {
12 PrefixLen = 0
13 KeySuffix = "apple"
14 Value = cid1
15 Tree = None
16 }
17
18 let e2 = {
19 PrefixLen = 2
20 KeySuffix = "ricot"
21 Value = cid1
22 Tree = None
23 }
24
25 let node = { Left = None; Entries = [ e1; e2 ] }
26
27 let bytes = Mst.serialize node
28 let node2 = Mst.deserialize bytes
29
30 Assert.Equal(node.Entries.Length, node2.Entries.Length)
31 Assert.Equal("apple", node2.Entries.[0].KeySuffix)
32 Assert.Equal("ricot", node2.Entries.[1].KeySuffix)
33 Assert.Equal(2, node2.Entries.[1].PrefixLen)
34
35[<Fact>]
36let ``Get Operation Linear Scan`` () =
37 let cid1 = Cid(Crypto.sha256Str "val1")
38 let cid2 = Cid(Crypto.sha256Str "val2")
39
40 let e1 = {
41 PrefixLen = 0
42 KeySuffix = "apple"
43 Value = cid1
44 Tree = None
45 }
46
47 let e2 = {
48 PrefixLen = 0
49 KeySuffix = "banana"
50 Value = cid2
51 Tree = None
52 }
53
54 let node = { Left = None; Entries = [ e1; e2 ] }
55
56 let loader (c : Cid) = async { return None }
57
58 let res1 = Mst.get loader node "apple" "" |> Async.RunSynchronously
59 Assert.Equal(Some cid1, res1)
60
61 let res2 = Mst.get loader node "banana" "" |> Async.RunSynchronously
62 Assert.Equal(Some cid2, res2)
63
64 let res3 = Mst.get loader node "cherry" "" |> Async.RunSynchronously
65 Assert.True(Option.isNone res3)
66
67[<Fact>]
68let ``Get Operation With Prefix Compression`` () =
69 let cid1 = Cid(Crypto.sha256Str "val1")
70 let cid2 = Cid(Crypto.sha256Str "val2")
71
72 let e1 = {
73 PrefixLen = 0
74 KeySuffix = "apple"
75 Value = cid1
76 Tree = None
77 }
78
79 let e2 = {
80 PrefixLen = 2
81 KeySuffix = "ricot"
82 Value = cid2
83 Tree = None
84 }
85
86 let node = { Left = None; Entries = [ e1; e2 ] }
87 let loader (c : Cid) = async { return None }
88
89 let res1 = Mst.get loader node "apricot" "" |> Async.RunSynchronously
90 Assert.Equal(Some cid2, res1)
91
92[<Fact>]
93let ``Put Operation Simple Insert`` () =
94 let store = System.Collections.Concurrent.ConcurrentDictionary<string, MstNode>()
95
96 let loader (c : Cid) = async {
97 let key = System.Convert.ToBase64String(c.Bytes)
98 let success, node = store.TryGetValue(key)
99 return if success then Some node else None
100 }
101
102 let persister (n : MstNode) = async {
103 let bytes = Mst.serialize n
104 let cid = Cid(Crypto.sha256 bytes)
105 let key = System.Convert.ToBase64String(cid.Bytes)
106 store.[key] <- n
107 return cid
108 }
109
110 let node = { Left = None; Entries = [] }
111 let cid1 = Cid(Crypto.sha256Str "v1")
112 let node2 = Mst.put loader persister node "apple" cid1 "" |> Async.RunSynchronously
113
114 Assert.Equal(1, node2.Entries.Length)
115 Assert.Equal("apple", node2.Entries.[0].KeySuffix)
116 Assert.Equal(0, node2.Entries.[0].PrefixLen)
117 Assert.Equal(cid1, node2.Entries.[0].Value)
118
119 let res = Mst.get loader node2 "apple" "" |> Async.RunSynchronously
120 Assert.Equal(Some cid1, res)
121
122[<Fact>]
123let ``Put Operation Multiple Sorted`` () =
124 let store = System.Collections.Concurrent.ConcurrentDictionary<string, MstNode>()
125
126 let loader (c : Cid) = async {
127 let key = System.Convert.ToBase64String(c.Bytes)
128 let success, node = store.TryGetValue(key)
129 return if success then Some node else None
130 }
131
132 let persister (n : MstNode) = async {
133 let bytes = Mst.serialize n
134 let cid = Cid(Crypto.sha256 bytes)
135 let key = System.Convert.ToBase64String(cid.Bytes)
136 store.[key] <- n
137 return cid
138 }
139
140 let mutable node = { Left = None; Entries = [] }
141
142 let k1, v1 = "apple", Cid(Crypto.sha256Str "1")
143 let k2, v2 = "banana", Cid(Crypto.sha256Str "2")
144 let k3, v3 = "cherry", Cid(Crypto.sha256Str "3")
145
146 node <- Mst.put loader persister node k1 v1 "" |> Async.RunSynchronously
147 node <- Mst.put loader persister node k2 v2 "" |> Async.RunSynchronously
148 node <- Mst.put loader persister node k3 v3 "" |> Async.RunSynchronously
149
150 let g1 = Mst.get loader node "apple" "" |> Async.RunSynchronously
151 let g2 = Mst.get loader node "banana" "" |> Async.RunSynchronously
152 let g3 = Mst.get loader node "cherry" "" |> Async.RunSynchronously
153
154 Assert.Equal(Some v1, g1)
155 Assert.Equal(Some v2, g2)
156 Assert.Equal(Some v3, g3)
157
158[<Fact>]
159let ``Put Operation Multiple Reverse`` () =
160 let store = System.Collections.Concurrent.ConcurrentDictionary<string, MstNode>()
161
162 let loader (c : Cid) = async {
163 let key = System.Convert.ToBase64String(c.Bytes)
164 let success, node = store.TryGetValue(key)
165 return if success then Some node else None
166 }
167
168 let persister (n : MstNode) = async {
169 let bytes = Mst.serialize n
170 let cid = Cid(Crypto.sha256 bytes)
171 let key = System.Convert.ToBase64String(cid.Bytes)
172 store.[key] <- n
173 return cid
174 }
175
176 let mutable node = { Left = None; Entries = [] }
177
178 let data = [ "zebra"; "yak"; "xylophone" ]
179
180 for k in data do
181 let v = Cid(Crypto.sha256Str k)
182 node <- Mst.put loader persister node k v "" |> Async.RunSynchronously
183
184 for k in data do
185 let expected = Cid(Crypto.sha256Str k)
186 let actual = Mst.get loader node k "" |> Async.RunSynchronously
187 Assert.Equal(Some expected, actual)
188
189[<Fact>]
190let ``Delete Operation Simple`` () =
191 let store = System.Collections.Concurrent.ConcurrentDictionary<string, MstNode>()
192
193 let loader (c : Cid) = async {
194 let key = System.Convert.ToBase64String(c.Bytes)
195 let success, node = store.TryGetValue(key)
196 return if success then Some node else None
197 }
198
199 let persister (n : MstNode) = async {
200 let bytes = Mst.serialize n
201 let cid = Cid(Crypto.sha256 bytes)
202 let key = System.Convert.ToBase64String(cid.Bytes)
203 store.[key] <- n
204 return cid
205 }
206
207 let mutable node = { Left = None; Entries = [] }
208 let cid1 = Cid(Crypto.sha256Str "val1")
209
210 node <- Mst.put loader persister node "apple" cid1 "" |> Async.RunSynchronously
211
212 let res1 = Mst.get loader node "apple" "" |> Async.RunSynchronously
213 Assert.Equal(Some cid1, res1)
214
215 // Delete
216 let nodeOpt = Mst.delete loader persister node "apple" "" |> Async.RunSynchronously
217
218 match nodeOpt with
219 | None -> ()
220 | Some n ->
221 let res2 = Mst.get loader n "apple" "" |> Async.RunSynchronously
222 Assert.True(Option.isNone res2)
223
224[<Fact>]
225let ``Determinism From Entries`` () =
226 let store = System.Collections.Concurrent.ConcurrentDictionary<string, MstNode>()
227
228 let loader (c : Cid) = async {
229 let key = System.Convert.ToBase64String(c.Bytes)
230 let success, node = store.TryGetValue(key)
231 return if success then Some node else None
232 }
233
234 let persister (n : MstNode) = async {
235 let bytes = Mst.serialize n
236 let cid = Cid(Crypto.sha256 bytes)
237 let key = System.Convert.ToBase64String(cid.Bytes)
238 store.[key] <- n
239 return cid
240 }
241
242 let data = [
243 "apple", Cid(Crypto.sha256Str "1")
244 "banana", Cid(Crypto.sha256Str "2")
245 "cherry", Cid(Crypto.sha256Str "3")
246 "date", Cid(Crypto.sha256Str "4")
247 "elderberry", Cid(Crypto.sha256Str "5")
248 ]
249
250
251 let node1 = Mst.fromEntries loader persister data |> Async.RunSynchronously
252 let cid1 = persister node1 |> Async.RunSynchronously
253
254 let node2 =
255 Mst.fromEntries loader persister (List.rev data) |> Async.RunSynchronously
256
257 let cid2 = persister node2 |> Async.RunSynchronously
258 let data3 = [ data.[2]; data.[0]; data.[4]; data.[1]; data.[3] ]
259 let node3 = Mst.fromEntries loader persister data3 |> Async.RunSynchronously
260 let cid3 = persister node3 |> Async.RunSynchronously
261
262 Assert.Equal(cid1, cid2)
263 Assert.Equal(cid1, cid3)
264 ()