@fireproof/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,203 @@
1
+ import { describe, it } from 'mocha'
2
+ import assert from 'node:assert'
3
+ import { advance } from '../src/clock.js'
4
+
5
+ import { put, get, getAll, root, eventsSince } from '../src/prolly.js'
6
+ import { Blockstore, seqEventData, setSeq } from './helpers.js'
7
+
8
+ describe('Prolly', () => {
9
+ it('put a value to a new clock', async () => {
10
+ const blocks = new Blockstore()
11
+ const alice = new TestPail(blocks, [])
12
+ const key = 'key'
13
+ const value = seqEventData()
14
+ const { event, head } = await alice.put(key, value)
15
+
16
+ assert.equal(event.value.data.type, 'put')
17
+ assert.equal(event.value.data.key, key)
18
+ assert.equal(event.value.data.value.toString(), value.toString())
19
+ assert.equal(head.length, 1)
20
+ assert.equal(head[0].toString(), event.cid.toString())
21
+
22
+ const avalue = await alice.get('key')
23
+ assert(avalue)
24
+ assert.equal(JSON.stringify(avalue), JSON.stringify(value))
25
+ })
26
+
27
+ it('linear put multiple values', async () => {
28
+ setSeq(-1)
29
+ const blocks = new Blockstore()
30
+ const alice = new TestPail(blocks, [])
31
+
32
+ const key0 = 'key0'
33
+ const value0 = seqEventData()
34
+ const { head: oldHead } = await alice.put(key0, value0)
35
+
36
+ const key1 = 'key1'
37
+ const value1 = seqEventData()
38
+ const result = await alice.put(key1, value1)
39
+
40
+ assert.equal(result.event.value.data.type, 'put')
41
+ assert.equal(result.event.value.data.key, key1)
42
+ assert.equal(result.event.value.data.value.toString(), value1.toString())
43
+ assert.equal(result.head.length, 1)
44
+ assert.equal(result.head[0].toString(), result.event.cid.toString())
45
+
46
+ const allResp = await alice.getAll()
47
+ assert(allResp)
48
+ assert.equal(allResp.length, 2)
49
+ assert.equal(allResp[0].key, key0)
50
+
51
+ // add a third value
52
+ // try getSince
53
+ const sinceResp = await alice.getSince(oldHead)
54
+ assert.equal(sinceResp.length, 1)
55
+ assert.equal(sinceResp[0].value.value, 'event0')
56
+ })
57
+
58
+ it('get missing', async () => {
59
+ const blocks = new Blockstore()
60
+ const alice = new TestPail(blocks, [])
61
+ const key = 'key'
62
+ const value = seqEventData('test-missing-root')
63
+ await alice.put(key, value)
64
+
65
+ await alice.get('missing').then((value) => {
66
+ assert('false', 'should not get here')
67
+ }).catch((err) => {
68
+ assert.equal(err.message, 'Not found')
69
+ })
70
+ })
71
+
72
+ it('simple parallel put multiple values', async () => {
73
+ const blocks = new Blockstore()
74
+ const alice = new TestPail(blocks, [])
75
+ await alice.put('key0', seqEventData())
76
+ const bob = new TestPail(blocks, alice.head)
77
+
78
+ /** @type {Array<[string, import('../src/link').AnyLink]>} */
79
+ const data = [
80
+ ['key1', seqEventData()],
81
+ ['key2', seqEventData()],
82
+ ['key3', seqEventData()],
83
+ ['key4', seqEventData()]
84
+ ]
85
+
86
+ const { event: aevent0 } = await alice.put(data[0][0], data[0][1])
87
+ const { event: bevent0 } = await bob.put(data[1][0], data[1][1])
88
+ const { event: bevent1 } = await bob.put(data[2][0], data[2][1])
89
+
90
+ await alice.advance(bevent0.cid)
91
+ await alice.advance(bevent1.cid)
92
+ await bob.advance(aevent0.cid)
93
+
94
+ const { event: aevent1 } = await alice.put(data[3][0], data[3][1])
95
+
96
+ await bob.advance(aevent1.cid)
97
+
98
+ assert(alice.root)
99
+ assert(bob.root)
100
+ assert.equal(alice.root.toString(), bob.root.toString())
101
+
102
+ // get item put to bob
103
+ const avalue = await alice.get(data[1][0])
104
+ assert(avalue)
105
+ assert.equal(avalue.toString(), data[1][1].toString())
106
+
107
+ // get item put to alice
108
+ const bvalue = await bob.get(data[0][0])
109
+ assert(bvalue)
110
+ assert.equal(bvalue.toString(), data[0][1].toString())
111
+ })
112
+
113
+ it.skip('linear put hundreds of values', async () => {
114
+ const blocks = new Blockstore()
115
+ const alice = new TestPail(blocks, [])
116
+
117
+ for (let i = 0; i < 100; i++) {
118
+ await alice.put('key' + i, seqEventData())
119
+ }
120
+
121
+ for (let i = 0; i < 100; i++) {
122
+ const vx = await alice.get('key' + i)
123
+ assert(vx)
124
+ // console.log('vx', vx)
125
+ // assert.equal(vx.toString(), value.toString())
126
+ }
127
+ console.log('blocks', Array.from(blocks.entries()).length)
128
+ }).timeout(10000)
129
+ })
130
+
131
+ class TestPail {
132
+ /**
133
+ * @param {Blockstore} blocks
134
+ * @param {import('../src/clock').EventLink<import('../src/crdt').EventData>[]} head
135
+ */
136
+ constructor (blocks, head) {
137
+ this.blocks = blocks
138
+ this.head = head
139
+ /** @type {import('../src/shard.js').ShardLink?} */
140
+ this.root = null
141
+ }
142
+
143
+ /**
144
+ * @param {string} key
145
+ * @param {import('../src/link').AnyLink} value
146
+ */
147
+ async put (key, value) {
148
+ const result = await put(this.blocks, this.head, { key, value })
149
+ if (!result) { console.log('failed', key, value) }
150
+ this.blocks.putSync(result.event.cid, result.event.bytes)
151
+ result.additions.forEach(a => this.blocks.putSync(a.cid, a.bytes))
152
+ this.head = result.head
153
+ this.root = result.root.cid
154
+ // this difference probably matters, but we need to test it
155
+ // this.root = await root(this.blocks, this.head)
156
+ // console.log('prolly PUT', key, value, { head: result.head, additions: result.additions.map(a => a.cid), event: result.event.cid })
157
+ return result
158
+ }
159
+
160
+ // todo make bulk ops which should be easy at the prolly layer by passing a list of events instead of one
161
+ // async bulk() {}
162
+
163
+ /** @param {import('../src/clock').EventLink<import('../src/crdt').EventData>} event */
164
+ async advance (event) {
165
+ this.head = await advance(this.blocks, this.head, event)
166
+ this.root = (await root(this.blocks, this.head)).block.cid
167
+ return this.head
168
+ }
169
+
170
+ // /**
171
+ // * @param {string} key
172
+ // * @param {import('../src/link.js').AnyLink} value
173
+ // */
174
+ // async putAndVis (key, value) {
175
+ // const result = await this.put(key, value)
176
+ // /** @param {import('../src/link').AnyLink} l */
177
+ // const shortLink = l => `${String(l).slice(0, 4)}..${String(l).slice(-4)}`
178
+ // /** @type {(e: import('../src/clock').EventBlockView<import('../src/crdt').EventData>) => string} */
179
+ // const renderNodeLabel = event => {
180
+ // return event.value.data.type === 'put'
181
+ // ? `${shortLink(event.cid)}\\nput(${event.value.data.key}, ${shortLink(event.value.data.value)})`
182
+ // : `${shortLink(event.cid)}\\ndel(${event.value.data.key})`
183
+ // }
184
+ // for await (const line of vis(this.blocks, result.head, { renderNodeLabel })) {
185
+ // console.log(line)
186
+ // }
187
+ // return result
188
+ // }
189
+
190
+ /** @param {string} key */
191
+ async get (key) {
192
+ return get(this.blocks, this.head, key)
193
+ }
194
+
195
+ /** @param {string} key */
196
+ async getAll () {
197
+ return getAll(this.blocks, this.head)
198
+ }
199
+
200
+ async getSince (since) {
201
+ return eventsSince(this.blocks, this.head, since)
202
+ }
203
+ }