orbitdb/test/sync.test.js
Haad bc816c7e2e
Browser tests (#41)
* Get browsers tests running

* Clean up replication test

* Setup fixtures for browser tests

* Fix import paths for webpack

* Fix webpack

* Add mocha-headless-chrome to run browser tests

* Add webrtc swarm endpoints for browser test IPFS node configs

* Remove adding pubkey to storage in KeyStore

* Runs browser tests in CI

* Fix import paths again

* Fix failing browser tests

* Fixes
2023-03-11 18:56:23 +08:00

478 lines
12 KiB
JavaScript

import { deepStrictEqual, strictEqual, notStrictEqual } from 'assert'
import rmrf from 'rimraf'
import { copy } from 'fs-extra'
import * as IPFS from 'ipfs'
import Sync from '../src/sync.js'
import { Log, Entry, Identities, KeyStore } from '../src/index.js'
import config from './config.js'
import connectPeers from './utils/connect-nodes.js'
import waitFor from './utils/wait-for.js'
import testKeysPath from './fixtures/test-keys-path.js'
const keysPath = './testkeys'
describe('Sync protocol', function () {
this.timeout(10000)
let ipfs1, ipfs2
let keystore
let identities
let testIdentity1, testIdentity2
let peerId1, peerId2
before(async () => {
await rmrf('./ipfs1')
await rmrf('./ipfs2')
ipfs1 = await IPFS.create({ ...config.daemon1, repo: './ipfs1' })
ipfs2 = await IPFS.create({ ...config.daemon2, repo: './ipfs2' })
peerId1 = (await ipfs1.id()).id
peerId2 = (await ipfs2.id()).id
await connectPeers(ipfs1, ipfs2)
await copy(testKeysPath, keysPath)
keystore = await KeyStore({ path: keysPath })
identities = await Identities({ keystore })
testIdentity1 = await identities.createIdentity({ id: 'userA' })
testIdentity2 = await identities.createIdentity({ id: 'userB' })
})
after(async () => {
await ipfs1.stop()
await ipfs2.stop()
await rmrf('./ipfs1')
await rmrf('./ipfs2')
if (keystore) {
await keystore.close()
}
await rmrf(keysPath)
})
describe('Creating an instance', () => {
let sync
before(async () => {
const log = await Log(testIdentity1)
sync = await Sync({ ipfs: ipfs1, log })
})
after(async () => {
if (sync) {
await sync.stop()
}
})
it('creates an instance', async () => {
notStrictEqual(sync, undefined)
})
it('has an ad function', async () => {
notStrictEqual(sync.add, undefined)
strictEqual(typeof sync.add, 'function')
})
it('has a start function', async () => {
notStrictEqual(sync.start, undefined)
strictEqual(typeof sync.stop, 'function')
})
it('has a stop function', async () => {
notStrictEqual(sync.stop, undefined)
strictEqual(typeof sync.stop, 'function')
})
it('has events', async () => {
notStrictEqual(sync.events, undefined)
})
it('has a set of peers', async () => {
notStrictEqual(sync.peers, undefined)
strictEqual(sync.peers instanceof Set, true)
})
})
describe('Syncing automatically', () => {
let sync1, sync2
let joinEventFired = false
let syncedEventFired = false
let syncedHead
let expectedEntry
before(async () => {
const log1 = await Log(testIdentity1, { logId: 'synclog1' })
const log2 = await Log(testIdentity2, { logId: 'synclog1' })
const onSynced = async (bytes) => {
syncedHead = await Entry.decode(bytes)
syncedEventFired = true
}
const onJoin = async (peerId, heads) => {
joinEventFired = true
}
expectedEntry = await log1.append('hello1')
sync1 = await Sync({ ipfs: ipfs1, log: log1, onSynced: () => {} })
sync2 = await Sync({ ipfs: ipfs2, log: log2, onSynced })
sync1.events.on('join', onJoin)
await waitFor(() => joinEventFired, () => true)
await waitFor(() => syncedEventFired, () => true)
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('syncs the head', async () => {
deepStrictEqual(syncedHead, expectedEntry)
})
it('updates the set of connected peers', async () => {
strictEqual(sync2.peers.has(String(peerId1)), true)
strictEqual(sync1.peers.has(String(peerId2)), true)
})
})
describe('Starting sync manually', () => {
let sync1, sync2
let syncedEventFired = false
let syncedHead
let expectedEntry
before(async () => {
const log1 = await Log(testIdentity1, { logId: 'synclog1' })
const log2 = await Log(testIdentity2, { logId: 'synclog1' })
const onSynced = async (bytes) => {
syncedHead = await Entry.decode(bytes)
syncedEventFired = true
}
sync1 = await Sync({ ipfs: ipfs1, log: log1 })
sync2 = await Sync({ ipfs: ipfs2, log: log2, onSynced, start: false })
await log1.append('hello1')
await log1.append('hello2')
await log1.append('hello3')
await log1.append('hello4')
expectedEntry = await log1.append('hello5')
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('starts syncing', async () => {
await sync2.start()
await waitFor(() => syncedEventFired, () => true)
strictEqual(syncedEventFired, true)
})
it('syncs the correct head', async () => {
deepStrictEqual(syncedHead, expectedEntry)
})
it('updates the set of connected peers', async () => {
strictEqual(sync2.peers.has(String(peerId1)), true)
strictEqual(sync1.peers.has(String(peerId2)), true)
})
})
describe.skip('Stopping sync', () => {
let sync1, sync2
let log1, log2
let syncedEventFired = false
let leaveEventFired = false
let syncedHead
let expectedEntry
let leavingPeerId
before(async () => {
log1 = await Log(testIdentity1, { logId: 'synclog1' })
log2 = await Log(testIdentity2, { logId: 'synclog1' })
const onSynced = async (bytes) => {
syncedHead = await Entry.decode(bytes)
syncedEventFired = true
}
const onLeave = async (peerId) => {
leaveEventFired = true
leavingPeerId = peerId
}
sync1 = await Sync({ ipfs: ipfs1, log: log1 })
sync2 = await Sync({ ipfs: ipfs2, log: log2, onSynced })
sync2.events.on('leave', onLeave)
await log1.append('hello1')
await log1.append('hello2')
await log1.append('hello3')
await log1.append('hello4')
expectedEntry = await log1.append('hello5')
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('starts syncing', async () => {
await waitFor(() => syncedEventFired, () => true)
strictEqual(syncedEventFired, true)
deepStrictEqual(syncedHead, expectedEntry)
strictEqual(sync1.peers.has(String(peerId2)), true)
strictEqual(sync2.peers.has(String(peerId1)), true)
})
it('stops syncing', async () => {
await sync1.stop()
await log1.append('hello6')
await log1.append('hello7')
await log1.append('hello8')
await log1.append('hello9')
await log1.append('hello10')
await waitFor(() => leaveEventFired, () => true)
deepStrictEqual(syncedHead, expectedEntry)
})
it('the peerId passed by the \'leave\' event is the expected peer ID', async () => {
strictEqual(String(leavingPeerId), String(peerId1))
})
it('updates the set of connected peers', async () => {
strictEqual(sync2.peers.has(String(leavingPeerId)), false)
strictEqual(sync1.peers.has(String(peerId2)), false)
})
})
describe.skip('Restarting sync after stopping it manually', () => {
let sync1, sync2
let log1, log2
let syncedEventFired = false
let leaveEventFired = false
let syncedHead
let expectedEntry, expectedEntry2
before(async () => {
log1 = await Log(testIdentity1, { logId: 'synclog1' })
log2 = await Log(testIdentity2, { logId: 'synclog1' })
const onSynced = async (bytes) => {
syncedHead = await Entry.decode(bytes)
syncedEventFired = true
}
const onLeave = async (peerId) => {
leaveEventFired = true
}
sync1 = await Sync({ ipfs: ipfs1, log: log1 })
sync2 = await Sync({ ipfs: ipfs2, log: log2, onSynced })
sync2.events.on('leave', onLeave)
await log1.append('hello1')
await log1.append('hello2')
await log1.append('hello3')
await log1.append('hello4')
expectedEntry = await log1.append('hello5')
await waitFor(() => syncedEventFired, () => true)
strictEqual(syncedEventFired, true)
deepStrictEqual(syncedHead, expectedEntry)
await sync1.stop()
await waitFor(() => leaveEventFired, () => true)
strictEqual(leaveEventFired, true)
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('restarts syncing', async () => {
await log1.append('hello6')
await log1.append('hello7')
await log1.append('hello8')
await log1.append('hello9')
expectedEntry2 = await log1.append('hello10')
syncedEventFired = false
await sync1.start()
await waitFor(() => syncedEventFired, () => true)
strictEqual(syncedEventFired, true)
deepStrictEqual(syncedHead, expectedEntry2)
})
it('updates the set of connected peers', async () => {
strictEqual(sync1.peers.has(String(peerId2)), true)
strictEqual(sync2.peers.has(String(peerId1)), true)
})
})
describe.skip('Syncing after initial sync', () => {
let sync1, sync2
let log1, log2
let syncedEventFired = false
let syncedHead
let expectedEntry
before(async () => {
log1 = await Log(testIdentity1, { logId: 'synclog2' })
log2 = await Log(testIdentity2, { logId: 'synclog2' })
const onSynced = async (bytes) => {
syncedHead = await Entry.decode(bytes)
syncedEventFired = true
}
sync1 = await Sync({ ipfs: ipfs1, log: log1 })
sync2 = await Sync({ ipfs: ipfs2, log: log2, onSynced })
await log1.append('hello1')
await log1.append('hello2')
await log1.append('hello3')
await log1.append('hello4')
expectedEntry = await log1.append('hello5')
await waitFor(() => syncedEventFired, () => true)
strictEqual(syncedEventFired, true)
deepStrictEqual(syncedHead, expectedEntry)
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('doesn\'t sync when an entry is added to a log', async () => {
await log1.append('hello6')
deepStrictEqual(syncedHead, expectedEntry)
})
it('syncs new entries', async () => {
syncedEventFired = false
await log1.append('hello7')
await log1.append('hello8')
await log1.append('hello9')
const expectedEntry2 = await log1.append('hello10')
await sync1.add(expectedEntry2)
await waitFor(() => syncedEventFired, () => true)
deepStrictEqual(syncedHead, expectedEntry2)
})
})
describe('Events', () => {
let sync1, sync2
let joinEventFired = false
let leaveEventFired = false
let receivedHeads = []
let joiningPeerId
let leavingPeerId
before(async () => {
const log1 = await Log(testIdentity1, { logId: 'synclog2' })
const log2 = await Log(testIdentity2, { logId: 'synclog2' })
const onJoin = async (peerId, heads) => {
joinEventFired = true
joiningPeerId = peerId
receivedHeads = heads
}
const onLeave = async (peerId) => {
leaveEventFired = true
leavingPeerId = peerId
}
await log1.append('hello!')
sync1 = await Sync({ ipfs: ipfs1, log: log1 })
sync2 = await Sync({ ipfs: ipfs2, log: log2 })
sync1.events.on('join', onJoin)
sync1.events.on('leave', onLeave)
await waitFor(() => joinEventFired, () => true)
await sync2.stop()
await waitFor(() => leaveEventFired, () => true)
})
after(async () => {
if (sync1) {
await sync1.stop()
}
if (sync2) {
await sync2.stop()
}
})
it('emits \'join\' event when a peer starts syncing', async () => {
strictEqual(joinEventFired, true)
})
it('heads passed by the \'join\' event are the expected heads', async () => {
strictEqual(receivedHeads.length, 1)
strictEqual(receivedHeads[0].payload, 'hello!')
})
it('the peerId passed by the \'join\' event is the expected peer ID', async () => {
const { id } = await ipfs2.id()
strictEqual(String(joiningPeerId), String(id))
})
it('the peerId passed by the \'leave\' event is the expected peer ID', async () => {
const { id } = await ipfs2.id()
strictEqual(String(leavingPeerId), String(id))
})
it.skip('emits an \'error\' event', async () => {
// TODO
})
})
})