New lifecycle management for GunSmith
This commit is contained in:
parent
a41862c7b0
commit
1dc71d31c2
2 changed files with 125 additions and 79 deletions
|
|
@ -99,7 +99,7 @@ const handleMsg = msg => {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Could not find request for put message from gun subprocess. Data will be logged below.`
|
`Could not find request for put message from gun subprocess. Data will be logged below.`
|
||||||
)
|
)
|
||||||
logger.info({
|
console.log({
|
||||||
msg,
|
msg,
|
||||||
pendingPut: pendingPut || 'No pending put found',
|
pendingPut: pendingPut || 'No pending put found',
|
||||||
allPendingPuts: pendingPuts
|
allPendingPuts: pendingPuts
|
||||||
|
|
@ -240,7 +240,12 @@ const isReady = () =>
|
||||||
|
|
||||||
let procID = 0
|
let procID = 0
|
||||||
|
|
||||||
|
let killed = false
|
||||||
|
|
||||||
const forge = async () => {
|
const forge = async () => {
|
||||||
|
if (killed) {
|
||||||
|
throw new Error('Tried to forge after killing GunSmith')
|
||||||
|
}
|
||||||
logger.info(`Forging Gun # ${++procID}`)
|
logger.info(`Forging Gun # ${++procID}`)
|
||||||
if (isForging) {
|
if (isForging) {
|
||||||
throw new Error('Double forge?')
|
throw new Error('Double forge?')
|
||||||
|
|
@ -453,11 +458,12 @@ function createReplica(path, afterMap = false) {
|
||||||
return this
|
return this
|
||||||
},
|
},
|
||||||
once(cb, opts = { wait: 500 }) {
|
once(cb, opts = { wait: 500 }) {
|
||||||
// We could use this.on() but then we couldn't call .off()
|
|
||||||
const tmp = createReplica(path, afterMap)
|
|
||||||
if (afterMap) {
|
if (afterMap) {
|
||||||
throw new Error('Cannot call once() after map() on a GunSmith node')
|
throw new Error('Cannot call once() after map() on a GunSmith node')
|
||||||
}
|
}
|
||||||
|
// We could use this.on() but then we couldn't call .off()
|
||||||
|
const tmp = createReplica(path, afterMap)
|
||||||
|
|
||||||
/** @type {GunT.ListenerData} */
|
/** @type {GunT.ListenerData} */
|
||||||
let lastVal = null
|
let lastVal = null
|
||||||
|
|
||||||
|
|
@ -713,28 +719,28 @@ function createUserReplica() {
|
||||||
return completeReplica
|
return completeReplica
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Smith.GunSmithNode & { kill(): void }} RootNode
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import('gun/types/options').IGunConstructorOptions} opts
|
* @param {import('gun/types/options').IGunConstructorOptions} opts
|
||||||
* @returns {RootNode}
|
* @returns {Smith.GunSmithNode}
|
||||||
*/
|
*/
|
||||||
const Gun = opts => {
|
const Gun = opts => {
|
||||||
lastOpts = opts
|
lastOpts = opts
|
||||||
forge()
|
forge()
|
||||||
|
|
||||||
// We should ideally wait for a response but we'd break the constructor's
|
return createReplica('$root')
|
||||||
// signature
|
|
||||||
return {
|
|
||||||
...createReplica('$root'),
|
|
||||||
kill() {
|
|
||||||
currentGun.send('bye')
|
|
||||||
currentGun.disconnect()
|
|
||||||
currentGun.kill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Gun
|
module.exports = Gun
|
||||||
|
|
||||||
|
module.exports.kill = () => {
|
||||||
|
if (currentGun) {
|
||||||
|
currentGun.send('bye')
|
||||||
|
currentGun.off('message', handleMsg)
|
||||||
|
currentGun.disconnect()
|
||||||
|
currentGun.kill()
|
||||||
|
// @ts-ignore
|
||||||
|
currentGun = null
|
||||||
|
killed = true
|
||||||
|
logger.info('Killed gunsmith.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,25 +8,54 @@ const Gun = require('./GunSmith')
|
||||||
const words = require('random-words')
|
const words = require('random-words')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const logger = require('../../config/log')
|
||||||
|
|
||||||
const { removeBuiltInGunProps } = require('./misc')
|
const { removeBuiltInGunProps } = require('./misc')
|
||||||
|
|
||||||
if (!fs.existsSync('./test-radata')) {
|
if (!fs.existsSync('./test-radata')) {
|
||||||
fs.mkdirSync('./test-radata')
|
fs.mkdirSync('./test-radata')
|
||||||
}
|
}
|
||||||
|
|
||||||
const createInstance = () =>
|
// start with true, have first test doesn't check, else all other test start to
|
||||||
Gun({
|
// run right away
|
||||||
axe: false,
|
let isBusy = true
|
||||||
multicast: false,
|
|
||||||
file: './test-radata/' + words({ exactly: 2 }).join('-')
|
const instance = Gun({
|
||||||
|
axe: false,
|
||||||
|
multicast: false,
|
||||||
|
file: './test-radata/' + words({ exactly: 2 }).join('-')
|
||||||
|
})
|
||||||
|
|
||||||
|
const release = () => {
|
||||||
|
isBusy = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const whenReady = () =>
|
||||||
|
new Promise(res => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isBusy) {
|
||||||
|
whenReady().then(res)
|
||||||
|
} else {
|
||||||
|
isBusy = true
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('gun smith', () => {
|
describe('gun smith', () => {
|
||||||
it('puts a true and reads it with once()', done => {
|
afterAll(() => {
|
||||||
expect.hasAssertions()
|
Gun.kill()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('puts a true and reads it with once()', async function(done) {
|
||||||
|
// await whenReady()
|
||||||
|
logger.info('puts a true and reads it with once()')
|
||||||
const a = words()
|
const a = words()
|
||||||
const b = words()
|
const b = words()
|
||||||
const instance = createInstance()
|
|
||||||
instance
|
instance
|
||||||
.get(a)
|
.get(a)
|
||||||
.get(b)
|
.get(b)
|
||||||
|
|
@ -37,39 +66,53 @@ describe('gun smith', () => {
|
||||||
.get(b)
|
.get(b)
|
||||||
.once(
|
.once(
|
||||||
val => {
|
val => {
|
||||||
expect(val).toStrictEqual(true)
|
expect(val).toBe(true)
|
||||||
instance.kill()
|
|
||||||
done()
|
done()
|
||||||
|
release()
|
||||||
},
|
},
|
||||||
{ wait: 5000 }
|
{ wait: 1000 }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('puts a false and reads it with once()', done => {
|
it('puts a false and reads it with once()', async function(done) {
|
||||||
expect.hasAssertions()
|
await whenReady()
|
||||||
|
logger.info('puts a false and reads it with once()')
|
||||||
const a = words()
|
const a = words()
|
||||||
const b = words()
|
const b = words()
|
||||||
const instance = createInstance()
|
|
||||||
instance
|
await new Promise((res, rej) => {
|
||||||
.get(a)
|
instance
|
||||||
.get(b)
|
.get(a)
|
||||||
.put(false)
|
.get(b)
|
||||||
|
.put(false, ack => {
|
||||||
|
if (ack.err) {
|
||||||
|
rej(new Error(ack.err))
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
instance
|
instance
|
||||||
.get(a)
|
.get(a)
|
||||||
.get(b)
|
.get(b)
|
||||||
.once(val => {
|
.once(
|
||||||
expect(val).toBe(false)
|
val => {
|
||||||
instance.kill()
|
expect(val).toBe(false)
|
||||||
done()
|
release()
|
||||||
})
|
done()
|
||||||
|
},
|
||||||
|
{ wait: 1000 }
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('puts numbers and reads them with once()', done => {
|
it('puts numbers and reads them with once()', async done => {
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
|
await whenReady()
|
||||||
const a = words()
|
const a = words()
|
||||||
const b = words()
|
const b = words()
|
||||||
const instance = createInstance()
|
|
||||||
instance
|
instance
|
||||||
.get(a)
|
.get(a)
|
||||||
.get(b)
|
.get(b)
|
||||||
|
|
@ -80,17 +123,17 @@ describe('gun smith', () => {
|
||||||
.get(b)
|
.get(b)
|
||||||
.once(val => {
|
.once(val => {
|
||||||
expect(val).toBe(5)
|
expect(val).toBe(5)
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('puts strings and reads them with once()', done => {
|
it('puts strings and reads them with once()', async done => {
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
|
await whenReady()
|
||||||
const a = words()
|
const a = words()
|
||||||
const b = words()
|
const b = words()
|
||||||
const sentence = words({ exactly: 50 }).join(' ')
|
const sentence = words({ exactly: 50 }).join(' ')
|
||||||
const instance = createInstance()
|
|
||||||
|
|
||||||
instance
|
instance
|
||||||
.get(a)
|
.get(a)
|
||||||
|
|
@ -102,13 +145,14 @@ describe('gun smith', () => {
|
||||||
.get(b)
|
.get(b)
|
||||||
.once(val => {
|
.once(val => {
|
||||||
expect(val).toBe(sentence)
|
expect(val).toBe(sentence)
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('merges puts', done => {
|
it('merges puts', async done => {
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
|
await whenReady()
|
||||||
const a = {
|
const a = {
|
||||||
a: 1
|
a: 1
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +160,6 @@ describe('gun smith', () => {
|
||||||
b: 1
|
b: 1
|
||||||
}
|
}
|
||||||
const c = { ...a, ...b }
|
const c = { ...a, ...b }
|
||||||
const instance = createInstance()
|
|
||||||
|
|
||||||
const node = instance.get('foo').get('bar')
|
const node = instance.get('foo').get('bar')
|
||||||
|
|
||||||
|
|
@ -129,21 +172,21 @@ describe('gun smith', () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expect(removeBuiltInGunProps(data)).toEqual(c)
|
expect(removeBuiltInGunProps(data)).toEqual(c)
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes primitive items into sets', done => {
|
it('writes primitive items into sets', async done => {
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
const item = node.set('hello')
|
const item = node.set('hello')
|
||||||
|
|
||||||
node.once(data => {
|
node.once(data => {
|
||||||
if (typeof data !== 'object' || data === null) {
|
if (typeof data !== 'object' || data === null) {
|
||||||
instance.kill()
|
release()
|
||||||
done(new Error('Data not an object'))
|
done(new Error('Data not an object'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -151,14 +194,14 @@ describe('gun smith', () => {
|
||||||
expect(removeBuiltInGunProps(data)).toEqual({
|
expect(removeBuiltInGunProps(data)).toEqual({
|
||||||
[item._.get]: 'hello'
|
[item._.get]: 'hello'
|
||||||
})
|
})
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('writes object items into sets', done => {
|
it('writes object items into sets', async done => {
|
||||||
expect.hasAssertions()
|
expect.hasAssertions()
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
const obj = {
|
const obj = {
|
||||||
|
|
@ -175,14 +218,14 @@ describe('gun smith', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(removeBuiltInGunProps(data)).toEqual(obj)
|
expect(removeBuiltInGunProps(data)).toEqual(obj)
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('maps over a primitive set', done => {
|
it('maps over a primitive set', async done => {
|
||||||
expect.assertions(100)
|
expect.assertions(100)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
|
|
@ -197,16 +240,15 @@ describe('gun smith', () => {
|
||||||
expect(ids).toContain(id)
|
expect(ids).toContain(id)
|
||||||
checked++
|
checked++
|
||||||
if (checked === 50) {
|
if (checked === 50) {
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('maps over an object set', done => {
|
it('maps over an object set', async done => {
|
||||||
expect.assertions(100)
|
expect.assertions(100)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
const items = words({ exactly: 50 }).map(w => ({
|
const items = words({ exactly: 50 }).map(w => ({
|
||||||
|
|
@ -222,16 +264,15 @@ describe('gun smith', () => {
|
||||||
expect(ids).toContain(id)
|
expect(ids).toContain(id)
|
||||||
checked++
|
checked++
|
||||||
if (checked === 50) {
|
if (checked === 50) {
|
||||||
instance.kill()
|
release()
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('offs `on()`s', done => {
|
it('offs `on()`s', async done => {
|
||||||
expect.assertions(1)
|
expect.assertions(1)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
const fn = jest.fn()
|
const fn = jest.fn()
|
||||||
|
|
@ -245,16 +286,15 @@ describe('gun smith', () => {
|
||||||
done(new Error(ack.err))
|
done(new Error(ack.err))
|
||||||
} else {
|
} else {
|
||||||
expect(fn).not.toHaveBeenCalled()
|
expect(fn).not.toHaveBeenCalled()
|
||||||
instance.kill()
|
|
||||||
done()
|
done()
|
||||||
|
release()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('offs `map().on()`s', done => {
|
it('offs `map().on()`s', async done => {
|
||||||
expect.assertions(1)
|
expect.assertions(1)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const node = instance.get(words()).get(words())
|
const node = instance.get(words()).get(words())
|
||||||
|
|
||||||
const fn = jest.fn()
|
const fn = jest.fn()
|
||||||
|
|
@ -270,17 +310,17 @@ describe('gun smith', () => {
|
||||||
done(new Error(ack.err))
|
done(new Error(ack.err))
|
||||||
} else {
|
} else {
|
||||||
expect(fn).not.toHaveBeenCalled()
|
expect(fn).not.toHaveBeenCalled()
|
||||||
instance.kill()
|
|
||||||
done()
|
done()
|
||||||
|
release()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line jest/no-commented-out-tests
|
// eslint-disable-next-line jest/no-commented-out-tests
|
||||||
// it('on()s and handles object>primitive>object transitions', done => {
|
// it('on()s and handles object>primitive>object transitions', async done => {
|
||||||
// expect.assertions(3)
|
// expect.assertions(3)
|
||||||
// const instance = createInstance()
|
// await whenReady()
|
||||||
//
|
|
||||||
// const a = {
|
// const a = {
|
||||||
// one: 1
|
// one: 1
|
||||||
// }
|
// }
|
||||||
|
|
@ -311,7 +351,7 @@ describe('gun smith', () => {
|
||||||
|
|
||||||
it('provides an user node with create(), auth() and leave()', async done => {
|
it('provides an user node with create(), auth() and leave()', async done => {
|
||||||
expect.assertions(6)
|
expect.assertions(6)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const user = instance.user()
|
const user = instance.user()
|
||||||
const alias = words()
|
const alias = words()
|
||||||
|
|
@ -333,13 +373,13 @@ describe('gun smith', () => {
|
||||||
expect(authAck.sea.pub).toEqual(pub)
|
expect(authAck.sea.pub).toEqual(pub)
|
||||||
expect(user.is?.pub).toEqual(pub)
|
expect(user.is?.pub).toEqual(pub)
|
||||||
user.leave()
|
user.leave()
|
||||||
instance.kill()
|
|
||||||
done()
|
done()
|
||||||
|
release()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('provides thenables for values', async done => {
|
it('provides thenables for values', async done => {
|
||||||
expect.assertions(1)
|
expect.assertions(1)
|
||||||
const instance = createInstance()
|
await whenReady()
|
||||||
|
|
||||||
const a = words()
|
const a = words()
|
||||||
const b = words()
|
const b = words()
|
||||||
|
|
@ -362,7 +402,7 @@ describe('gun smith', () => {
|
||||||
.get(b)
|
.get(b)
|
||||||
.then()
|
.then()
|
||||||
expect(fetch).toEqual(value)
|
expect(fetch).toEqual(value)
|
||||||
instance.kill()
|
|
||||||
done()
|
done()
|
||||||
|
release()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue