hyper-sdk
Version:
A Software Development Kit for the Hypercore-Protocol
331 lines (248 loc) • 8.23 kB
JavaScript
import { test } from 'brittle'
import { once } from 'events'
import { create } from './index.js'
import b4a from 'b4a'
import tmp from 'test-tmp'
const NULL_KEY = 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
const NULL_BUFFER = b4a.alloc(32, 0)
const NULL_HEX_KEY = NULL_BUFFER.toString('hex')
const NULL_URL = `hyper://${NULL_KEY}/`
const timeout = 30000
test('Specify storage for sdk', async (t) => {
const storage = await tmp()
const name = 'example'
const data = 'Hello World!'
let sdk = await create({ storage })
let sdk2 = null
try {
try {
sdk2 = await create({ storage })
t.fail(new Error('Should not be able to load SDK over existing dir'))
} catch {
t.pass('Threw error when opening same storage path twice')
} finally {
if (sdk2) await sdk2.close()
}
const core1 = await sdk.get(name)
const url1 = core1.url
await core1.append(data)
await sdk.close()
sdk = await create({ storage })
const core2 = await sdk.get(name)
const url2 = core2.url
t.is(url1, url2, 'Loaded core has same key')
const contents = await core2.get(0)
t.alike(contents.toString('utf8'), data, 'Got data back from disk')
} finally {
await sdk.close()
}
})
test('Support storage reuse by default', async (t) => {
const storage = await tmp()
const sdk = await create({ storage })
const core = await sdk.get('persist in memory')
const key = core.key
const data = b4a.from('beep')
await core.append(data)
await core.close()
t.ok(core.closed, 'initial core was closed')
const coreAgain = await sdk.get(key)
t.alike(await coreAgain.get(0, { wait: false }), data, 'found persisted data')
await sdk.close()
})
test('Load hypercores by names and urls', async (t) => {
const storage = await tmp()
const sdk = await create({ storage })
const name = 'example'
try {
const core = await sdk.get(name)
t.ok(core, 'Got core for name')
const toTry = [
NULL_KEY,
NULL_BUFFER,
NULL_HEX_KEY,
`hyper://${NULL_KEY}`,
`hyper://${NULL_HEX_KEY}`
]
for (const key of toTry) {
const core = await sdk.get(key)
t.ok(core, `Got core for ${key}`)
t.is(core.url, NULL_URL, 'Correct URL got loaded')
}
} finally {
await sdk.close()
}
})
test('Loading same key twice results in same core', async (t) => {
const storage = await tmp()
const sdk = await create({ storage })
const name = 'example'
try {
const core1 = await sdk.get(name)
const core2 = await sdk.get(core1.key)
const core3 = await sdk.get(core1.url)
t.is(core1, core2, 'Key loaded same core from memory')
t.is(core1, core3, 'URL loaded same core from memory')
const drive1 = await sdk.getDrive(name)
const drive2 = await sdk.getDrive(drive1.key)
const drive3 = await sdk.getDrive(drive1.url)
t.is(drive1, drive2, 'Key loaded same drive from memory')
t.is(drive1, drive3, 'URL loaded same drive from memory')
const bee1 = await sdk.getBee(name)
const bee2 = await sdk.getBee(bee1.key)
const bee3 = await sdk.getBee(bee1.url)
t.is(bee1, bee2, 'Key loaded same bee from memory')
t.is(bee1, bee3, 'URL loaded same bee from memory')
await core1.close()
await drive1.close()
const core4 = await sdk.get(name)
t.not(core1, core4, 'New core after close')
const drive4 = await sdk.getDrive(name)
t.not(drive1, drive4, 'New drive after close')
const bee4 = await sdk.getBee(name)
t.not(bee1, bee4, 'New bee after close')
} finally {
await sdk.close()
}
})
test('Resolve DNS entries to keys', async (t) => {
const storage = await tmp()
const expected = NULL_KEY
const sdk = await create({ storage })
try {
const resolved = await sdk.resolveDNSToKey('example.mauve.moe')
t.is(resolved, expected, 'Resolved to correct key')
} finally {
await sdk.close()
}
})
test('Resolve DNS in hyper URLs', async (t) => {
const storage = await tmp()
const expected = NULL_KEY
const sdk = await create({ storage })
try {
const core = await sdk.get('hyper://example.mauve.moe')
t.is(core.id, expected, 'Loaded correct core from DNSLink')
} finally {
await sdk.close()
}
})
test('Get hostname from cache when fetch fails', async (t) => {
const storage = await tmp()
const expected = NULL_KEY
const fetch = globalThis.fetch || (await import('bare-fetch')).default
let isFirst = true
let hasFailed = false
function testFetch (...args) {
if (isFirst) {
isFirst = false
return fetch(...args)
}
hasFailed = true
throw new Error('Simulated Network Fail')
}
let sdk = await create({ fetch: testFetch, storage })
try {
const resolved = await sdk.resolveDNSToKey('example.mauve.moe')
t.is(resolved, expected, 'Resolved to correct key')
await sdk.close()
sdk = await create({ fetch: testFetch, storage })
const resolved2 = await sdk.resolveDNSToKey('example.mauve.moe')
t.is(resolved2, expected, 'Resolved to correct key, without network')
t.is(hasFailed, true, 'Fetch was called and failed')
} finally {
await sdk.close()
}
})
test('Load a core between two peers', { timeout }, async (t) => {
const storage1 = await tmp()
const storage2 = await tmp()
const sdk1 = await create({ storage: storage1 })
const sdk2 = await create({ storage: storage2 })
try {
t.comment('Initializing core on first peer')
const core1 = await sdk1.get('example')
await core1.append('Hello World!')
t.comment('Loading core on second peer')
const core2 = await sdk2.get(core1.url)
t.ok(core2.peers?.length, 'Found peer')
t.is(core2.url, core1.url, 'Got expected URL')
t.is(core2.length, 1, 'Not empty')
const data = await core2.get(0)
t.alike(data, Buffer.from('Hello World!'), 'Got block back out')
} finally {
await Promise.all([
sdk1.close(),
sdk2.close()
])
}
})
test('Connect directly between two peers', { timeout }, async (t) => {
const storage1 = await tmp()
const storage2 = await tmp()
const sdk1 = await create({ storage: storage1 })
const sdk2 = await create({ storage: storage2 })
const onPeer = once(sdk2, 'peer-add')
const onPeernt = once(sdk2, 'peer-remove')
try {
await sdk1.joinPeer(sdk2.publicKey)
const [peerInfo] = await onPeer
t.alike(peerInfo.publicKey, sdk1.publicKey, 'Connected to peer')
} finally {
await Promise.all([
sdk1.close(),
sdk2.close()
])
}
await onPeernt
t.pass('Peer remove event detected')
})
test('Get a hyperdrive and share a file', async (t) => {
const storage1 = await tmp()
const storage2 = await tmp()
const sdk1 = await create({ storage: storage1 })
const sdk2 = await create({ storage: storage2 })
try {
const drive1 = await sdk1.getDrive('example')
const ws = drive1.createWriteStream('/blob.txt')
const onWrote = once(ws, 'close')
ws.write('Hello, ')
ws.write('world!')
ws.end()
await onWrote
const drive2 = await sdk2.getDrive(drive1.url)
t.is(drive2.url, drive1.url, 'Loaded drive has same URL')
const rs = drive2.createReadStream('/blob.txt')
let data = ''
for await (const chunk of rs) {
data += chunk.toString('utf8')
}
t.is(data, 'Hello, world!', 'Loaded expected data')
} finally {
await Promise.all([
sdk1.close(),
sdk2.close()
])
}
})
test('Get a hyperbee and share a key value pair', async (t) => {
const storage1 = await tmp()
const storage2 = await tmp()
const sdk1 = await create({ storage: storage1 })
const sdk2 = await create({ storage: storage2 })
try {
const encodingOpts = { keyEncoding: 'utf8', valueEncoding: 'utf8' }
const db1 = await sdk1.getBee('example', encodingOpts)
await db1.put('hello', 'world')
const db2 = await sdk2.getBee(db1.url, encodingOpts)
t.is(db2.url, db1.url, 'Loaded bee has same URL')
const { value } = await db2.get('hello')
t.is(value, 'world', 'Got value for key')
} finally {
await Promise.all([
sdk1.close(),
sdk2.close()
])
}
})
// test('', async (t) => {})