-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathclock.test.js
More file actions
306 lines (240 loc) · 10.7 KB
/
clock.test.js
File metadata and controls
306 lines (240 loc) · 10.7 KB
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
import { describe, it } from 'mocha'
import assert from 'node:assert'
// eslint-disable-next-line no-unused-vars
import * as API from '../src/clock/api.js'
import { advance, EventBlock, vis } from '../src/clock/index.js'
import { Blockstore, randomCID } from './helpers.js'
async function randomEventData () {
return {
type: 'put',
key: `test-${Date.now()}`,
value: await randomCID(32)
}
}
describe('clock', () => {
it('create a new clock', async () => {
const blocks = new Blockstore()
const event = await EventBlock.create({})
await blocks.put(event.cid, event.bytes)
const head = await advance(blocks, [], event.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 1)
assert.equal(head[0].toString(), event.cid.toString())
})
it('add an event', async () => {
const blocks = new Blockstore()
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const event = await EventBlock.create(await randomEventData(), head)
await blocks.put(event.cid, event.bytes)
head = await advance(blocks, head, event.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 1)
assert.equal(head[0].toString(), event.cid.toString())
})
it('add two events with shared parents', async () => {
const blocks = new Blockstore()
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const parents = head
const event0 = await EventBlock.create(await randomEventData(), parents)
await blocks.put(event0.cid, event0.bytes)
head = await advance(blocks, parents, event0.cid)
const event1 = await EventBlock.create(await randomEventData(), parents)
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 2)
assert.equal(head[0].toString(), event0.cid.toString())
assert.equal(head[1].toString(), event1.cid.toString())
})
it('add two events with some shared parents', async () => {
const blocks = new Blockstore()
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const parents0 = head
const event0 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event0.cid, event0.bytes)
head = await advance(blocks, head, event0.cid)
const event1 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
const event2 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event2.cid, event2.bytes)
head = await advance(blocks, head, event2.cid)
const event3 = await EventBlock.create(await randomEventData(), [event0.cid, event1.cid])
await blocks.put(event3.cid, event3.bytes)
head = await advance(blocks, head, event3.cid)
const event4 = await EventBlock.create(await randomEventData(), [event2.cid])
await blocks.put(event4.cid, event4.bytes)
head = await advance(blocks, head, event4.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 2)
assert.equal(head[0].toString(), event3.cid.toString())
assert.equal(head[1].toString(), event4.cid.toString())
})
it('converge when multi-root', async () => {
const blocks = new Blockstore()
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const parents0 = head
const event0 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event0.cid, event0.bytes)
head = await advance(blocks, head, event0.cid)
const event1 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
const parents1 = head
const event2 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event2.cid, event2.bytes)
head = await advance(blocks, head, event2.cid)
const event3 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event3.cid, event3.bytes)
head = await advance(blocks, head, event3.cid)
const event4 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event4.cid, event4.bytes)
head = await advance(blocks, head, event4.cid)
const parents2 = head
const event5 = await EventBlock.create(await randomEventData(), parents2)
await blocks.put(event5.cid, event5.bytes)
head = await advance(blocks, head, event5.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 1)
assert.equal(head[0].toString(), event5.cid.toString())
})
it('add an old event', async () => {
const blocks = new Blockstore()
const blockGet = blocks.get.bind(blocks)
let count = 0
blocks.get = async cid => {
count++
return blockGet(cid)
}
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const parents0 = head
const event0 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event0.cid, event0.bytes)
head = await advance(blocks, head, event0.cid)
const event1 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
const parents1 = head
const event2 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event2.cid, event2.bytes)
head = await advance(blocks, head, event2.cid)
const event3 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event3.cid, event3.bytes)
head = await advance(blocks, head, event3.cid)
const event4 = await EventBlock.create(await randomEventData(), parents1)
await blocks.put(event4.cid, event4.bytes)
head = await advance(blocks, head, event4.cid)
const parents2 = head
const event5 = await EventBlock.create(await randomEventData(), parents2)
await blocks.put(event5.cid, event5.bytes)
head = await advance(blocks, head, event5.cid)
// now very old one
const event6 = await EventBlock.create(await randomEventData(), parents0)
await blocks.put(event6.cid, event6.bytes)
const before = count
head = await advance(blocks, head, event6.cid)
assert.equal(count - before, 10)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 2)
assert.equal(head[0].toString(), event5.cid.toString())
assert.equal(head[1].toString(), event6.cid.toString())
})
it('add an event with missing parents', async () => {
const blocks = new Blockstore()
const root = await EventBlock.create(await randomEventData())
await blocks.put(root.cid, root.bytes)
/** @type {API.EventLink<any>[]} */
let head = [root.cid]
const event0 = await EventBlock.create(await randomEventData(), head)
await blocks.put(event0.cid, event0.bytes)
const event1 = await EventBlock.create(await randomEventData(), [event0.cid])
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
for await (const line of vis(blocks, head)) console.log(line)
assert.equal(head.length, 1)
assert.equal(head[0].toString(), event1.cid.toString())
})
/*
```mermaid
flowchart TB
event3 --> event1
event3 --> event2
event2 --> event0
event1 --> event0
event0 --> genesis
event4 --> genesis
```
All we want to do is prove that `event0` and `genesis` are not fetched
multiple times, since there are multiple paths to it in the tree.
We arrive at 8, because when we advance with `head: [event3], event: event4`
we firstly check if the head is in the event:
* get event4 (1)
* get event3 (2)
* get genesis (3)
Then we check if the event is in the head, with de-duping:
* get event3 (4)
* get event4 (5)
* then get each of the nodes back to parent(s) of `event4` (`genesis`):
* event1 (6)
* event2 (7)
* event0 (8)
* (we don't fetch genesis due to existing cycle detection)
Without deduping, we expect 9 node fetches, since we traverse across `event0`
again, since it is linked to by 2 nodes.
*/
it('contains only traverses history once', async () => {
const blocks = new Blockstore()
const genesis = await EventBlock.create(await randomEventData())
await blocks.put(genesis.cid, genesis.bytes)
/** @type {API.EventLink<any>[]} */
let head = [genesis.cid]
const blockGet = blocks.get.bind(blocks)
let count = 0
blocks.get = async cid => {
count++
return blockGet(cid)
}
const event0 = await EventBlock.create(await randomEventData(), [genesis.cid])
await blocks.put(event0.cid, event0.bytes)
head = await advance(blocks, head, event0.cid)
const event1 = await EventBlock.create(await randomEventData(), [event0.cid])
await blocks.put(event1.cid, event1.bytes)
head = await advance(blocks, head, event1.cid)
const event2 = await EventBlock.create(await randomEventData(), [event0.cid])
await blocks.put(event2.cid, event2.bytes)
head = await advance(blocks, head, event2.cid)
const event3 = await EventBlock.create(await randomEventData(), [event1.cid, event2.cid])
await blocks.put(event3.cid, event3.bytes)
head = await advance(blocks, head, event3.cid)
const before = count
const event4 = await EventBlock.create(await randomEventData(), [genesis.cid])
await blocks.put(event4.cid, event4.bytes)
head = await advance(blocks, head, event4.cid)
assert.equal(head.length, 2)
assert.equal(head[1].toString(), event4.cid.toString())
assert.equal(head[0].toString(), event3.cid.toString())
assert.equal(count - before, 8, 'The number of traversals should be 8 with optimization')
})
it('sorts event links', async () => {
const parent0 = await EventBlock.create(await randomEventData())
const parent1 = await EventBlock.create(await randomEventData())
const child0 = await EventBlock.create({}, [parent0.cid, parent1.cid])
const child1 = await EventBlock.create({}, [parent1.cid, parent0.cid])
assert.deepEqual(child0.bytes, child1.bytes)
})
})