ipfs-interop
Version:
Interoperability Tests for IPFS
172 lines (139 loc) • 5.08 kB
JavaScript
/* eslint-env mocha */
import { peerIdToRoutingKey } from 'ipns'
import { expect } from 'aegir/chai'
import { daemonFactory } from './utils/daemon-factory.js'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import last from 'it-last'
import { peerIdFromString } from '@libp2p/peer-id'
import defer from 'p-defer'
import pWaitFor from 'p-wait-for'
/**
* @typedef {import('ipfsd-ctl').Controller} Controller
* @typedef {import('ipfsd-ctl').Factory} Factory
*/
const daemonsOptions = {
args: ['--enable-namesys-pubsub'] // enable ipns over pubsub
}
const namespace = '/record/'
const ipfsRef = '/ipfs/QmPFVLPmp9zv5Z5KUqLhe2EivAGccQW2r7M7jhVJGLZoZU'
describe('ipns-pubsub', function () {
/** @type {Controller} */
let goNode0
/** @type {Controller} */
let goNode1
/** @type {Controller} */
let jsNode0
/** @type {Controller} */
let jsNode1
/** @type {Factory} */
let factory
beforeEach('create daemon factory', async () => {
factory = await daemonFactory()
})
// Spawn daemons
beforeEach('create the nodes', async function () {
this.timeout(120e3)
;[
goNode0,
goNode1,
jsNode0,
jsNode1
] = await Promise.all([
factory.spawn({
type: 'go',
test: true,
...daemonsOptions
}),
factory.spawn({
type: 'go',
test: true,
...daemonsOptions
}),
factory.spawn({
type: 'js',
test: true,
...daemonsOptions
}),
factory.spawn({
type: 'js',
test: true,
...daemonsOptions
})
])
})
// Connect nodes and wait for republish
beforeEach('connect the nodes', async function () {
this.timeout(120e3)
await goNode0.api.swarm.connect(goNode1.peer.addresses[0])
await goNode0.api.swarm.connect(jsNode0.peer.addresses[0])
await goNode0.api.swarm.connect(jsNode1.peer.addresses[0])
await jsNode0.api.swarm.connect(jsNode1.peer.addresses[0])
await jsNode0.api.swarm.connect(goNode1.peer.addresses[0])
})
afterEach(() => factory.clean())
it('should get enabled state of pubsub', async function () {
for (const node of [goNode0, goNode1, jsNode0, jsNode1]) {
await expect(node.api.name.pubsub.state()).to.eventually.have.property('enabled', true)
}
})
it('should publish the received record to a go node and a js subscriber should receive it', async function () {
await resolveByPubSub(goNode0, jsNode0)
})
it('should publish the received record to a js node and a go subscriber should receive it', async function () {
await resolveByPubSub(goNode0, jsNode0)
})
it('should publish the received record to a go node and a go subscriber should receive it', async function () {
await resolveByPubSub(goNode0, goNode1)
})
it('should publish the received record to a js node and a js subscriber should receive it', async function () {
await resolveByPubSub(jsNode0, jsNode1)
})
})
// * IPNS resolve subscription test
// * 1) name.resolve(), which subscribes the topic
// * 2) check we are subscribed via name.subs
// * 3) subscribe to the actual pubsub topic
// * 4) publish new ipns record
// * 6) ensure we have received an update via pubsub
// * 7) resolve ipns record
/**
* @param {Controller} publisher
* @param {Controller} subscriber
*/
const resolveByPubSub = async (publisher, subscriber) => {
const routingKey = peerIdToRoutingKey(publisher.peer.id)
const topic = `${namespace}${uint8ArrayToString(routingKey, 'base64url')}`
// should not be subscribed to anything
await expect(subscriber.api.name.pubsub.subs()).to.eventually.have.lengthOf(0)
try {
// attempt to resolve the peer id - n.b js throws here, go does not because streaming API errors don't work over http
await last(subscriber.api.name.resolve(publisher.peer.id, {
timeout: 1000
}))
} catch {}
// wait for publisher to see subscriber's topic subscription
await pWaitFor(async () => {
const peers = await publisher.api.pubsub.peers(topic)
return peers.map(p => p.toString()).includes(subscriber.peer.id.toString())
})
// should now be subscribed to updates for the publisher's peer id
const subs = await subscriber.api.name.pubsub.subs()
expect(subs).to.have.lengthOf(1)
const subbed = peerIdFromString(subs[0].split('/ipns/').pop() || '')
expect(subbed.equals(publisher.peer.id)).to.be.true()
// set up a listener for the pubsub topic for the IPNS key
const deferred = defer()
await subscriber.api.pubsub.subscribe(topic, () => {
deferred.resolve()
})
// publish an update
const res1 = await publisher.api.name.publish(ipfsRef, { resolve: false })
// should receive a message on the topic for the IPNS key
await deferred.promise
// should succeed and be fast
const res2 = await last(subscriber.api.name.resolve(publisher.peer.id, {
timeout: 1000
}))
expect(peerIdFromString(res1.name).toString()).to.equal(publisher.peer.id.toString()) // Published to Node A ID
expect(res2).to.equal(ipfsRef)
}