3box
Version:
Interact with user data
259 lines (224 loc) • 10.6 kB
JavaScript
jest.mock('../keyValueStore')
jest.mock('../thread')
const Thread = require('../thread')
const ORBITDB = 'orbitdb instance'
let authenticated = false
const threeIdMock = {
isAuthenticated: jest.fn(() => {
return authenticated
}),
authenticate: jest.fn(),
hashDBKey: jest.fn(key => `${key}asdfasdfasdf`),
encrypt: data => { return { ciphertext: 'wow, such encrypted/' + data, nonce: 123 } },
decrypt: ({ciphertext, nonce}) => ciphertext.split('/')[1],
signJWT: (payload, { space }) => {
return `a fake jwt for ${space}`
},
getOdbId: async () => 'odbid',
getSubDID: (space) => `subdid-${space}`
}
const replicatorMock = 'replicator'
let rootstoreMockData = []
const rootstoreMock = {
iterator: () => { return { collect: () => rootstoreMockData } },
add: jest.fn(),
del: jest.fn()
}
const Space = require('../space')
describe('Space', () => {
let space
let NAME1 = 'test1'
let NAME2 = 'test2'
beforeEach(() => {
rootstoreMock.add.mockClear()
threeIdMock.isAuthenticated.mockClear()
threeIdMock.authenticate.mockClear()
})
it('should be correctly constructed', async () => {
space = new Space(NAME1, replicatorMock)
expect(space._name).toEqual(NAME1)
expect(space._store._replicator).toEqual(replicatorMock)
expect(space._store._name).toEqual('3box.space.' + NAME1 + '.keyvalue')
expect(space.isOpen).toBeFalsy()
})
it('should open a new space correctly', async () => {
let opts = {
consentCallback: jest.fn(),
}
const syncDonePromise = new Promise((resolve, reject) => {
opts.onSyncDone = resolve
})
await space.open(threeIdMock, opts)
expect(space.isOpen).toBeTruthy()
expect(opts.consentCallback).toHaveBeenCalledWith(true, NAME1)
//expect(rootstoreMock.add).toHaveBeenCalledWith({ type: 'space', DID: threeIdMock.getSubDID(NAME1), odbAddress:'/orbitdb/myodbaddr' })
expect(threeIdMock.isAuthenticated).toHaveBeenCalledWith([NAME1])
expect(threeIdMock.authenticate).toHaveBeenCalledWith([NAME1], opts)
return syncDonePromise
})
it('should return directly if space already opened', async () => {
let opts = {
consentCallback: jest.fn(),
}
await space.open(threeIdMock, opts)
expect(opts.consentCallback).toHaveBeenCalledTimes(0)
})
describe('public store reducer', () => {
it('get/set/remove works correctly', async () => {
await space.public.set('k1', 'v1')
await space.public.set('k2', 'v2')
await space.public.set('k3', 'v3')
expect(await space.public.get('k1')).toEqual('v1')
expect(await space.public.get('k2')).toEqual('v2')
const entry = await space.public.get('k3', { metadata: true })
expect(entry.value).toEqual('v3')
expect(entry.timestamp).toBeGreaterThan(0)
await space.public.remove('k2')
expect(await space.public.get('k2')).toBeUndefined()
})
it('setMultiple works correctly', async () => {
await space.public.setMultiple(['k4', 'k5', 'k6'], ['v4', 'v5', 'v6'])
expect(await space.public.get('k4')).toEqual('v4')
expect(await space.public.get('k5')).toEqual('v5')
expect(await space.public.get('k6')).toEqual('v6')
await space.public.remove('k6')
expect(await space.public.get('k6')).toBeUndefined()
expect(space.public.setMultiple(['key'], ['value', 'value1'])).rejects.toEqual(new Error('Arrays must be of the same length'))
expect(space.public.setMultiple('key', ['value', 'value1'])).rejects.toEqual(new Error('One or more arguments are not an array'))
})
it('log should only return public values', async () => {
const log1 = await space.public.log()
expect(log1).toMatchSnapshot()
space._store.set('key', 'value')
const log2 = await space.public.log()
expect(log2).toEqual(log1)
})
it('all should return all values from public store', async () => {
expect(await space.public.all()).toMatchSnapshot()
expect(await space.public.all({ metadata: true })).toMatchSnapshot()
})
it('should throw if key not given', async () => {
expect(space.public.set()).rejects.toEqual(new Error('key is a required argument'))
expect(space.public.remove()).rejects.toEqual(new Error('key is a required argument'))
})
})
describe('private store reducer', () => {
it('get/set/remove works correctly', async () => {
await space.private.set('k1', 'sv1')
await space.private.set('k2', 'sv2')
await space.private.set('k3', 'sv3')
expect(await space.private.get('k1')).toEqual('sv1')
expect(await space.private.get('k2')).toEqual('sv2')
const entry = await space.private.get('k3', { metadata: true })
expect(entry.value).toEqual('sv3')
expect(entry.timestamp).toBeGreaterThan(0)
await space.private.remove('k2')
expect(await space.private.get('k2')).toEqual(null)
})
it('setMultiple works correctly', async () => {
await space.private.setMultiple(['k4', 'k5', 'k6'], ['sv4', 'sv5', 'sv6'])
expect(await space.private.get('k4')).toEqual('sv4')
expect(await space.private.get('k5')).toEqual('sv5')
expect(await space.private.get('k6')).toEqual('sv6')
await space.private.remove('k6')
expect(await space.private.get('k6')).toEqual(null)
expect(space.private.setMultiple(['key'], ['value', 'value1'])).rejects.toEqual(new Error('Arrays must be of the same length'))
expect(space.private.setMultiple('key', ['value', 'value1'])).rejects.toEqual(new Error('One or more arguments are not an array'))
})
it('log should only return private values', async () => {
const refLog = [{ key: 'k1', op: 'PUT', timeStamp: 123, value: 'sv1' }, { key: 'k3', op: 'PUT', timeStamp: 123, value: 'sv3' }, { key: 'k4', op: 'PUT', timeStamp: 123, value: 'sv4' }, { key: 'k5', op: 'PUT', timeStamp: 123, value: 'sv5' } ]
const log1 = await space.private.log()
expect(log1).toEqual(refLog)
space._store.set('key', 'value')
const log2 = await space.private.log()
expect(log2).toEqual(log1)
})
it('all should return all values from private store', async () => {
const expected = {
k1: 'sv1',
k3: 'sv3',
k4: 'sv4',
k5: 'sv5'
}
expect(await space.private.all()).toEqual(expected)
const result = await space.private.all({ metadata: true })
Object.entries(expected).map(([k,v]) => {
expect(result[k].value).toEqual(v)
expect(result[k].timestamp).toBeGreaterThan(0)
})
})
it('should throw if key not given', async () => {
expect(space.private.set()).rejects.toEqual(new Error('key is a required argument'))
expect(space.private.remove()).rejects.toEqual(new Error('key is a required argument'))
})
})
describe('Threads', () => {
beforeEach(() => {
Thread.mockClear()
})
const threadAddress = '/orbitdb/zdpuAmUTuZVp5QNw75E9KVaQwfPSM611FCPR2RmMGF6jxWzxW/3box.thread.a.z'
const threadAddress2 = '/orbitdb/zdpuAu3nxzphjPVgP3sw4HufdG3uvZTerUgS6rQQtk29UAhbd/3box.thread.a.a'
it('does not subscribe or return invalid thread address (ignore experimental)', async () => {
await expect(space.subscribeThread('t1')).rejects.toThrowErrorMatchingSnapshot()
// experimental threads
await space.public.set('thread-t1')
expect(await space.subscribedThreads()).toEqual([])
})
it('subscribes to thread correctly', async () => {
await space.subscribeThread(threadAddress)
expect(await space.public.get(`thread-${threadAddress}`)).toEqual({ address: threadAddress })
expect(await space.subscribedThreads()).toEqual([{address: threadAddress}])
})
it('unsubscribes from thread correctly', async () => {
await space.unsubscribeThread(threadAddress)
expect(await space.public.get(`thread-${threadAddress}`)).toEqual()
expect(await space.subscribedThreads()).toEqual([])
})
it('joins thread correctly', async () => {
const t1 = await space.joinThread('t2')
expect(Thread).toHaveBeenCalledTimes(1)
expect(Thread.mock.calls[0][0]).toEqual(`3box.thread.${NAME1}.t2`)
expect(Thread.mock.calls[0][1]).toEqual(replicatorMock)
expect(t1._load).toHaveBeenCalledTimes(1)
expect(t1._setIdentity).toHaveBeenCalledTimes(1)
expect(t1._setIdentity).toHaveBeenCalledWith('odbid')
// function for autosubscribing works as intended
await Thread.mock.calls[0][6](threadAddress)
expect(await space.subscribedThreads()).toEqual([{address: threadAddress}])
})
it('a thread loaded by address, must be in same space as threadname, otherwise throws', async () => {
const threadAddress = "/orbitdb/zdpuAz8c2gjonfuhYCfPJqZJUfYM5Kd7bpHaMyJZSLDMHSNvQ/3box.thread.errorspace.test"
await expect(space.joinThreadByAddress(threadAddress)).rejects.toThrow(/must open within same space/)
})
it('joins thread correctly, no auto subscription', async () => {
const t1 = await space.joinThread('t3', { noAutoSub: true })
expect(Thread).toHaveBeenCalledTimes(1)
expect(Thread.mock.calls[0][0]).toEqual(`3box.thread.${NAME1}.t3`)
expect(Thread.mock.calls[0][1]).toEqual(replicatorMock)
expect(t1._load).toHaveBeenCalledTimes(1)
expect(t1._setIdentity).toHaveBeenCalledTimes(1)
expect(t1._setIdentity).toHaveBeenCalledWith('odbid')
// function for autosubscribing works as intended
await Thread.mock.calls[0][6](threadAddress2)
expect(await space.subscribedThreads()).toEqual([{address: threadAddress}])
})
it('should trow if space not open and no firstModerator', async () => {
const sp = new Space(NAME2, replicatorMock)
const t1 = await
expect(sp.joinThread('t1')).rejects.toMatchSnapshot()
})
it('joins thread correctly with space that is not open', async () => {
const sp = new Space(NAME2, replicatorMock)
const firstModerator = 'did:3:bafyasdf'
const t1 = await sp.joinThread('t1', { firstModerator })
expect(Thread).toHaveBeenCalledTimes(1)
expect(Thread.mock.calls[0][0]).toEqual(`3box.thread.${NAME2}.t1`)
expect(Thread.mock.calls[0][1]).toEqual(replicatorMock)
expect(Thread.mock.calls[0][3]).toEqual(firstModerator)
expect(t1._load).toHaveBeenCalledTimes(1)
expect(t1._setIdentity).toHaveBeenCalledTimes(0)
// function for autosubscribing works as intended
return Thread.mock.calls[0][6](threadAddress)
})
})
})