pzp-db/test/getTangle.test.js

354 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const test = require('node:test')
const assert = require('node:assert')
const path = require('node:path')
const os = require('node:os')
const p = require('node:util').promisify
const rimraf = require('rimraf')
const Keypair = require('pzp-keypair')
const { createPeer } = require('./util')
const DIR = path.join(os.tmpdir(), 'pzp-db-tangle')
rimraf.sync(DIR)
/**
* /-reply1Hi <-\ /--reply3Hi
* root <-< >-reply2 <-<
* \--reply1Lo <-/ \--reply3Lo
*/
test('getTangle()', async (t) => {
let peer
let rootPost, reply1Lo, reply1Hi, reply2, reply3Lo, reply3Hi
let reply1LoText, reply1HiText, reply3LoText, reply3HiText
let tangle
// Setup
{
const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob')
const keypairC = Keypair.generate('ed25519', 'carol')
peer = createPeer({ path: DIR, keypair: keypairA })
await peer.db.loaded()
const id = await p(peer.db.account.create)({
subdomain: 'person',
_nonce: 'alice',
})
// Slow down append so that we can trigger msg creation in parallel
const originalAppend = peer.db._getLog().append
peer.db._getLog().append = function (...args) {
setTimeout(originalAppend, 20, ...args)
}
rootPost = (
await p(peer.db.feed.publish)({
account: id,
keypair: keypairA,
domain: 'comment',
data: { text: 'root' },
})
).id
const [{ id: reply1B }, { id: reply1C }] = await Promise.all([
p(peer.db.feed.publish)({
account: id,
keypair: keypairB,
domain: 'comment',
data: { text: 'reply 1B' },
tangles: [rootPost],
}),
p(peer.db.feed.publish)({
account: id,
keypair: keypairC,
domain: 'comment',
data: { text: 'reply 1C' },
tangles: [rootPost],
}),
])
reply1Lo = reply1B.localeCompare(reply1C) < 0 ? reply1B : reply1C
reply1Hi = reply1B.localeCompare(reply1C) < 0 ? reply1C : reply1B
reply1LoText = reply1B.localeCompare(reply1C) < 0 ? 'reply 1B' : 'reply 1C'
reply1HiText = reply1B.localeCompare(reply1C) < 0 ? 'reply 1C' : 'reply 1B'
reply2 = (
await p(peer.db.feed.publish)({
account: id,
keypair: keypairA,
domain: 'comment',
data: { text: 'reply 2' },
tangles: [rootPost],
})
).id
const [{ id: reply3B }, { id: reply3C }] = await Promise.all([
p(peer.db.feed.publish)({
account: id,
keypair: keypairB,
domain: 'comment',
data: { text: 'reply 3B' },
tangles: [rootPost],
}),
p(peer.db.feed.publish)({
account: id,
keypair: keypairC,
domain: 'comment',
data: { text: 'reply 3C' },
tangles: [rootPost],
}),
])
reply3Lo = reply3B.localeCompare(reply3C) < 0 ? reply3B : reply3C
reply3Hi = reply3B.localeCompare(reply3C) < 0 ? reply3C : reply3B
reply3LoText = reply3B.localeCompare(reply3C) < 0 ? 'reply 3B' : 'reply 3C'
reply3HiText = reply3B.localeCompare(reply3C) < 0 ? 'reply 3C' : 'reply 3B'
tangle = await p(peer.db.getTangle)(rootPost)
}
await t.test('getTangle unknown ID returns null', async (t) => {
assert.equal(
await p(peer.db.getTangle)('Lq6xwbdvGVmSsY3oYRugpZ3DY8chX9SLhRhjJKyZHQn'),
null
)
})
await t.test('Tangle.has', (t) => {
assert.equal(tangle.has(rootPost), true, 'has rootPost')
assert.equal(tangle.has(reply1Lo), true, 'has reply1Lo')
assert.equal(tangle.has(reply1Hi), true, 'has reply1Hi')
assert.equal(tangle.has(reply2), true, 'has reply2A')
assert.equal(tangle.has(reply3Lo), true, 'has reply3Lo')
assert.equal(tangle.has(reply3Hi), true, 'has reply3Hi')
assert.equal(tangle.has('nonsense'), false, 'does not have nonsense')
})
await t.test('Tangle.getDepth', (t) => {
assert.equal(tangle.getDepth(rootPost), 0, 'depth of rootPost is 0')
assert.equal(tangle.getDepth(reply1Lo), 1, 'depth of reply1Lo is 1')
assert.equal(tangle.getDepth(reply1Hi), 1, 'depth of reply1Hi is 1')
assert.equal(tangle.getDepth(reply2), 2, 'depth of reply2A is 2')
assert.equal(tangle.getDepth(reply3Lo), 3, 'depth of reply3Lo is 3')
assert.equal(tangle.getDepth(reply3Hi), 3, 'depth of reply3Hi is 3')
})
await t.test('Tangle.maxDepth', (t) => {
assert.equal(tangle.maxDepth, 3, 'max depth is 3')
})
await t.test('Tangle.topoSort', (t) => {
const sorted = tangle.topoSort()
assert.deepEqual(sorted, [
rootPost,
reply1Lo,
reply1Hi,
reply2,
reply3Lo,
reply3Hi,
])
})
await t.test('Tangle.precedes', (t) => {
assert.equal(
tangle.precedes(rootPost, reply1Lo),
true,
'rootPost precedes reply1Lo'
)
assert.equal(
tangle.precedes(rootPost, reply1Hi),
true,
'rootPost precedes reply1Hi'
)
assert.equal(
tangle.precedes(reply1Hi, rootPost),
false,
'reply1Hi doesnt precede rootPost'
)
assert.equal(
tangle.precedes(reply1Lo, reply1Hi),
false,
'reply1Lo doesnt precede reply1Hi'
)
assert.equal(
tangle.precedes(reply1Lo, reply1Lo),
false,
'reply1Lo doesnt precede itself'
)
assert.equal(
tangle.precedes(reply1Lo, reply3Hi),
true,
'reply1Lo precedes reply3Hi'
)
assert.equal(
tangle.precedes(reply1Hi, reply2),
true,
'reply1Hi precedes reply2A'
)
assert.equal(
tangle.precedes(reply3Lo, reply1Hi),
false,
'reply3Lo doesnt precede reply1Hi'
)
})
await t.test('Tangle.tips', (t) => {
const tips = tangle.tips
assert.equal(tips.size, 2, 'there are 2 tips')
assert.equal(tips.has(reply3Lo), true, 'tips contains reply3Lo')
assert.equal(tips.has(reply3Hi), true, 'tips contains reply3Hi')
})
await t.test('Tangle.getLipmaaSet', (t) => {
assert.equal(tangle.getLipmaaSet(0).size, 0, 'lipmaa 0 (empty)')
assert.equal(tangle.getLipmaaSet(1).size, 1, 'lipmaa 1 (-1)')
assert.equal(tangle.getLipmaaSet(1).has(rootPost), true, 'lipmaa 1 (-1)')
assert.equal(tangle.getLipmaaSet(2).size, 2, 'lipmaa 2 (-1)')
assert.equal(tangle.getLipmaaSet(2).has(reply1Lo), true, 'lipmaa 2 (-1)')
assert.equal(tangle.getLipmaaSet(2).has(reply1Hi), true, 'lipmaa 2 (-1)')
assert.equal(tangle.getLipmaaSet(3).size, 1, 'lipmaa 3 (leap!)')
assert.equal(tangle.getLipmaaSet(3).has(rootPost), true, 'lipmaa 3 (leap!)')
assert.equal(tangle.getLipmaaSet(4).size, 2, 'lipmaa 4 (-1)')
assert.equal(tangle.getLipmaaSet(4).has(reply3Lo), true, 'lipmaa 4 (-1)')
assert.equal(tangle.getLipmaaSet(4).has(reply3Hi), true, 'lipmaa 4 (-1)')
assert.equal(tangle.getLipmaaSet(5).size, 0, 'lipmaa 5 (empty)')
})
await t.test('Tangle.getDeletablesAndErasables basic', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply2)
assert.deepEqual([...deletables], [reply1Hi], 'deletables')
assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables')
})
await t.test('Tangle.getDeletablesAndErasables with many inputs', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(
reply3Lo,
reply2
)
assert.deepEqual([...deletables], [reply1Hi], 'deletables')
assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables')
})
await t.test('Tangle.getDeletablesAndErasables with many inputs (2)', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(
reply3Lo,
reply3Hi
)
assert.deepEqual(
[...deletables],
[reply1Lo, reply1Hi, reply2],
'deletables'
)
assert.deepEqual([...erasables], [rootPost], 'erasables')
})
await t.test('Tangle.getDeletablesAndErasables with lipmaa', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
assert.deepEqual(
[...deletables],
[reply1Lo, reply1Hi, reply2],
'deletables'
)
assert.deepEqual([...erasables], [rootPost], 'erasables')
})
await t.test('Tangle.getMinimumAmong', (t) => {
const actual1 = tangle.getMinimumAmong([reply1Lo, reply1Hi])
const expected1 = [reply1Lo, reply1Hi]
assert.deepEqual(actual1, expected1)
const actual2 = tangle.getMinimumAmong([reply1Lo, reply1Hi, reply2])
const expected2 = [reply1Lo, reply1Hi]
assert.deepEqual(actual2, expected2)
const actual3 = tangle.getMinimumAmong([reply2, reply3Lo, reply3Hi])
const expected3 = [reply2]
assert.deepEqual(actual3, expected3)
const actual4 = tangle.getMinimumAmong([reply1Hi, reply3Lo])
const expected4 = [reply1Hi]
assert.deepEqual(actual4, expected4)
})
await t.test('Tangle.slice', async (t) => {
{
const msgs = await tangle.slice()
const texts = msgs.map((msg) => msg.data?.text)
assert.deepEqual(texts, [
'root',
reply1LoText,
reply1HiText,
'reply 2',
reply3LoText,
reply3HiText,
])
}
{
const msgs = await tangle.slice([], [reply2])
const texts = msgs.map((msg) => msg.data?.text)
assert.deepEqual(texts, ['root', reply1LoText, reply1HiText, 'reply 2'])
}
{
const msgs = await tangle.slice([reply2], [])
const texts = msgs.map((msg) => msg.data?.text)
assert.deepEqual(texts, [
undefined, // root
undefined, // reply1Lo (no need to have a trail from reply1Hi)
'reply 2',
reply3LoText,
reply3HiText,
])
}
{
const msgs = await tangle.slice([reply2], [reply2])
const texts = msgs.map((msg) => msg.data?.text)
assert.deepEqual(texts, [
undefined, // root
undefined, // reply1Lo (no need to have a trail from reply1Hi)
'reply 2',
])
}
{
const msgs = await tangle.slice([reply2], [reply2, reply3Lo])
const texts = msgs.map((msg) => msg.data?.text)
assert.deepEqual(texts, [
undefined, // root
undefined, // reply1Lo (no need to have a trail from reply1Hi)
'reply 2',
reply3LoText,
])
}
})
await t.test('Tangle.topoSort after some deletes and erases', async (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
for (const msgID of deletables) {
await p(peer.db.del)(msgID)
}
for (const msgID of erasables) {
await p(peer.db.erase)(msgID)
}
const tangle2 = await p(peer.db.getTangle)(rootPost)
const sorted = tangle2.topoSort()
assert.deepEqual(sorted, [rootPost, reply3Lo, reply3Hi])
})
await p(peer.close)(true)
})