Foundations for switching to mocha for testing

This commit is contained in:
Daniel Lugo 2021-09-23 17:50:44 -04:00
parent 9d342a460c
commit 7bde98e6a3
5 changed files with 768 additions and 2490 deletions

View file

@ -73,7 +73,6 @@
"@types/eccrypto": "^1.1.2", "@types/eccrypto": "^1.1.2",
"@types/express": "^4.17.1", "@types/express": "^4.17.1",
"@types/gun": "^0.9.2", "@types/gun": "^0.9.2",
"@types/jest": "^24.0.18",
"@types/jsonwebtoken": "^8.3.7", "@types/jsonwebtoken": "^8.3.7",
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node-fetch": "^2.5.8", "@types/node-fetch": "^2.5.8",
@ -86,10 +85,10 @@
"eslint": "^6.6.0", "eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0", "eslint-config-prettier": "^6.5.0",
"eslint-plugin-babel": "^5.3.1", "eslint-plugin-babel": "^5.3.1",
"eslint-plugin-jest": "^22.20.1",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"jest": "^24.9.0", "expect": "^27.2.1",
"lint-staged": "^10.2.2", "lint-staged": "^10.2.2",
"mocha": "^9.1.1",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"prettier": "^1.18.2", "prettier": "^1.18.2",
"random-words": "^1.1.1", "random-words": "^1.1.1",

View file

@ -628,6 +628,17 @@ function createReplica(path, afterMap = false) {
} }
}) })
}) })
},
pSet(data) {
return new Promise((res, rej) => {
this.set(data, ack => {
if (ack.err) {
rej(new Error(ack.err))
} else {
res()
}
})
})
} }
} }
} }

View file

@ -8,6 +8,7 @@ const Gun = require('./GunSmith')
const words = require('random-words') const words = require('random-words')
const fs = require('fs') const fs = require('fs')
const debounce = require('lodash/debounce') const debounce = require('lodash/debounce')
const expect = require('expect')
const logger = require('../../config/log') const logger = require('../../config/log')
@ -27,6 +28,10 @@ const instance = Gun({
file: './test-radata/' + words({ exactly: 2 }).join('-') file: './test-radata/' + words({ exactly: 2 }).join('-')
}) })
const user = instance.user()
const alias = words()
const pass = words()
const release = () => { const release = () => {
isBusy = false isBusy = false
} }
@ -46,9 +51,14 @@ const whenReady = () =>
}, 1000) }, 1000)
}) })
/**
* @param {number} ms
*/
const delay = ms => new Promise(res => setTimeout(res, ms))
describe('gun smith', () => { describe('gun smith', () => {
// eslint-disable-next-line jest/no-hooks // eslint-disable-next-line jest/no-hooks
afterAll(() => { after(() => {
Gun.kill() Gun.kill()
}) })
@ -67,56 +77,46 @@ describe('gun smith', () => {
instance instance
.get(a) .get(a)
.get(b) .get(b)
.once( .once(val => {
val => {
expect(val).toBe(true) expect(val).toBe(true)
done() done()
release() release()
}, })
{ wait: 1000 }
)
}) })
it('puts a false and reads it with once()', async done => { it('puts a false and reads it with once()', done => {
expect.assertions(1) expect.assertions(1)
await whenReady()
logger.info('puts a false and reads it with once()')
const a = words() const a = words()
const b = words() const b = words()
await new Promise((res, rej) => { whenReady().then(() => {
instance instance
.get(a) .get(a)
.get(b) .get(b)
.put(false, ack => { .put(false, ack => {
if (ack.err) { if (ack.err) {
rej(new Error(ack.err)) throw new Error(ack.err)
} else { } else {
// @ts-ignore
res()
}
})
})
instance instance
.get(a) .get(a)
.get(b) .get(b)
.once( .once(val => {
val => {
expect(val).toBe(false) expect(val).toBe(false)
done() done()
release() release()
}, })
{ wait: 1000 } }
) })
})
}) })
it('puts numbers and reads them with once()', async done => { it('puts numbers and reads them with once()', done => {
expect.hasAssertions() expect.hasAssertions()
await whenReady()
const a = words() const a = words()
const b = words() const b = words()
whenReady().then(() => {
instance instance
.get(a) .get(a)
.get(b) .get(b)
@ -131,14 +131,14 @@ describe('gun smith', () => {
release() release()
}) })
}) })
})
it('puts strings and reads them with once()', async done => { it('puts strings and reads them with once()', 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(' ')
whenReady().then(() => {
instance instance
.get(a) .get(a)
.get(b) .get(b)
@ -153,8 +153,9 @@ describe('gun smith', () => {
release() release()
}) })
}) })
})
it('merges puts', async done => { it('merges puts', async () => {
expect.hasAssertions() expect.hasAssertions()
await whenReady() await whenReady()
const a = { const a = {
@ -170,32 +171,23 @@ describe('gun smith', () => {
node.put(a) node.put(a)
node.put(b) node.put(b)
node.once(data => { const data = await node.then()
if (typeof data !== 'object' || data === null) { if (typeof data !== 'object' || data === null) {
done(new Error('Data not an object')) throw new Error('Data not an object')
release()
return
} }
expect(removeBuiltInGunProps(data)).toEqual(c) expect(removeBuiltInGunProps(data)).toEqual(c)
done()
release() release()
}) })
})
it('writes primitive items into sets', async done => { it('writes primitive items into sets and correctly assigns the id to ._.get', done => {
expect.hasAssertions() expect.hasAssertions()
await whenReady() whenReady().then(() => {
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) {
done(new Error('Data not an object'))
release()
return
}
expect(removeBuiltInGunProps(data)).toEqual({ expect(removeBuiltInGunProps(data)).toEqual({
[item._.get]: 'hello' [item._.get]: 'hello'
}) })
@ -203,108 +195,13 @@ describe('gun smith', () => {
release() release()
}) })
}) })
it('writes object items into sets', async done => {
expect.hasAssertions()
await whenReady()
const node = instance.get(words()).get(words())
const obj = {
a: 1,
b: 'hello'
}
const item = node.set(obj)
node.get(item._.get).once(data => {
if (typeof data !== 'object' || data === null) {
done(new Error('Data not an object'))
release()
return
}
expect(removeBuiltInGunProps(data)).toEqual(obj)
done()
release()
})
})
it('provides an special once() that restarts gun until a value is fetched', async done => {
expect.assertions(1)
jest.setTimeout(100000)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
node.specialOnce(data => {
expect(data).toEqual(value)
done()
release()
})
setTimeout(() => {
node.put(value)
}, 30000)
})
it('provides an special then() that restarts gun until a value is fetched', async done => {
expect.assertions(1)
jest.setTimeout(100000)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
setTimeout(() => {
node.put(value)
}, 30000)
const res = await node.specialThen()
expect(res).toBe(value)
done()
release()
})
it('provides an special on() that restarts gun when a value has not been obtained in a determinate amount of time', async done => {
// Kinda crappy test, this should be easier to test in real usage.
expect.assertions(1)
await whenReady()
jest.setTimeout(40000)
const initialProcCounter = Gun._getProcCounter()
const node = instance.get(words()).get(words())
const secondValue = words()
node.specialOn(
debounce(data => {
if (data === secondValue) {
expect(Gun._getProcCounter()).toEqual(initialProcCounter + 1)
done()
release()
}
})
)
setTimeout(() => {
node.put(secondValue)
}, 32000)
}) })
// TODO: find out why this test fucks up the previous one if it runs before // TODO: find out why this test fucks up the previous one if it runs before
// that one // that one
it('maps over a primitive set', async done => { it('maps over a primitive set', done => {
expect.assertions(100) expect.assertions(100)
await whenReady() whenReady().then(() => {
const node = instance.get(words()).get(words()) const node = instance.get(words()).get(words())
const items = words({ exactly: 50 }) const items = words({ exactly: 50 })
@ -323,10 +220,11 @@ describe('gun smith', () => {
} }
}) })
}) })
})
it('maps over an object set', async done => { it('maps over an object set', done => {
expect.assertions(100) expect.assertions(100)
await whenReady() whenReady().then(() => {
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 => ({
@ -347,57 +245,235 @@ describe('gun smith', () => {
} }
}) })
}) })
})
it('offs `on()`s', async done => { it('offs `on()`s', async () => {
expect.assertions(1) expect.assertions(1)
await whenReady() await whenReady()
const node = instance.get(words()).get(words()) const node = instance.get(words()).get(words())
const fn = jest.fn() let called = false
node.on(fn) node.on(() => {
called = true
})
node.off() node.off()
node.put('return', ack => { await node.pPut('return')
if (ack.err) { await delay(500)
done(new Error(ack.err)) expect(called).toBe(false)
release() release()
} else {
expect(fn).not.toHaveBeenCalled()
done()
release()
}
})
}) })
it('offs `map().on()`s', async done => { it('offs `map().on()`s', async () => {
expect.assertions(1) expect.assertions(1)
await whenReady() await whenReady()
const node = instance.get(words()).get(words()) const node = instance.get(words()).get(words())
const fn = jest.fn() let called = false
const iterateeNode = node.map() const iterateeNode = node.map()
iterateeNode.on(fn) iterateeNode.on(() => {
called = true
})
iterateeNode.off() iterateeNode.off()
node.set('return', ack => { await node.pSet('return')
if (ack.err) {
done(new Error(ack.err)) await delay(500)
expect(called).toBe(false)
})
it('provides an user node with create(), auth() and leave()', async () => {
expect.assertions(7)
await whenReady()
const ack = await new Promise(res => user.create(alias, pass, res))
expect(ack.err).toBeUndefined()
const { pub } = ack
// eslint-disable-next-line jest/no-truthy-falsy
expect(pub).toBeTruthy()
expect(user.is?.pub).toEqual(pub)
user.leave()
expect(user.is).toBeUndefined()
/** @type {GunT.AuthAck} */
const authAck = await new Promise(res =>
user.auth(alias, pass, ack => res(ack))
)
expect(authAck.err).toBeUndefined()
expect(authAck.sea?.pub).toEqual(pub)
expect(user.is?.pub).toEqual(pub)
user.leave()
release() release()
})
it('reliably provides authentication information across re-forges', async () => {
expect.assertions(3)
await whenReady()
/** @type {GunT.AuthAck} */
const authAck = await new Promise(res =>
user.auth(alias, pass, ack => res(ack))
)
const pub = authAck.sea?.pub
// eslint-disable-next-line jest/no-truthy-falsy
expect(pub).toBeTruthy()
Gun._reforge()
expect(user.is?.pub).toEqual(pub)
await Gun._isReady()
expect(user.is?.pub).toEqual(pub)
user.leave()
release()
})
it('provides thenables for values', async () => {
expect.assertions(1)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
await new Promise((res, rej) => {
node.put(value, ack => {
if (ack.err) {
rej(new Error(ack.err))
} else { } else {
expect(fn).not.toHaveBeenCalled() // @ts-ignore
res()
}
})
})
const fetch = await instance
.get(a)
.get(b)
.then()
expect(fetch).toEqual(value)
release()
})
it('provides an special thenable put()', async () => {
expect.assertions(1)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
await node.pPut(value)
const res = await node.then()
expect(res).toBe(value)
release()
})
// long tests below
it('writes object items into sets and correctly populates item._.get with the newly created id', done => {
expect.hasAssertions()
whenReady().then(() => {
const node = instance.get(words()).get(words())
const obj = {
a: 1,
b: 'hello'
}
const item = node.set(obj)
node.get(item._.get).once(data => {
expect(removeBuiltInGunProps(data)).toEqual(obj)
done()
release()
})
})
})
it('provides an special once() that restarts gun until a value is fetched', done => {
expect.assertions(1)
whenReady().then(() => {
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
node.specialOnce(data => {
expect(data).toEqual(value)
done()
release()
})
setTimeout(() => {
node.put(value)
}, 30000)
})
})
it('provides an special then() that restarts gun until a value is fetched', async () => {
expect.assertions(1)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
setTimeout(() => {
node.put(value)
}, 30000)
const res = await node.specialThen()
expect(res).toBe(value)
release()
})
it('provides an special on() that restarts gun when a value has not been obtained in a determinate amount of time', done => {
// Kinda crappy test, this should be easier to test in real usage.
expect.assertions(1)
whenReady().then(() => {
const initialProcCounter = Gun._getProcCounter()
const node = instance.get(words()).get(words())
const secondValue = words()
node.specialOn(
debounce(data => {
if (data === secondValue) {
expect(Gun._getProcCounter()).toEqual(initialProcCounter + 1)
done() done()
release() release()
} }
}) })
)
setTimeout(() => {
node.put(secondValue)
}, 32000)
})
}) })
// 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', async done => { // it('on()s and handles object>primitive>object transitions', async () => {
// expect.assertions(3) // expect.assertions(3)
// await whenReady() // await whenReady()
@ -428,108 +504,4 @@ describe('gun smith', () => {
// node.put(c) // node.put(c)
// }, 800) // }, 800)
// }) // })
describe('authentication', () => {
const user = instance.user()
const alias = words()
const pass = words()
jest.setTimeout(20000)
it('provides an user node with create(), auth() and leave()', async done => {
expect.assertions(7)
await whenReady()
const ack = await new Promise(res => user.create(alias, pass, res))
expect(ack.err).toBeUndefined()
const { pub } = ack
// eslint-disable-next-line jest/no-truthy-falsy
expect(pub).toBeTruthy()
expect(user.is?.pub).toEqual(pub)
user.leave()
expect(user.is).toBeUndefined()
/** @type {GunT.AuthAck} */
const authAck = await new Promise(res =>
user.auth(alias, pass, ack => res(ack))
)
expect(authAck.err).toBeUndefined()
expect(authAck.sea?.pub).toEqual(pub)
expect(user.is?.pub).toEqual(pub)
user.leave()
done()
release()
})
it('reliably provides authentication information across re-forges', async done => {
expect.assertions(3)
await whenReady()
/** @type {GunT.AuthAck} */
const authAck = await new Promise(res =>
user.auth(alias, pass, ack => res(ack))
)
const pub = authAck.sea?.pub
// eslint-disable-next-line jest/no-truthy-falsy
expect(pub).toBeTruthy()
Gun._reforge()
expect(user.is?.pub).toEqual(pub)
await Gun._isReady()
expect(user.is?.pub).toEqual(pub)
user.leave()
done()
release()
})
})
it('provides thenables for values', async done => {
expect.assertions(1)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
await new Promise((res, rej) => {
node.put(value, ack => {
if (ack.err) {
rej(new Error(ack.err))
} else {
// @ts-ignore
res()
}
})
})
const fetch = await instance
.get(a)
.get(b)
.then()
expect(fetch).toEqual(value)
done()
release()
})
it('provides an special thenable put()', async done => {
expect.assertions(1)
await whenReady()
const a = words()
const b = words()
const node = instance.get(a).get(b)
const value = words()
await node.pPut(value)
const res = await node.then()
expect(res).toBe(value)
done()
release()
})
}) })

View file

@ -39,6 +39,11 @@ namespace Smith {
* @throws * @throws
*/ */
pPut(data: GunT.ValidDataValue): Promise<void> pPut(data: GunT.ValidDataValue): Promise<void>
/**
* A promise version of set().
* @throws
*/
pSet(data: GunT.ValidDataValue): Promise<void>
} }
export type UserSmithNode = GunSmithNode & GunT.UserGUNNode export type UserSmithNode = GunSmithNode & GunT.UserGUNNode

2581
yarn.lock

File diff suppressed because it is too large Load diff