@dxos/echo-pipeline 0.4.7 → 0.4.8-main.05fda5d

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.
@@ -1,325 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import expect from 'expect';
6
-
7
- import { sleep } from '@dxos/async';
8
- import { DocumentModel, MutationBuilder, OrderedArray } from '@dxos/document-model';
9
- import {
10
- COMMIT_BATCH_INACTIVITY_DEFAULT,
11
- type Item,
12
- createModelMutation,
13
- encodeModelMutation,
14
- genesisMutation,
15
- } from '@dxos/echo-db';
16
- import { invariant } from '@dxos/invariant';
17
- import { PublicKey } from '@dxos/keys';
18
- import { describe, test } from '@dxos/test';
19
- import { Doc, TextModel } from '@dxos/text-model';
20
- import { Timeframe } from '@dxos/timeframe';
21
-
22
- import { DatabaseTestBuilder } from '../testing/database-test-rig';
23
-
24
- describe('database (unit)', () => {
25
- test('create object and reload', async () => {
26
- const builder = new DatabaseTestBuilder();
27
- const peer = await builder.createPeer();
28
-
29
- const id = PublicKey.random().toHex();
30
- peer.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
31
- peer.proxy.commitBatch();
32
- peer.proxy.mutate(
33
- createModelMutation(id, encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('test', 42).build())),
34
- );
35
- peer.proxy.commitBatch();
36
- await peer.confirm();
37
-
38
- expect(peer.confirmed).toEqual(1);
39
- expect(peer.timeframe).toEqual(new Timeframe([[peer.key, 1]]));
40
-
41
- const state = peer.items.getItem(id)!.state;
42
-
43
- {
44
- await peer.reload();
45
- const stateAfterReload = peer.items.getItem(id)!.state;
46
-
47
- expect(stateAfterReload).toEqual(state);
48
- }
49
- });
50
-
51
- test('create object and reload from snapshot', async () => {
52
- const builder = new DatabaseTestBuilder();
53
- const peer = await builder.createPeer();
54
-
55
- const id = PublicKey.random().toHex();
56
- peer.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
57
- peer.proxy.commitBatch();
58
-
59
- peer.proxy.mutate(
60
- createModelMutation(id, encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('test', 42).build())),
61
- );
62
-
63
- peer.proxy.commitBatch();
64
- await peer.confirm();
65
- expect(peer.confirmed).toEqual(1);
66
- expect(peer.timeframe).toEqual(new Timeframe([[peer.key, 1]]));
67
- peer.makeSnapshot();
68
-
69
- const state = peer.items.getItem(id)!.state;
70
-
71
- {
72
- await peer.reload();
73
- const stateAfterReload = peer.items.getItem(id)!.state;
74
-
75
- expect(stateAfterReload).toEqual(state);
76
- }
77
- });
78
-
79
- test('two peers replicate', async () => {
80
- const builder = new DatabaseTestBuilder();
81
- const peer1 = await builder.createPeer();
82
- const peer2 = await builder.createPeer();
83
-
84
- const id = PublicKey.random().toHex();
85
- peer1.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
86
- peer1.proxy.mutate(
87
- createModelMutation(id, encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('test', 42).build())),
88
- );
89
- peer1.proxy.commitBatch();
90
- await peer1.confirm();
91
-
92
- peer2.replicate(peer1.timeframe);
93
-
94
- // TODO(dmaretskyi): Helper functions to compare state.
95
- expect(peer1.items.getItem(id)!.state).toEqual(peer1.items.getItem(id)!.state);
96
- });
97
-
98
- test('batches', async () => {
99
- const builder = new DatabaseTestBuilder();
100
- const peer1 = await builder.createPeer();
101
- const peer2 = await builder.createPeer();
102
-
103
- const id = PublicKey.random().toHex();
104
- peer1.proxy.beginBatch();
105
- peer1.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
106
- peer1.proxy.mutate(
107
- createModelMutation(id, encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('test', 42).build())),
108
- );
109
-
110
- await peer1.confirm();
111
- peer2.replicate(peer1.timeframe);
112
-
113
- expect(peer2.items.getItem(id)).toBeUndefined();
114
-
115
- peer1.proxy.commitBatch();
116
- await peer1.confirm();
117
- peer2.replicate(peer1.timeframe);
118
-
119
- // TODO(dmaretskyi): Helper functions to compare state.
120
- expect(peer1.items.getItem(id)!.state).toEqual(peer1.items.getItem(id)!.state);
121
- });
122
-
123
- test('flush', async () => {
124
- const builder = new DatabaseTestBuilder();
125
- const peer = await builder.createPeer();
126
-
127
- const id = PublicKey.random().toHex();
128
- peer.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
129
- await peer.confirm();
130
- const flushPromise = peer.proxy.flush();
131
- await peer.confirm();
132
- await flushPromise;
133
- }).timeout(100);
134
-
135
- describe('DocumentModel', () => {
136
- test('array assign and push', async () => {
137
- const builder = new DatabaseTestBuilder();
138
- const peer1 = await builder.createPeer();
139
- const id = PublicKey.random().toHex();
140
- const { updateEvent } = peer1.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
141
- const item = updateEvent.itemsUpdated[0] as Item<DocumentModel>;
142
- const model = new DocumentModel(DocumentModel.meta, item.id, () => item.state);
143
-
144
- peer1.proxy.mutate(
145
- createModelMutation(
146
- id,
147
- encodeModelMutation(
148
- DocumentModel.meta,
149
- model
150
- .builder()
151
- .set('tags', OrderedArray.fromValues(['red']))
152
- .build(),
153
- ),
154
- ),
155
- );
156
- await peer1.confirm();
157
- expect(model.get('tags').toArray()).toHaveLength(1);
158
-
159
- peer1.proxy.mutate(
160
- createModelMutation(
161
- id,
162
- encodeModelMutation(DocumentModel.meta, model.builder().set('tags', OrderedArray.fromValues([])).build()),
163
- ),
164
- );
165
- await peer1.confirm();
166
- expect(model.get('tags').toArray()).toHaveLength(0);
167
-
168
- peer1.proxy.mutate(
169
- createModelMutation(
170
- id,
171
- encodeModelMutation(DocumentModel.meta, model.builder().arrayPush('tags', ['green']).build()),
172
- ),
173
- );
174
- await peer1.confirm();
175
- expect(model.get('tags').toArray()).toHaveLength(1);
176
- });
177
- });
178
-
179
- describe('TextModel', () => {
180
- test('insert', async () => {
181
- const rig = new DatabaseTestBuilder();
182
- const peer1 = await rig.createPeer();
183
- const peer2 = await rig.createPeer();
184
-
185
- const mutations = [];
186
- const doc = new Doc();
187
- doc.on('update', (update) => {
188
- mutations.push(update);
189
- });
190
-
191
- const id = PublicKey.random().toHex();
192
- peer1.proxy.mutate(genesisMutation(id, TextModel.meta.type));
193
- const model = peer1.getModel(id);
194
- invariant(model instanceof TextModel);
195
- model.insert('Hello World!', 0);
196
-
197
- peer1.proxy.commitBatch();
198
- await peer1.confirm();
199
- await peer1.proxy.flush();
200
-
201
- peer2.replicate(peer1.timeframe);
202
- const replicatedItem = peer2.items.entities.get(id);
203
- expect(replicatedItem).toBeDefined();
204
- const replicatedModel = peer2.getModel(id)! as TextModel;
205
- expect(replicatedModel.textContent).toBe('Hello World!');
206
-
207
- replicatedModel.insert(' DXOS', 5);
208
-
209
- peer2.proxy.commitBatch();
210
- await peer2.confirm();
211
- await peer2.proxy.flush();
212
-
213
- peer1.replicate(peer2.timeframe);
214
- expect(model.textContent).toBe('Hello DXOS World!');
215
- });
216
- });
217
-
218
- describe('Mutations merging', () => {
219
- test('merge EchoObject-s for one item', async () => {
220
- const rig = new DatabaseTestBuilder();
221
- const peer1 = await rig.createPeer();
222
- const peer2 = await rig.createPeer();
223
-
224
- const mutations = [];
225
- const doc = new Doc();
226
- doc.on('update', (update) => {
227
- mutations.push(update);
228
- });
229
-
230
- const id = PublicKey.random().toHex();
231
- const begin = peer1.proxy.beginBatch();
232
- expect(begin).toBeTruthy();
233
- peer1.proxy.mutate(genesisMutation(id, TextModel.meta.type));
234
- const model = peer1.getModel(id);
235
- invariant(model instanceof TextModel);
236
- model.insert('Hello', 0);
237
- model.insert(' World!', 5);
238
-
239
- peer1.proxy.commitBatch();
240
- await peer1.confirm();
241
-
242
- // Mutations got merged.
243
- expect(peer1.feedMessages.length).toEqual(1);
244
-
245
- peer2.replicate(peer1.timeframe);
246
-
247
- const replicatedItem = peer2.items.entities.get(id);
248
- expect(replicatedItem).toBeDefined();
249
- const replicatedModel = peer2.getModel(id)! as TextModel;
250
- expect(replicatedModel.textContent).toBe('Hello World!');
251
-
252
- replicatedModel.insert(' DXOS', 5);
253
- peer2.proxy.commitBatch();
254
- await peer2.confirm();
255
-
256
- peer1.replicate(peer2.timeframe);
257
- expect(model.textContent).toBe('Hello DXOS World!');
258
- });
259
-
260
- test('consecutive mutations are batched', async () => {
261
- const rig = new DatabaseTestBuilder();
262
- const peer = await rig.createPeer();
263
-
264
- const id = PublicKey.random().toHex();
265
- peer.proxy.mutate(genesisMutation(id, TextModel.meta.type));
266
- const model = peer.getModel(id);
267
- invariant(model instanceof TextModel);
268
- model.insert('Hello', 0);
269
- model.insert(' World!', 5);
270
- // Wait for batch commit.
271
- await sleep(COMMIT_BATCH_INACTIVITY_DEFAULT + 10);
272
-
273
- // Mutations got merged.
274
- expect(peer.feedMessages.length).toEqual(1);
275
-
276
- model.insert(' DXOS', 5);
277
- // New batch is still not committed.
278
- expect(peer.feedMessages.length).toEqual(1);
279
- await sleep(COMMIT_BATCH_INACTIVITY_DEFAULT + 10);
280
- // New batch is committed.
281
- expect(peer.feedMessages.length).toEqual(2);
282
- });
283
- });
284
-
285
- it('epoch correctly resets items', async () => {
286
- const builder = new DatabaseTestBuilder();
287
- const peer = await builder.createPeer();
288
-
289
- const id = PublicKey.random().toHex();
290
- peer.proxy.mutate(genesisMutation(id, DocumentModel.meta.type));
291
- peer.proxy.commitBatch();
292
- peer.proxy.mutate(
293
- createModelMutation(id, encodeModelMutation(DocumentModel.meta, new MutationBuilder().set('test', 42).build())),
294
- );
295
- peer.proxy.commitBatch();
296
-
297
- await peer.confirm();
298
- expect(peer.confirmed).toEqual(1);
299
- expect(peer.timeframe).toEqual(new Timeframe([[peer.key, 1]]));
300
-
301
- const state = peer.items.getItem(id)!.state;
302
- const snapshotWithItem = peer.makeSnapshot().database;
303
-
304
- {
305
- // Reset on empty epoch.
306
- const updated = peer.proxy.itemUpdate.waitForCount(1);
307
-
308
- expect(peer.proxy._itemManager.items.length).toEqual(1);
309
-
310
- peer.createEpoch({});
311
-
312
- await updated;
313
- expect(peer.proxy._itemManager.items.length).toEqual(0);
314
- }
315
-
316
- {
317
- // Reset to epoch with one item.
318
- const updated = peer.proxy.itemUpdate.waitForCount(1);
319
- peer.createEpoch(snapshotWithItem);
320
- await updated;
321
- expect(peer.proxy._itemManager.items.length).toEqual(1);
322
- expect(peer.items.getItem(id)!.state).toEqual(state);
323
- }
324
- });
325
- });