hypercore 10.0.0-alpha.7 → 10.0.0

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.
package/test/oplog.js DELETED
@@ -1,399 +0,0 @@
1
- const p = require('path')
2
- const fs = require('fs')
3
- const test = require('brittle')
4
- const fsctl = require('fsctl')
5
- const raf = require('random-access-file')
6
- const c = require('compact-encoding')
7
-
8
- const Oplog = require('../lib/oplog')
9
-
10
- const STORAGE_FILE_NAME = 'oplog-test-storage'
11
- const STORAGE_FILE_PATH = p.join(__dirname, STORAGE_FILE_NAME)
12
- const SHOULD_ERROR = Symbol('hypercore-oplog-should-error')
13
-
14
- test.configure({ serial: true })
15
-
16
- test('oplog - reset storage', async function (t) {
17
- // just to make sure to cleanup storage if it failed half way through before
18
- if (fs.existsSync(STORAGE_FILE_PATH)) await fs.promises.unlink(STORAGE_FILE_PATH)
19
- t.pass('data is reset')
20
- t.end()
21
- })
22
-
23
- test('oplog - basic append', async function (t) {
24
- const storage = testStorage()
25
-
26
- const logWr = new Oplog(storage)
27
-
28
- await logWr.open()
29
- await logWr.flush(Buffer.from('h'))
30
- await logWr.append(Buffer.from('a'))
31
- await logWr.append(Buffer.from('b'))
32
-
33
- const logRd = new Oplog(storage)
34
-
35
- {
36
- const { header, entries } = await logRd.open()
37
-
38
- t.alike(header, Buffer.from('h'))
39
- t.is(entries.length, 2)
40
- t.alike(entries[0], Buffer.from('a'))
41
- t.alike(entries[1], Buffer.from('b'))
42
- }
43
-
44
- await logWr.flush(Buffer.from('i'))
45
-
46
- {
47
- const { header, entries } = await logRd.open()
48
-
49
- t.alike(header, Buffer.from('i'))
50
- t.is(entries.length, 0)
51
- }
52
-
53
- await logWr.append(Buffer.from('c'))
54
-
55
- {
56
- const { header, entries } = await logRd.open()
57
-
58
- t.alike(header, Buffer.from('i'))
59
- t.is(entries.length, 1)
60
- t.alike(entries[0], Buffer.from('c'))
61
- }
62
-
63
- await cleanup(storage)
64
- t.end()
65
- })
66
-
67
- test('oplog - custom encoding', async function (t) {
68
- const storage = testStorage()
69
-
70
- const log = new Oplog(storage, {
71
- headerEncoding: c.string,
72
- entryEncoding: c.uint
73
- })
74
-
75
- await log.open()
76
- await log.flush('one header')
77
- await log.append(42)
78
- await log.append(43)
79
-
80
- const { header, entries } = await log.open()
81
-
82
- t.is(header, 'one header')
83
- t.is(entries.length, 2)
84
- t.is(entries[0], 42)
85
- t.is(entries[1], 43)
86
-
87
- await cleanup(storage)
88
- t.end()
89
- })
90
-
91
- test('oplog - alternating header writes', async function (t) {
92
- const storage = testStorage()
93
-
94
- const log = new Oplog(storage)
95
-
96
- await log.open()
97
- await log.flush(Buffer.from('1'))
98
- await log.flush(Buffer.from('2'))
99
-
100
- {
101
- const { header } = await log.open()
102
- t.alike(header, Buffer.from('2'))
103
- }
104
-
105
- await log.flush(Buffer.from('1')) // Should overwrite first header
106
-
107
- {
108
- const { header } = await log.open()
109
- t.alike(header, Buffer.from('1'))
110
- }
111
-
112
- await log.flush(Buffer.from('2')) // Should overwrite second header
113
-
114
- {
115
- const { header } = await log.open()
116
- t.alike(header, Buffer.from('2'))
117
- }
118
-
119
- await cleanup(storage)
120
- t.end()
121
- })
122
-
123
- test('oplog - one fully-corrupted header', async function (t) {
124
- const storage = testStorage()
125
-
126
- const log = new Oplog(storage)
127
-
128
- await log.open()
129
- await log.flush(Buffer.from('header 1'))
130
-
131
- {
132
- const { header } = await log.open()
133
- t.alike(header, Buffer.from('header 1'))
134
- }
135
-
136
- await log.flush(Buffer.from('header 2'))
137
-
138
- {
139
- const { header } = await log.open()
140
- t.alike(header, Buffer.from('header 2'))
141
- }
142
-
143
- await log.flush(Buffer.from('header 3')) // should overwrite first header
144
-
145
- {
146
- const { header } = await log.open()
147
- t.alike(header, Buffer.from('header 3'))
148
- }
149
-
150
- // Corrupt the first header -- second header should win now
151
- await new Promise((resolve, reject) => {
152
- storage.write(0, Buffer.from('hello world'), err => {
153
- if (err) return reject(err)
154
- return resolve()
155
- })
156
- })
157
-
158
- {
159
- const { header } = await log.open()
160
- t.alike(header, Buffer.from('header 2'), 'one is corrupted or partially written')
161
- }
162
-
163
- await cleanup(storage)
164
- t.end()
165
- })
166
-
167
- test('oplog - header invalid checksum', async function (t) {
168
- const storage = testStorage()
169
-
170
- const log = new Oplog(storage)
171
-
172
- await log.open()
173
- await log.flush(Buffer.from('a'))
174
- await log.flush(Buffer.from('b'))
175
-
176
- {
177
- const { header } = await log.open()
178
- t.alike(header, Buffer.from('b'))
179
- }
180
-
181
- // Invalidate the first header's checksum -- second header should win now
182
- await new Promise((resolve, reject) => {
183
- storage.write(4096 + 8, Buffer.from('a'), err => {
184
- if (err) return reject(err)
185
- return resolve()
186
- })
187
- })
188
-
189
- {
190
- const { header } = await log.open()
191
- t.alike(header, Buffer.from('a'))
192
- }
193
-
194
- // Invalidate the second header's checksum -- the hypercore is now corrupted
195
- await new Promise((resolve, reject) => {
196
- storage.write(8, Buffer.from('b'), err => {
197
- if (err) return reject(err)
198
- return resolve()
199
- })
200
- })
201
-
202
- try {
203
- await log.open()
204
- t.fail('corruption should have been detected')
205
- } catch {
206
- t.pass('corruption was correctly detected')
207
- }
208
-
209
- await cleanup(storage)
210
- t.end()
211
- })
212
-
213
- test('oplog - malformed log entry gets overwritten', async function (t) {
214
- let storage = testStorage()
215
- let log = new Oplog(storage)
216
-
217
- await log.flush(Buffer.from('header'))
218
- await log.append(Buffer.from('a'))
219
- await log.append(Buffer.from('b'))
220
- await log.close()
221
-
222
- const offset = log.byteLength
223
-
224
- storage = testStorage()
225
- log = new Oplog(storage)
226
-
227
- // Write a bad oplog message at the end (simulates a failed append)
228
- await new Promise((resolve, reject) => {
229
- storage.write(offset + 4096 * 2, Buffer.from([0, 0, 0, 0, 4, 0, 0, 0]), err => {
230
- if (err) return reject(err)
231
- return resolve()
232
- })
233
- })
234
-
235
- {
236
- const { entries } = await log.open()
237
-
238
- t.is(entries.length, 2) // The partial entry should not be present
239
- t.alike(entries[0], Buffer.from('a'))
240
- t.alike(entries[1], Buffer.from('b'))
241
- }
242
-
243
- // Write a valid oplog message now
244
- await log.append(Buffer.from('c'))
245
-
246
- {
247
- const { entries } = await log.open()
248
-
249
- t.is(entries.length, 3) // The partial entry should not be present
250
- t.alike(entries[0], Buffer.from('a'))
251
- t.alike(entries[1], Buffer.from('b'))
252
- t.alike(entries[2], Buffer.from('c'))
253
- }
254
-
255
- await cleanup(storage)
256
- t.end()
257
- })
258
-
259
- test('oplog - log not truncated when header write fails', async function (t) {
260
- const storage = failingOffsetStorage(4096 * 2)
261
-
262
- const log = new Oplog(storage)
263
-
264
- await log.flush(Buffer.from('header'))
265
- await log.append(Buffer.from('a'))
266
- await log.append(Buffer.from('b'))
267
-
268
- // Make subsequent header writes fail
269
- storage[SHOULD_ERROR](true)
270
-
271
- // The flush should fail because the header can't be updated -- log should still have entries after this
272
- try {
273
- await log.flush(Buffer.from('header two'))
274
- } catch (err) {
275
- t.ok(err.synthetic)
276
- }
277
-
278
- {
279
- const { header, entries } = await log.open()
280
-
281
- t.alike(header, Buffer.from('header'))
282
- t.is(entries.length, 2)
283
- t.alike(entries[0], Buffer.from('a'))
284
- t.alike(entries[1], Buffer.from('b'))
285
- }
286
-
287
- // Re-enable header writes
288
- storage[SHOULD_ERROR](false)
289
- await log.flush(Buffer.from('header two')) // Should correctly truncate the oplog now
290
-
291
- {
292
- const { header, entries } = await log.open()
293
-
294
- t.alike(header, Buffer.from('header two'))
295
- t.is(entries.length, 0)
296
- }
297
-
298
- await cleanup(storage)
299
- t.end()
300
- })
301
-
302
- test('oplog - multi append', async function (t) {
303
- const storage = testStorage()
304
-
305
- const log = new Oplog(storage)
306
-
307
- await log.open()
308
- await log.flush(Buffer.from('a'))
309
-
310
- await log.append([
311
- Buffer.from('1'),
312
- Buffer.from('22'),
313
- Buffer.from('333'),
314
- Buffer.from('4')
315
- ])
316
-
317
- t.is(log.length, 4)
318
- t.is(log.byteLength, 32 + 1 + 2 + 3 + 1)
319
-
320
- const { header, entries } = await log.open()
321
-
322
- t.alike(header, Buffer.from('a'))
323
- t.alike(entries, [
324
- Buffer.from('1'),
325
- Buffer.from('22'),
326
- Buffer.from('333'),
327
- Buffer.from('4')
328
- ])
329
-
330
- await cleanup(storage)
331
- t.end()
332
- })
333
-
334
- test('oplog - multi append is atomic', async function (t) {
335
- const storage = testStorage()
336
-
337
- const log = new Oplog(storage)
338
-
339
- await log.open()
340
- await log.flush(Buffer.from('a'))
341
-
342
- await log.append(Buffer.from('0'))
343
- await log.append([
344
- Buffer.from('1'),
345
- Buffer.from('22'),
346
- Buffer.from('333'),
347
- Buffer.from('4')
348
- ])
349
-
350
- t.is(log.length, 5)
351
- t.is(log.byteLength, 40 + 1 + 1 + 2 + 3 + 1)
352
-
353
- // Corrupt the last write, should revert the full batch
354
- await new Promise((resolve, reject) => {
355
- storage.write(8192 + log.byteLength - 1, Buffer.from('x'), err => {
356
- if (err) return reject(err)
357
- return resolve()
358
- })
359
- })
360
-
361
- const { entries } = await log.open()
362
-
363
- t.is(log.length, 1)
364
- t.alike(entries, [
365
- Buffer.from('0')
366
- ])
367
-
368
- await cleanup(storage)
369
- t.end()
370
- })
371
-
372
- function testStorage () {
373
- return raf(STORAGE_FILE_NAME, { directory: __dirname, lock: fsctl.lock })
374
- }
375
-
376
- function failingOffsetStorage (offset) {
377
- let shouldError = false
378
- const storage = raf(STORAGE_FILE_NAME, { directory: __dirname, lock: fsctl.lock })
379
- const write = storage.write.bind(storage)
380
-
381
- storage.write = (off, data, cb) => {
382
- if (off < offset && shouldError) {
383
- const err = new Error('Synthetic write failure')
384
- err.synthetic = true
385
- return cb(err)
386
- }
387
- return write(off, data, cb)
388
- }
389
- storage[SHOULD_ERROR] = s => {
390
- shouldError = s
391
- }
392
-
393
- return storage
394
- }
395
-
396
- async function cleanup (storage) {
397
- await new Promise((resolve) => storage.close(() => resolve()))
398
- await fs.promises.unlink(STORAGE_FILE_PATH)
399
- }
package/test/preload.js DELETED
@@ -1,72 +0,0 @@
1
- const crypto = require('hypercore-crypto')
2
- const test = require('brittle')
3
- const ram = require('random-access-memory')
4
- const Hypercore = require('../')
5
-
6
- test('preload - storage', async function (t) {
7
- const core = new Hypercore(null, {
8
- preload: () => {
9
- return { storage: ram }
10
- }
11
- })
12
- await core.ready()
13
-
14
- await core.append('hello world')
15
- t.is(core.length, 1)
16
- t.alike(await core.get(0), Buffer.from('hello world'))
17
-
18
- t.end()
19
- })
20
-
21
- test('preload - from another core', async function (t) {
22
- t.plan(2)
23
-
24
- const first = new Hypercore(ram)
25
- await first.ready()
26
-
27
- const second = new Hypercore(null, {
28
- preload: () => {
29
- return { from: first }
30
- }
31
- })
32
- await second.ready()
33
-
34
- t.is(first.key, second.key)
35
- t.is(first.sessions, second.sessions)
36
- })
37
-
38
- test('preload - custom keypair', async function (t) {
39
- const keyPair = crypto.keyPair()
40
- const core = new Hypercore(ram, keyPair.publicKey, {
41
- preload: () => {
42
- return { keyPair }
43
- }
44
- })
45
- await core.ready()
46
-
47
- t.ok(core.writable)
48
- t.is(core.key, keyPair.publicKey)
49
-
50
- t.end()
51
- })
52
-
53
- test('preload - sign/storage', async function (t) {
54
- const keyPair = crypto.keyPair()
55
- const core = new Hypercore(null, keyPair.publicKey, {
56
- valueEncoding: 'utf-8',
57
- preload: () => {
58
- return {
59
- storage: ram,
60
- sign: signable => crypto.sign(signable, keyPair.secretKey)
61
- }
62
- }
63
- })
64
- await core.ready()
65
-
66
- t.ok(core.writable)
67
- await core.append('hello world')
68
- t.is(core.length, 1)
69
- t.is(await core.get(0), 'hello world')
70
-
71
- t.end()
72
- })