corestore 6.0.0-beta2 → 6.0.1-alpha.12

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/all.js CHANGED
@@ -1,302 +1,207 @@
1
- const p = require('path')
1
+ const { test, configure } = require('brittle')
2
+ const crypto = require('hypercore-crypto')
2
3
  const ram = require('random-access-memory')
3
- const raf = require('random-access-file')
4
- const test = require('tape')
5
- const hypercoreCrypto = require('hypercore-crypto')
6
- const { toPromises } = require('hypercore-promisifier')
7
- const OldCorestore = require('@andrewosh/corestore-migration')
4
+ const tmp = require('tmp-promise')
8
5
 
9
6
  const Corestore = require('..')
10
- const { cleanup } = require('./helpers')
11
7
 
12
- test('ram-based corestore, acceptable get options', async t => {
13
- const store = create(ram)
8
+ configure({ serial: true })
14
9
 
15
- // A name option
16
- const core1 = store.get({ name: 'default' })
17
- await toPromises(core1).ready()
10
+ test('basic get with caching', async function (t) {
11
+ const store = new Corestore(ram)
12
+ const core1a = store.get({ name: 'core-1' })
13
+ const core1b = store.get({ name: 'core-1' })
14
+ const core2 = store.get({ name: 'core-2' })
18
15
 
19
- {
20
- // Buffer arg
21
- const core = store.get(core1.key)
22
- await toPromises(core).ready()
23
- t.same(core, core1)
24
- }
25
-
26
- {
27
- // String arg
28
- const core = store.get(core1.key.toString('hex'))
29
- await toPromises(core).ready()
30
- t.same(core, core1)
31
- }
16
+ await Promise.all([core1a.ready(), core1b.ready(), core2.ready()])
32
17
 
33
- {
34
- // Object arg
35
- const core = store.get({ key: core1.key })
36
- await toPromises(core).ready()
37
- t.same(core, core1)
38
- }
18
+ t.alike(core1a.key, core1b.key)
19
+ t.unlike(core1a.key, core2.key)
39
20
 
40
- {
41
- // Object arg with string key
42
- const core = store.get({ key: core1.key.toString('hex') })
43
- await toPromises(core).ready()
44
- t.same(core, core1)
45
- }
46
-
47
- {
48
- // Custom keypair
49
- const core = store.get({ keyPair: { secretKey: core1.secretKey, publicKey: core1.key } })
50
- await toPromises(core).ready()
51
- t.same(core, core1)
52
- }
21
+ t.ok(core1a.writable)
22
+ t.ok(core1b.writable)
53
23
 
54
- t.end()
55
- })
56
-
57
- test('ram-based corestore, unacceptable get options', async t => {
58
- const store = create(ram)
59
- const badGets = [
60
- () => store.get(),
61
- () => store.get('abc'),
62
- () => store.get({ name: null }),
63
- () => store.get({ key: null })
64
- ]
65
- for (const get of badGets) {
66
- try {
67
- get()
68
- t.fail('get did not throw correctly')
69
- } catch (err) {
70
- t.true(err)
71
- }
72
- }
24
+ t.is(store.cores.size, 2)
73
25
  })
74
26
 
75
- test('ram-based corestore, many gets before ready', async t => {
76
- const store = create(ram)
77
- const core1 = store.get({ name: 'core1' })
78
- const core2 = store.get({ name: 'core1' })
79
- const core3 = store.get({ name: 'core3' })
80
-
81
- await Promise.all([
82
- toPromises(core1).ready(),
83
- toPromises(core2).ready(),
84
- toPromises(core3).ready()
85
- ])
86
-
87
- t.same(core1, core2)
88
- t.notSame(core1, core3)
27
+ test('basic get with custom keypair', async function (t) {
28
+ const store = new Corestore(ram)
29
+ const kp1 = crypto.keyPair()
30
+ const kp2 = crypto.keyPair()
89
31
 
90
- // At this point, the pre-ready cores should've been moved to the main cache.
91
- t.same(store.cache.size, 2)
32
+ const core1 = store.get(kp1)
33
+ const core2 = store.get(kp2)
34
+ await Promise.all([core1.ready(), core2.ready()])
92
35
 
93
- t.end()
36
+ t.alike(core1.key, kp1.publicKey)
37
+ t.alike(core2.key, kp2.publicKey)
38
+ t.ok(core1.writable)
39
+ t.ok(core2.writable)
94
40
  })
95
41
 
96
- test('ram-based corestore, closing a ref decrements refcount', async t => {
97
- const store = create(ram)
98
- const core = toPromises(store.get({ name: 'core1' }))
99
- await core.ready()
42
+ test('basic namespaces', async function (t) {
43
+ const store = new Corestore(ram)
44
+ const ns1 = store.namespace('ns1')
45
+ const ns2 = store.namespace('ns2')
46
+ const ns3 = store.namespace('ns1') // Duplicate namespace
100
47
 
101
- const cacheEntry = store.cache.entries.get(core.discoveryKey.toString('hex'))
102
- t.same(cacheEntry.refs, 1)
48
+ const core1 = ns1.get({ name: 'main' })
49
+ const core2 = ns2.get({ name: 'main' })
50
+ const core3 = ns3.get({ name: 'main' })
51
+ await Promise.all([core1.ready(), core2.ready(), core3.ready()])
103
52
 
104
- await core.close()
105
- t.same(cacheEntry.refs, 0)
53
+ t.absent(core1.key.equals(core2.key))
54
+ t.ok(core1.key.equals(core3.key))
55
+ t.ok(core1.writable)
56
+ t.ok(core2.writable)
57
+ t.ok(core3.writable)
58
+ t.is(store.cores.size, 2)
106
59
 
107
60
  t.end()
108
61
  })
109
62
 
110
- test('ram-based corestore, simple replication', async t => {
111
- const store1 = create(ram)
112
- const store2 = create(ram)
63
+ test('basic replication', async function (t) {
64
+ const store1 = new Corestore(ram)
65
+ const store2 = new Corestore(ram)
113
66
 
114
- const core1 = toPromises(store1.get({ name: 'core1', valueEncoding: 'utf-8' }))
67
+ const core1 = store1.get({ name: 'core-1' })
68
+ const core2 = store1.get({ name: 'core-2' })
115
69
  await core1.append('hello')
116
-
117
- const s1 = store1.replicate(true, { live: true })
118
- s1.pipe(store2.replicate(false, { live: true })).pipe(s1)
119
-
120
- const clone1 = toPromises(store2.get({ key: core1.key, valueEncoding: 'utf-8' }))
121
- t.same(await clone1.get(0), 'hello')
122
-
123
- const core2 = toPromises(store1.get({ name: 'core2', valueEncoding: 'utf-8' }))
124
70
  await core2.append('world')
125
71
 
126
- const clone2 = toPromises(store2.get({ key: core2.key, valueEncoding: 'utf-8' }))
127
- t.same(await clone2.get(0), 'world')
72
+ const core3 = store2.get({ key: core1.key })
73
+ const core4 = store2.get({ key: core2.key })
128
74
 
129
- t.end()
130
- })
75
+ const s = store1.replicate(true)
76
+ s.pipe(store2.replicate(false)).pipe(s)
131
77
 
132
- test('ram-based corestore, sparse replication', async t => {
133
- const store1 = create(ram, { sparse: true })
134
- const store2 = create(ram, { sparse: true })
135
-
136
- const core1 = toPromises(store1.get({ name: 'core1', valueEncoding: 'utf-8' }))
137
- await core1.append('hello')
78
+ t.alike(await core3.get(0), Buffer.from('hello'))
79
+ t.alike(await core4.get(0), Buffer.from('world'))
80
+ })
138
81
 
139
- const s1 = store1.replicate(true, { live: true })
140
- s1.pipe(store2.replicate(false, { live: true })).pipe(s1)
82
+ test('replicating cores created after replication begins', async function (t) {
83
+ const store1 = new Corestore(ram)
84
+ const store2 = new Corestore(ram)
141
85
 
142
- const clone1 = toPromises(store2.get({ key: core1.key, valueEncoding: 'utf-8' }))
143
- t.same(await clone1.get(0), 'hello')
86
+ const s = store1.replicate(true, { live: true })
87
+ s.pipe(store2.replicate(false, { live: true })).pipe(s)
144
88
 
145
- const core2 = toPromises(store1.get({ name: 'core2', valueEncoding: 'utf-8' }))
89
+ const core1 = store1.get({ name: 'core-1' })
90
+ const core2 = store1.get({ name: 'core-2' })
91
+ await core1.append('hello')
146
92
  await core2.append('world')
147
93
 
148
- const clone2 = toPromises(store2.get({ key: core2.key, valueEncoding: 'utf-8' }))
149
- t.same(await clone2.get(0), 'world')
94
+ const core3 = store2.get({ key: core1.key })
95
+ const core4 = store2.get({ key: core2.key })
150
96
 
151
- t.end()
97
+ t.alike(await core3.get(0), Buffer.from('hello'))
98
+ t.alike(await core4.get(0), Buffer.from('world'))
152
99
  })
153
100
 
154
- test('raf-based corestore, simple replication', async t => {
155
- const store1 = create(path => raf(p.join('store1', path)))
156
- const store2 = create(path => raf(p.join('store2', path)))
157
-
158
- const core1 = toPromises(store1.get({ name: 'core1', valueEncoding: 'utf-8' }))
159
- await core1.append('hello')
101
+ test('replicating cores using discovery key hook', async function (t) {
102
+ const dir = await tmp.dir({ unsafeCleanup: true })
103
+ let store1 = new Corestore(dir.path)
104
+ const store2 = new Corestore(ram)
160
105
 
161
- const s1 = store1.replicate(true, { live: true })
162
- s1.pipe(store2.replicate(false, { live: true })).pipe(s1)
106
+ const core = store1.get({ name: 'main' })
107
+ await core.append('hello')
108
+ const key = core.key
163
109
 
164
- const clone1 = toPromises(store2.get({ key: core1.key, valueEncoding: 'utf-8' }))
165
- t.same(await clone1.get(0), 'hello')
110
+ await store1.close()
111
+ store1 = new Corestore(dir.path)
166
112
 
167
- const core2 = toPromises(store1.get({ name: 'core2', valueEncoding: 'utf-8' }))
168
- await core2.append('world')
113
+ const s = store1.replicate(true, { live: true })
114
+ s.pipe(store2.replicate(false, { live: true })).pipe(s)
169
115
 
170
- const clone2 = toPromises(store2.get({ key: core2.key, valueEncoding: 'utf-8' }))
171
- t.same(await clone2.get(0), 'world')
116
+ const core2 = store2.get(key)
117
+ t.alike(await core2.get(0), Buffer.from('hello'))
172
118
 
173
- await cleanup(['store1', 'store2'])
174
- t.end()
119
+ await dir.cleanup()
175
120
  })
176
121
 
177
- test('raf-based corestore, close and reopen', async t => {
178
- let store = create('test-store')
122
+ test('nested namespaces', async function (t) {
123
+ const store = new Corestore(ram)
124
+ const ns1a = store.namespace('ns1').namespace('a')
125
+ const ns1b = store.namespace('ns1').namespace('b')
179
126
 
180
- let core1 = toPromises(store.get({ name: 'core1', valueEncoding: 'utf-8' }))
181
- await core1.append('hello')
182
-
183
- t.same(await core1.get(0), 'hello')
184
-
185
- await store.close()
186
- store = create('test-store')
187
- core1 = toPromises(store.get({ name: 'core1', valueEncoding: 'utf-8' }))
127
+ const core1 = ns1a.get({ name: 'main' })
128
+ const core2 = ns1b.get({ name: 'main' })
129
+ await Promise.all([core1.ready(), core2.ready()])
188
130
 
189
- t.same(await core1.get(0), 'hello')
131
+ t.not(core1.key.equals(core2.key))
132
+ t.ok(core1.writable)
133
+ t.ok(core2.writable)
134
+ t.is(store.cores.size, 2)
135
+ })
190
136
 
191
- await cleanup(['test-store'])
192
- t.end()
137
+ test('core uncached when all sessions close', async function (t) {
138
+ const store = new Corestore(ram)
139
+ const core1 = store.get({ name: 'main' })
140
+ await core1.ready()
141
+ t.is(store.cores.size, 1)
142
+ await core1.close()
143
+ t.is(store.cores.size, 0)
193
144
  })
194
145
 
195
- test('raf-based corestore, close and reopen with keypair option', async t => {
196
- let store = create('test-store')
197
- const keyPair = hypercoreCrypto.keyPair()
146
+ test('writable core loaded from name userData', async function (t) {
147
+ const dir = await tmp.dir({ unsafeCleanup: true })
198
148
 
199
- let core1 = toPromises(store.get({ keyPair, valueEncoding: 'utf-8' }))
200
- await core1.append('hello')
149
+ let store = new Corestore(dir.path)
150
+ let core = store.get({ name: 'main' })
151
+ await core.ready()
152
+ const key = core.key
201
153
 
202
- t.same(await core1.get(0), 'hello')
154
+ t.ok(core.writable)
155
+ await core.append('hello')
156
+ t.is(core.length, 1)
203
157
 
204
158
  await store.close()
205
- store = create('test-store')
206
- core1 = toPromises(store.get({ keyPair, valueEncoding: 'utf-8' }))
207
-
208
- t.same(await core1.get(0), 'hello')
209
-
210
- await cleanup(['test-store'])
211
- t.end()
212
- })
213
-
214
- test('namespace method is equivalent to name array', async t => {
215
- const store = create(ram)
216
-
217
- const core1 = store.get({ name: ['a', 'b', 'c'] })
218
- const core2 = store.namespace('a').namespace('b').get({ name: 'c' })
159
+ store = new Corestore(dir.path)
160
+ core = store.get(key)
161
+ await core.ready()
219
162
 
220
- await toPromises(core1).ready()
221
- await toPromises(core2).ready()
163
+ t.ok(core.writable)
164
+ await core.append('world')
165
+ t.is(core.length, 2)
166
+ t.alike(await core.get(0), Buffer.from('hello'))
167
+ t.alike(await core.get(1), Buffer.from('world'))
222
168
 
223
- t.same(core1, core2)
224
- t.end()
169
+ await dir.cleanup()
225
170
  })
226
171
 
227
- test('can backup/restore', async t => {
228
- const firstStore = create(ram)
229
- const core1 = firstStore.get({ name: 'hello-world' })
230
- await toPromises(core1).ready()
231
- const manifest = await firstStore.backup()
172
+ test('storage locking', async function (t) {
173
+ const dir = await tmp.dir({ unsafeCleanup: true })
232
174
 
233
- const secondStore = await Corestore.restore(manifest, ram)
234
- const core2 = secondStore.get({ key: core1.key })
235
- await toPromises(core2).ready()
175
+ const store1 = new Corestore(dir.path)
176
+ await store1.ready()
236
177
 
237
- t.true(core2.writable)
178
+ const store2 = new Corestore(dir.path)
179
+ try {
180
+ await store2.ready()
181
+ t.fail('dir should have been locked')
182
+ } catch {
183
+ t.pass('dir was locked')
184
+ }
238
185
 
239
- t.end()
186
+ await dir.cleanup()
240
187
  })
241
188
 
242
- test('can migrate an old corestore', async t => {
243
- const oldStore = new OldCorestore('test-store')
244
- await oldStore.ready()
245
-
246
- const oldCores = [
247
- oldStore.default(),
248
- oldStore.namespace('hello').default(),
249
- oldStore.namespace('hello').namespace('world').default(),
250
- oldStore.get({ name: 'test-name' }),
251
- // Randomly-generated names should also be migrated.
252
- oldStore.get()
253
- ]
254
- await Promise.all(oldCores.map(c => new Promise(resolve => c.ready(resolve))))
255
-
256
- const names = [
257
- 'default',
258
- ['hello'],
259
- ['hello', 'world'],
260
- 'test-name'
261
- ]
262
- const keys = oldCores.map(c => c.key)
263
- await new Promise(resolve => oldStore.close(resolve))
264
-
265
- {
266
- const store = create('test-store', {
267
- migrationRoot: 'test-store'
268
- })
269
-
270
- // Cores are writable when loaded by name
271
- for (let i = 0; i < names.length; i++) {
272
- const core = store.get({ name: names[i] })
273
- await toPromises(core).ready()
274
- t.true(core.writable)
275
- t.same(core.key, keys[i])
276
- }
277
-
278
- await store.close()
279
- }
189
+ test('closing a namespace does not close cores', async function (t) {
190
+ const store = new Corestore(ram)
191
+ const ns1 = store.namespace('ns1')
192
+ const core1 = ns1.get({ name: 'core-1' })
193
+ const core2 = ns1.get({ name: 'core-2' })
194
+ await Promise.all([core1.ready(), core2.ready()])
280
195
 
281
- {
282
- // The second time should work without a migration.
283
- const store = create('test-store')
196
+ await ns1.close()
284
197
 
285
- // Cores are writable when loaded by key
286
- for (let i = 0; i < keys.length; i++) {
287
- const core = store.get({ key: keys[i] })
288
- await toPromises(core).ready()
289
- t.true(core.writable)
290
- }
198
+ t.is(store.cores.size, 2)
199
+ t.not(core1.closed)
200
+ t.not(core1.closed)
291
201
 
292
- await store.close()
293
- }
202
+ await store.close()
294
203
 
295
- await cleanup(['test-store'])
296
- t.end()
204
+ t.is(store.cores.size, 0)
205
+ t.ok(core1.closed)
206
+ t.ok(core2.closed)
297
207
  })
298
-
299
- function create (storage, opts) {
300
- const store = new Corestore(storage, opts)
301
- return store
302
- }
@@ -1,12 +1,7 @@
1
- const fs = require('fs')
1
+ const fs = require('fs').promises
2
2
 
3
3
  async function cleanup (dirs) {
4
- return Promise.all(dirs.map(dir => new Promise((resolve, reject) => {
5
- fs.rmdir(dir, { recursive: true }, err => {
6
- if (err) return reject(err)
7
- return resolve()
8
- })
9
- })))
4
+ return Promise.allSettled(dirs.map(dir => fs.rmdir(dir, { recursive: true })))
10
5
  }
11
6
 
12
7
  function delay (ms, cb) {
package/test/keys.js ADDED
@@ -0,0 +1,65 @@
1
+ const p = require('path')
2
+ const fs = require('fs')
3
+
4
+ const test = require('brittle')
5
+ const ram = require('random-access-memory')
6
+ const raf = require('random-access-file')
7
+
8
+ const KeyManager = require('../lib/keys')
9
+
10
+ test('can create hypercore keypairs', async t => {
11
+ const keys = await KeyManager.fromStorage(ram)
12
+
13
+ const kp1 = await keys.createHypercoreKeyPair('core1')
14
+ const kp2 = await keys.createHypercoreKeyPair('core2')
15
+
16
+ t.is(kp1.publicKey.length, 32)
17
+ t.is(kp2.publicKey.length, 32)
18
+ t.unlike(kp1.publicKey, kp2.publicKey)
19
+ })
20
+
21
+ test('distinct tokens create distinct hypercore keypairs', async t => {
22
+ const keys = await KeyManager.fromStorage(ram)
23
+ const token1 = KeyManager.createToken()
24
+ const token2 = KeyManager.createToken()
25
+
26
+ const kp1 = await keys.createHypercoreKeyPair('core1', token1)
27
+ const kp2 = await keys.createHypercoreKeyPair('core1', token2)
28
+
29
+ t.unlike(kp1.publicKey, kp2.publicKey)
30
+ })
31
+
32
+ test('short user-provided token will throw', async t => {
33
+ const keys = await KeyManager.fromStorage(ram)
34
+
35
+ try {
36
+ await keys.createHypercoreKeyPair('core1', Buffer.from('hello'))
37
+ t.fail('did not throw')
38
+ } catch {
39
+ t.pass('threw correctly')
40
+ }
41
+ })
42
+
43
+ test('persistent storage regenerates keys correctly', async t => {
44
+ const testPath = p.resolve(__dirname, 'test-data')
45
+
46
+ const keys1 = await KeyManager.fromStorage((name) => raf(testPath, { directory: testPath }))
47
+ const kp1 = await keys1.createHypercoreKeyPair('core1')
48
+
49
+ const keys2 = await KeyManager.fromStorage((name) => raf(testPath, { directory: testPath }))
50
+ const kp2 = await keys2.createHypercoreKeyPair('core1')
51
+
52
+ t.alike(kp1.publicKey, kp2.publicKey)
53
+
54
+ await fs.promises.rm(testPath, { recursive: true })
55
+ })
56
+
57
+ test('different master keys -> different keys', async t => {
58
+ const keys1 = await KeyManager.fromStorage(ram)
59
+ const keys2 = await KeyManager.fromStorage(ram)
60
+
61
+ const kp1 = await keys1.createHypercoreKeyPair('core1')
62
+ const kp2 = await keys2.createHypercoreKeyPair('core1')
63
+
64
+ t.unlike(kp1.publicKey, kp2.publicKey)
65
+ })
@@ -1,26 +0,0 @@
1
- const RAS = require('random-access-storage')
2
-
3
- module.exports = class BufferFile extends RAS {
4
- constructor (buf) {
5
- super()
6
- this._buf = buf
7
- }
8
-
9
- _open (req) {
10
- if (typeof this._buf !== 'function') return req.callback(null)
11
- this._buf((err, b) => {
12
- if (err) return req.callback(err)
13
- this._buf = b
14
- req.callback(null)
15
- })
16
- }
17
-
18
- _stat (req) {
19
- req.callback(null, { size: this._buf ? this._buf.length : 0 })
20
- }
21
-
22
- _read (req) {
23
- if (!this._buf) return req.callback(new Error('Could not satisfy length'))
24
- req.callback(null, this._buf.slice(req.offset, req.offset + req.size))
25
- }
26
- }