refactor/prettify tests

This commit is contained in:
Andre Staltz 2023-11-10 11:06:19 +02:00
parent 9356b9b3d9
commit f40ea71ff9
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890
14 changed files with 1533 additions and 1519 deletions

View File

@ -12,217 +12,219 @@ const DIR = path.join(os.tmpdir(), 'ppppp-db-account-add')
rimraf.sync(DIR) rimraf.sync(DIR)
test('account.add()', async (t) => { test('account.add()', async (t) => {
const keypair1 = Keypair.generate('ed25519', 'alice') await t.test('Basic usage', async (t) => {
const keypair2 = Keypair.generate('ed25519', 'bob') const keypair1 = Keypair.generate('ed25519', 'alice')
const keypair2 = Keypair.generate('ed25519', 'bob')
const peer = SecretStack({ appKey: caps.shse }) const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))
.use(require('ssb-box')) .use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR }) .call(null, { keypair: keypair1, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const account = await p(peer.db.account.create)({ const account = await p(peer.db.account.create)({
keypair: keypair1, keypair: keypair1,
domain: 'person', domain: 'person',
}) })
assert.equal(peer.db.account.has({ account, keypair: keypair2 }), false) assert.equal(peer.db.account.has({ account, keypair: keypair2 }), false)
const consent = peer.db.account.consent({ account, keypair: keypair2 }) const consent = peer.db.account.consent({ account, keypair: keypair2 })
const accountRec1 = await p(peer.db.account.add)({ const accountRec1 = await p(peer.db.account.add)({
account, account,
keypair: keypair2, keypair: keypair2,
consent, consent,
powers: ['box'], powers: ['box'],
}) })
assert.ok(accountRec1, 'accountRec1 exists') assert.ok(accountRec1, 'accountRec1 exists')
const { id, msg } = accountRec1 const { id, msg } = accountRec1
assert.ok(account, 'id exists') assert.ok(account, 'id exists')
assert.deepEqual( assert.deepEqual(
msg.data, msg.data,
{ {
action: 'add', action: 'add',
add: { add: {
key: { key: {
purpose: 'sig', purpose: 'sig',
algorithm: 'ed25519', algorithm: 'ed25519',
bytes: keypair2.public, bytes: keypair2.public,
},
consent,
powers: ['box'],
}, },
consent,
powers: ['box'],
}, },
}, 'msg.data.add NEW KEY'
'msg.data.add NEW KEY' )
) assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account') assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips') assert.equal(msg.metadata.domain, 'person', 'msg.metadata.domain')
assert.equal(msg.metadata.domain, 'person', 'msg.metadata.domain') assert.deepEqual(
assert.deepEqual( msg.metadata.tangles,
msg.metadata.tangles, { [account]: { depth: 1, prev: [account] } },
{ [account]: { depth: 1, prev: [account] } }, 'msg.metadata.tangles'
'msg.metadata.tangles' )
) assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY')
assert.equal(msg.pubkey, keypair1.public, 'msg.pubkey OLD KEY')
assert.equal(peer.db.account.has({ account, keypair: keypair2 }), true) assert.equal(peer.db.account.has({ account, keypair: keypair2 }), true)
await p(peer.close)() await p(peer.close)()
})
test('keypair with no "add" powers cannot account.add()', async (t) => {
rimraf.sync(DIR)
const keypair1 = Keypair.generate('ed25519', 'alice')
const keypair2 = Keypair.generate('ed25519', 'bob')
const keypair3 = Keypair.generate('ed25519', 'carol')
const peer1 = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR })
await peer1.db.loaded()
const id = await p(peer1.db.account.create)({
keypair: keypair1,
domain: 'account',
}) })
const msg1 = peer1.db.get(id)
const { msg: msg2 } = await p(peer1.db.account.add)({ await t.test('keypair with no "add" powers cannot add', async (t) => {
account: id, rimraf.sync(DIR)
keypair: keypair2, const keypair1 = Keypair.generate('ed25519', 'alice')
powers: [], const keypair2 = Keypair.generate('ed25519', 'bob')
}) const keypair3 = Keypair.generate('ed25519', 'carol')
assert.equal(msg2.data.add.key.bytes, keypair2.public)
assert.equal(peer1.db.account.has({ account: id, keypair: keypair2 }), true) const peer1 = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR })
await p(peer1.close)() await peer1.db.loaded()
rimraf.sync(DIR) const id = await p(peer1.db.account.create)({
keypair: keypair1,
domain: 'account',
})
const msg1 = peer1.db.get(id)
const peer2 = SecretStack({ appKey: caps.shse }) const { msg: msg2 } = await p(peer1.db.account.add)({
.use(require('../lib')) account: id,
.use(require('ssb-box')) keypair: keypair2,
.call(null, { keypair: keypair2, path: DIR }) powers: [],
})
assert.equal(msg2.data.add.key.bytes, keypair2.public)
await peer2.db.loaded() assert.equal(peer1.db.account.has({ account: id, keypair: keypair2 }), true)
await p(peer2.db.add)(msg1, id)
await p(peer2.db.add)(msg2, id)
// Test author-side power validation await p(peer1.close)()
assert.rejects( rimraf.sync(DIR)
p(peer2.db.account.add)({
const peer2 = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair2, path: DIR })
await peer2.db.loaded()
await p(peer2.db.add)(msg1, id)
await p(peer2.db.add)(msg2, id)
// Test author-side power validation
assert.rejects(
p(peer2.db.account.add)({
account: id,
keypair: keypair3,
powers: [],
}),
/signing keypair does not have the "add" power/
)
// Make the author disobey power validation
const { msg: msg3 } = await p(peer2.db.account.add)({
account: id, account: id,
keypair: keypair3, keypair: keypair3,
powers: [], powers: [],
}), _disobey: true,
/signing keypair does not have the "add" power/ })
)
// Make the author disobey power validation assert.equal(msg3.data.add.key.bytes, keypair3.public)
const { msg: msg3 } = await p(peer2.db.account.add)({
account: id, await p(peer2.close)()
keypair: keypair3, rimraf.sync(DIR)
powers: [],
_disobey: true, const peer1again = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR })
await peer1again.db.loaded()
await p(peer1again.db.add)(msg1, id) // re-add because lost during rimraf
await p(peer1again.db.add)(msg2, id) // re-add because lost during rimraf
// Test replicator-side power validation
assert.rejects(
p(peer1again.db.add)(msg3, id),
/add\(\) failed to verify msg/
)
await p(peer1again.close)()
}) })
assert.equal(msg3.data.add.key.bytes, keypair3.public) await t.test('publish with a key in the account', async (t) => {
rimraf.sync(DIR)
await p(peer2.close)() const keypair1 = Keypair.generate('ed25519', 'alice')
rimraf.sync(DIR) const keypair2 = Keypair.generate('ed25519', 'bob')
const peer1again = SecretStack({ appKey: caps.shse }) let peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))
.use(require('ssb-box')) .use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR }) .call(null, { keypair: keypair1, path: DIR })
await peer1again.db.loaded() await peer.db.loaded()
await p(peer1again.db.add)(msg1, id) // re-add because lost during rimraf
await p(peer1again.db.add)(msg2, id) // re-add because lost during rimraf
// Test replicator-side power validation const account = await p(peer.db.account.create)({
assert.rejects( keypair: keypair1,
p(peer1again.db.add)(msg3, id), domain: 'person',
/add\(\) failed to verify msg/ })
) const accountMsg0 = peer.db.get(account)
await p(peer1again.close)() // Consent is implicitly created because keypair2 has .private
}) const accountRec1 = await p(peer.db.account.add)({
account,
test('publish with a key in the account', async (t) => { keypair: keypair2,
rimraf.sync(DIR) })
const keypair1 = Keypair.generate('ed25519', 'alice') const postRec = await p(peer.db.feed.publish)({
const keypair2 = Keypair.generate('ed25519', 'bob')
let peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair1, path: DIR })
await peer.db.loaded()
const account = await p(peer.db.account.create)({
keypair: keypair1,
domain: 'person',
})
const accountMsg0 = peer.db.get(account)
// Consent is implicitly created because keypair2 has .private
const accountRec1 = await p(peer.db.account.add)({
account,
keypair: keypair2,
})
const postRec = await p(peer.db.feed.publish)({
account,
domain: 'post',
data: { text: 'hello' },
keypair: keypair2,
})
assert.equal(postRec.msg.data.text, 'hello', 'post text correct')
const postsID = peer.db.feed.getID(account, 'post')
assert.ok(postsID, 'postsID exists')
const recs = [...peer.db.records()]
assert.equal(recs.length, 4, '4 records')
const [_accountRec0, _accountRec1, postsRoot, _post] = recs
assert.deepEqual(_accountRec0.msg, accountMsg0, 'accountMsg0')
assert.deepEqual(_accountRec1.msg, accountRec1.msg, 'accountMsg1')
assert.deepEqual(
postsRoot.msg.metadata,
{
dataHash: null,
dataSize: 0,
account, account,
accountTips: null,
tangles: {},
domain: 'post', domain: 'post',
v: 3, data: { text: 'hello' },
}, keypair: keypair2,
'postsRoot' })
) assert.equal(postRec.msg.data.text, 'hello', 'post text correct')
assert.deepEqual(_post.msg, postRec.msg, 'postMsg') const postsID = peer.db.feed.getID(account, 'post')
assert.ok(postsID, 'postsID exists')
await p(peer.close)() const recs = [...peer.db.records()]
assert.equal(recs.length, 4, '4 records')
const [_accountRec0, _accountRec1, postsRoot, _post] = recs
assert.deepEqual(_accountRec0.msg, accountMsg0, 'accountMsg0')
assert.deepEqual(_accountRec1.msg, accountRec1.msg, 'accountMsg1')
assert.deepEqual(
postsRoot.msg.metadata,
{
dataHash: null,
dataSize: 0,
account,
accountTips: null,
tangles: {},
domain: 'post',
v: 3,
},
'postsRoot'
)
assert.deepEqual(_post.msg, postRec.msg, 'postMsg')
// Re-load as Carol, add the msgs to validate them await p(peer.close)()
rimraf.sync(DIR)
const keypair3 = Keypair.generate('ed25519', 'carol')
const carol = SecretStack({ appKey: caps.shse }) // Re-load as Carol, add the msgs to validate them
.use(require('../lib')) rimraf.sync(DIR)
.use(require('ssb-box')) const keypair3 = Keypair.generate('ed25519', 'carol')
.call(null, { keypair: keypair3, path: DIR })
await carol.db.loaded() const carol = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypair3, path: DIR })
await p(carol.db.add)(accountMsg0, account) await carol.db.loaded()
await p(carol.db.add)(accountRec1.msg, account)
await p(carol.db.add)(postsRoot.msg, postsID)
await p(carol.db.add)(postRec.msg, postsID)
// t.pass('carol added all msgs successfully')
await p(carol.close)() await p(carol.db.add)(accountMsg0, account)
await p(carol.db.add)(accountRec1.msg, account)
await p(carol.db.add)(postsRoot.msg, postsID)
await p(carol.db.add)(postRec.msg, postsID)
// t.pass('carol added all msgs successfully')
await p(carol.close)()
})
}) })

View File

@ -11,148 +11,150 @@ const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-account-create') const DIR = path.join(os.tmpdir(), 'ppppp-db-account-create')
rimraf.sync(DIR) rimraf.sync(DIR)
test('account.create() with just "domain"', async (t) => { test('account.create() ', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') await t.test('create with just "domain"', async (t) => {
const peer = SecretStack({ appKey: caps.shse }) const keypair = Keypair.generate('ed25519', 'alice')
.use(require('../lib')) const peer = SecretStack({ appKey: caps.shse })
.use(require('ssb-box')) .use(require('../lib'))
.call(null, { keypair, path: DIR }) .use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
const account = await p(peer.db.account.create)({ const account = await p(peer.db.account.create)({
domain: 'person', domain: 'person',
_nonce: 'MYNONCE', _nonce: 'MYNONCE',
}) })
assert.ok(account, 'accountRec0 exists') assert.ok(account, 'accountRec0 exists')
const msg = peer.db.get(account) const msg = peer.db.get(account)
assert.deepEqual( assert.deepEqual(
msg.data, msg.data,
{ {
action: 'add', action: 'add',
add: { add: {
key: { key: {
purpose: 'sig', purpose: 'sig',
algorithm: 'ed25519', algorithm: 'ed25519',
bytes: keypair.public, bytes: keypair.public,
},
nonce: 'MYNONCE',
powers: ['add', 'del', 'box'],
}, },
nonce: 'MYNONCE',
powers: ['add', 'del', 'box'],
}, },
}, 'msg.data.add'
'msg.data.add' )
) assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account') assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips') assert.deepEqual(
assert.deepEqual( Object.keys(msg.metadata.tangles),
Object.keys(msg.metadata.tangles), [],
[], 'msg.metadata.tangles'
'msg.metadata.tangles' )
) assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() await p(peer.close)()
})
test('account.create() with "keypair" and "domain"', async (t) => {
rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
const account = await p(peer.db.account.create)({
keypair,
domain: 'person',
}) })
assert.ok(account, 'account created')
const msg = peer.db.get(account)
assert.equal(msg.data.add.key.bytes, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() await t.test('create with "keypair" and "domain"', async (t) => {
}) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
test('account.find() can find', async (t) => { const peer = SecretStack({ appKey: caps.shse })
rimraf.sync(DIR) .use(require('../lib'))
const keypair = Keypair.generate('ed25519', 'alice') .use(require('ssb-box'))
const domain = 'person' .call(null, { keypair, path: DIR })
const peer = SecretStack({ appKey: caps.shse }) await peer.db.loaded()
.use(require('../lib')) const account = await p(peer.db.account.create)({
.use(require('ssb-box')) keypair,
.call(null, { keypair, path: DIR }) domain: 'person',
})
assert.ok(account, 'account created')
const msg = peer.db.get(account)
assert.equal(msg.data.add.key.bytes, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await peer.db.loaded() await p(peer.close)()
const account = await p(peer.db.account.create)({ keypair, domain })
assert.ok(account, 'account created')
const found = await p(peer.db.account.find)({ keypair, domain })
assert.equal(found, account, 'found')
await p(peer.close)()
})
test('account.findOrCreate() can find', async (t) => {
rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const domain = 'person'
const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
const account = await p(peer.db.account.create)({ keypair, domain })
assert.ok(account, 'account created')
const found = await p(peer.db.account.findOrCreate)({ keypair, domain })
assert.equal(found, account, 'found')
await p(peer.close)()
})
test('account.findOrCreate() can create', async (t) => {
rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const domain = 'person'
const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
let gotError = false
await p(peer.db.account.find)({ keypair, domain }).catch((err) => {
assert.equal(err.cause, 'ENOENT')
gotError = true
}) })
assert.ok(gotError, 'account not found')
const account = await p(peer.db.account.findOrCreate)({ keypair, domain }) await t.test('account.find() can find', async (t) => {
assert.ok(account, 'account created') rimraf.sync(DIR)
const msg = peer.db.get(account) const keypair = Keypair.generate('ed25519', 'alice')
assert.equal(msg.data.add.key.bytes, keypair.public, 'msg.data.add') const domain = 'person'
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)() const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
const account = await p(peer.db.account.create)({ keypair, domain })
assert.ok(account, 'account created')
const found = await p(peer.db.account.find)({ keypair, domain })
assert.equal(found, account, 'found')
await p(peer.close)()
})
await t.test('account.findOrCreate() can find', async (t) => {
rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const domain = 'person'
const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
const account = await p(peer.db.account.create)({ keypair, domain })
assert.ok(account, 'account created')
const found = await p(peer.db.account.findOrCreate)({ keypair, domain })
assert.equal(found, account, 'found')
await p(peer.close)()
})
await t.test('account.findOrCreate() can create', async (t) => {
rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const domain = 'person'
const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
let gotError = false
await p(peer.db.account.find)({ keypair, domain }).catch((err) => {
assert.equal(err.cause, 'ENOENT')
gotError = true
})
assert.ok(gotError, 'account not found')
const account = await p(peer.db.account.findOrCreate)({ keypair, domain })
assert.ok(account, 'account created')
const msg = peer.db.get(account)
assert.equal(msg.data.add.key.bytes, keypair.public, 'msg.data.add')
assert.equal(msg.metadata.account, 'self', 'msg.metadata.account')
assert.equal(msg.metadata.accountTips, null, 'msg.metadata.accountTips')
assert.deepEqual(
Object.keys(msg.metadata.tangles),
[],
'msg.metadata.tangles'
)
assert.equal(msg.pubkey, keypair.public, 'msg.pubkey')
await p(peer.close)()
})
}) })

View File

@ -6,14 +6,13 @@ const p = require('node:util').promisify
const rimraf = require('rimraf') const rimraf = require('rimraf')
const SecretStack = require('secret-stack') const SecretStack = require('secret-stack')
const Log = require('../lib/log') const Log = require('../lib/log')
const push = require('push-stream')
const caps = require('ppppp-caps') const caps = require('ppppp-caps')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-del') const DIR = path.join(os.tmpdir(), 'ppppp-db-del')
rimraf.sync(DIR) rimraf.sync(DIR)
test('del', async (t) => { test('del()', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shse }) const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))

View File

@ -13,7 +13,7 @@ const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-erase') const DIR = path.join(os.tmpdir(), 'ppppp-db-erase')
rimraf.sync(DIR) rimraf.sync(DIR)
test('erase', async (t) => { test('erase()', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
const peer = SecretStack({ appKey: caps.shse }) const peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))

View File

@ -12,31 +12,23 @@ const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish')
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice') test('feed.getID()', async (t) => {
let peer const keypair = Keypair.generate('ed25519', 'alice')
let id const peer = SecretStack({ appKey: caps.shse })
let moot
let mootID
test('setup', async (t) => {
peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))
.use(require('ssb-box')) .use(require('ssb-box'))
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
id = (await p(peer.db.account.create)({domain: 'person'})) const id = await p(peer.db.account.create)({ domain: 'person' })
moot = MsgV3.createMoot(id, 'post', keypair) const moot = MsgV3.createMoot(id, 'post', keypair)
mootID = MsgV3.getMsgID(moot) const mootID = MsgV3.getMsgID(moot)
await p(peer.db.add)(moot, mootID) await p(peer.db.add)(moot, mootID)
})
test('feed.getID()', async (t) => {
const feedID = peer.db.feed.getID(id, 'post') const feedID = peer.db.feed.getID(id, 'post')
assert.equal(feedID, mootID, 'feed.getID() returns moot ID') assert.equal(feedID, mootID, 'feed.getID() returns moot ID')
})
test('teardown', (t) => { await p(peer.close)(true)
peer.close(t.end)
}) })

View File

@ -12,148 +12,151 @@ const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish') const DIR = path.join(os.tmpdir(), 'ppppp-db-feed-publish')
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice')
const bobKeypair = Keypair.generate('ed25519', 'bob')
let peer
let id
let moot
let mootID
test('setup', async (t) => {
peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
await peer.db.loaded()
id = (await p(peer.db.account.create)({domain: 'person'}))
moot = MsgV3.createMoot(id, 'post', keypair)
mootID = MsgV3.getMsgID(moot)
})
let msgID1
let rec1
let msgID2
test('feed.publish()', async (t) => { test('feed.publish()', async (t) => {
rec1 = await p(peer.db.feed.publish)({ const keypair = Keypair.generate('ed25519', 'alice')
account: id, const bobKeypair = Keypair.generate('ed25519', 'bob')
domain: 'post', let peer
data: { text: 'I am 1st post' }, let id
}) let moot
assert.equal(rec1.msg.data.text, 'I am 1st post', 'msg1 text correct') let mootID
assert.equal(
rec1.msg.metadata.tangles[mootID].depth,
1,
'msg1 tangle depth correct'
)
assert.deepEqual(
rec1.msg.metadata.tangles[mootID].prev,
[mootID],
'msg1 tangle prev correct'
)
msgID1 = MsgV3.getMsgID(rec1.msg) // Setup
{
peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair, path: DIR })
const rec2 = await p(peer.db.feed.publish)({ await peer.db.loaded()
account: id,
domain: 'post', id = await p(peer.db.account.create)({ domain: 'person' })
data: { text: 'I am 2nd post' }, moot = MsgV3.createMoot(id, 'post', keypair)
mootID = MsgV3.getMsgID(moot)
}
let msgID1
let rec1
let msgID2
await t.test('can add new msgs to the db', async (t) => {
rec1 = await p(peer.db.feed.publish)({
account: id,
domain: 'post',
data: { text: 'I am 1st post' },
})
assert.equal(rec1.msg.data.text, 'I am 1st post', 'msg1 text correct')
assert.equal(
rec1.msg.metadata.tangles[mootID].depth,
1,
'msg1 tangle depth correct'
)
assert.deepEqual(
rec1.msg.metadata.tangles[mootID].prev,
[mootID],
'msg1 tangle prev correct'
)
msgID1 = MsgV3.getMsgID(rec1.msg)
const rec2 = await p(peer.db.feed.publish)({
account: id,
domain: 'post',
data: { text: 'I am 2nd post' },
})
assert.equal(rec2.msg.data.text, 'I am 2nd post', 'msg2 text correct')
assert.equal(
rec2.msg.metadata.tangles[mootID].depth,
2,
'msg2 tangle depth correct'
)
assert.deepEqual(
rec2.msg.metadata.tangles[mootID].prev,
[msgID1],
'msg2 tangle prev correct'
)
msgID2 = MsgV3.getMsgID(rec2.msg)
}) })
assert.equal(rec2.msg.data.text, 'I am 2nd post', 'msg2 text correct')
assert.equal( await t.test('merges tangle after a forked add()', async (t) => {
rec2.msg.metadata.tangles[mootID].depth, const tangle = new MsgV3.Tangle(mootID)
2, tangle.add(mootID, moot)
'msg2 tangle depth correct' tangle.add(rec1.id, rec1.msg)
)
assert.deepEqual( const msg3 = MsgV3.create({
rec2.msg.metadata.tangles[mootID].prev, keypair,
[msgID1], account: id,
'msg2 tangle prev correct' accountTips: [id],
) domain: 'post',
msgID2 = MsgV3.getMsgID(rec2.msg) data: { text: '3rd post forked from 1st' },
}) tangles: {
[mootID]: tangle,
test('add() forked then feed.publish() merged', async (t) => { },
const tangle = new MsgV3.Tangle(mootID) })
tangle.add(mootID, moot)
tangle.add(rec1.id, rec1.msg) const rec3 = await p(peer.db.add)(msg3, mootID)
const msgID3 = MsgV3.getMsgID(rec3.msg)
const msg3 = MsgV3.create({
keypair, const rec4 = await p(peer.db.feed.publish)({
account: id, account: id,
accountTips: [id], domain: 'post',
domain: 'post', data: { text: 'I am 4th post' },
data: { text: '3rd post forked from 1st' }, })
tangles: { assert.ok(rec4, '4th post published')
[mootID]: tangle, assert.equal(
}, rec4.msg.metadata.tangles[mootID].prev.length,
}) 3,
'msg4 prev has 3' // is root, msg2 and msg3'
const rec3 = await p(peer.db.add)(msg3, mootID) )
const msgID3 = MsgV3.getMsgID(rec3.msg) assert.ok(
rec4.msg.metadata.tangles[mootID].prev.includes(mootID),
const rec4 = await p(peer.db.feed.publish)({ 'msg4 prev has root'
account: id, )
domain: 'post', assert.ok(
data: { text: 'I am 4th post' }, rec4.msg.metadata.tangles[mootID].prev.includes(msgID2),
}) 'msg4 prev has msg2'
assert.ok(rec4, '4th post published') )
assert.equal( assert.ok(
rec4.msg.metadata.tangles[mootID].prev.length, rec4.msg.metadata.tangles[mootID].prev.includes(msgID3),
3, 'msg4 prev has msg3'
'msg4 prev has 3' // is root, msg2 and msg3' )
) })
assert.ok(
rec4.msg.metadata.tangles[mootID].prev.includes(mootID), await t.test('publish encrypted with box', async (t) => {
'msg4 prev has root' const recEncrypted = await p(peer.db.feed.publish)({
) account: id,
assert.ok( domain: 'post',
rec4.msg.metadata.tangles[mootID].prev.includes(msgID2), data: { text: 'I am chewing food', recps: [keypair.public] },
'msg4 prev has msg2' encryptionFormat: 'box',
) })
assert.ok( assert.equal(typeof recEncrypted.msg.data, 'string')
rec4.msg.metadata.tangles[mootID].prev.includes(msgID3), assert.ok(recEncrypted.msg.data.endsWith('.box'), '.box')
'msg4 prev has msg3'
) const msgDecrypted = peer.db.get(recEncrypted.id)
}) assert.equal(msgDecrypted.data.text, 'I am chewing food')
})
test('feed.publish() encrypted with box', async (t) => {
const recEncrypted = await p(peer.db.feed.publish)({ await t.test('publish with tangles', async (t) => {
account: id, const recA = await p(peer.db.feed.publish)({
domain: 'post', account: id,
data: { text: 'I am chewing food', recps: [keypair.public] }, domain: 'comment',
encryptionFormat: 'box', data: { text: 'I am root' },
}) })
assert.equal(typeof recEncrypted.msg.data, 'string') assert.equal(recA.msg.data.text, 'I am root', 'root text correct')
assert.ok(recEncrypted.msg.data.endsWith('.box'), '.box')
const recB = await p(peer.db.feed.publish)({
const msgDecrypted = peer.db.get(recEncrypted.id) account: id,
assert.equal(msgDecrypted.data.text, 'I am chewing food') domain: 'comment',
}) data: { text: 'I am comment 1' },
tangles: [recA.id],
test('feed.publish() with tangles', async (t) => { keypair: bobKeypair,
const recA = await p(peer.db.feed.publish)({ })
account: id, assert.equal(recB.msg.metadata.tangles[recA.id].depth, 1, 'tangle depth 1')
domain: 'comment', assert.deepEqual(
data: { text: 'I am root' }, recB.msg.metadata.tangles[recA.id].prev,
}) [recA.id],
assert.equal(recA.msg.data.text, 'I am root', 'root text correct') 'tangle prev'
)
const recB = await p(peer.db.feed.publish)({ })
account: id,
domain: 'comment', await p(peer.close)(true)
data: { text: 'I am comment 1' },
tangles: [recA.id],
keypair: bobKeypair,
})
assert.equal(recB.msg.metadata.tangles[recA.id].depth, 1, 'tangle depth 1')
assert.deepEqual(
recB.msg.metadata.tangles[recA.id].prev,
[recA.id],
'tangle prev'
)
})
test('teardown', (t) => {
peer.close(t.end)
}) })

View File

@ -12,34 +12,27 @@ const MsgV3 = require('../lib/msg-v3')
const DIR = path.join(os.tmpdir(), 'ppppp-db-get') const DIR = path.join(os.tmpdir(), 'ppppp-db-get')
rimraf.sync(DIR) rimraf.sync(DIR)
const keypair = Keypair.generate('ed25519', 'alice') test('get()', async (t) => {
let peer const keypair = Keypair.generate('ed25519', 'alice')
let id const peer = SecretStack({ appKey: caps.shse })
let msgID1
test('setup', async (t) => {
peer = SecretStack({ appKey: caps.shse })
.use(require('../lib')) .use(require('../lib'))
.use(require('ssb-box')) .use(require('ssb-box'))
.call(null, { keypair, path: DIR }) .call(null, { keypair, path: DIR })
await peer.db.loaded() await peer.db.loaded()
id = (await p(peer.db.account.create)({domain: 'person'})) const id = await p(peer.db.account.create)({ domain: 'person' })
const rec1 = await p(peer.db.feed.publish)({ const rec1 = await p(peer.db.feed.publish)({
account: id, account: id,
domain: 'post', domain: 'post',
data: { text: 'I am 1st post' }, data: { text: 'I am 1st post' },
}) })
msgID1 = MsgV3.getMsgID(rec1.msg) const msgID1 = MsgV3.getMsgID(rec1.msg)
})
test('get() supports msg IDs', async (t) => {
const msg = peer.db.get(msgID1) const msg = peer.db.get(msgID1)
assert.ok(msg, 'msg exists') assert.ok(msg, 'msg exists')
assert.equal(msg.data.text, 'I am 1st post') assert.equal(msg.data.text, 'I am 1st post')
})
test('teardown', (t) => { await p(peer.close)(true)
peer.close(t.end)
}) })

View File

@ -11,263 +11,280 @@ const Keypair = require('ppppp-keypair')
const DIR = path.join(os.tmpdir(), 'ppppp-db-tangle') const DIR = path.join(os.tmpdir(), 'ppppp-db-tangle')
rimraf.sync(DIR) rimraf.sync(DIR)
let peer test('getTangle()', async (t) => {
let rootPost, reply1Lo, reply1Hi, reply2, reply3Lo, reply3Hi let peer
let tangle let rootPost, reply1Lo, reply1Hi, reply2, reply3Lo, reply3Hi
test('setup', async (t) => { let tangle
const keypairA = Keypair.generate('ed25519', 'alice')
const keypairB = Keypair.generate('ed25519', 'bob')
const keypairC = Keypair.generate('ed25519', 'carol')
peer = SecretStack({ appKey: caps.shse }) // Setup
.use(require('../lib')) {
.use(require('ssb-box')) const keypairA = Keypair.generate('ed25519', 'alice')
.call(null, { keypair: keypairA, path: DIR }) const keypairB = Keypair.generate('ed25519', 'bob')
const keypairC = Keypair.generate('ed25519', 'carol')
await peer.db.loaded() peer = SecretStack({ appKey: caps.shse })
.use(require('../lib'))
.use(require('ssb-box'))
.call(null, { keypair: keypairA, path: DIR })
const id = await p(peer.db.account.create)({ domain: 'person' }) await peer.db.loaded()
// Slow down append so that we can trigger msg creation in parallel const id = await p(peer.db.account.create)({ domain: 'person' })
const originalAppend = peer.db._getLog().append
peer.db._getLog().append = function (...args) { // Slow down append so that we can trigger msg creation in parallel
setTimeout(originalAppend, 20, ...args) 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
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
tangle = peer.db.getTangle(rootPost)
} }
rootPost = ( await t.test('Tangle.has', (t) => {
await p(peer.db.feed.publish)({ assert.equal(tangle.has(rootPost), true, 'has rootPost')
account: id, assert.equal(tangle.has(reply1Lo), true, 'has reply1Lo')
keypair: keypairA, assert.equal(tangle.has(reply1Hi), true, 'has reply1Hi')
domain: 'comment', assert.equal(tangle.has(reply2), true, 'has reply2A')
data: { text: 'root' }, assert.equal(tangle.has(reply3Lo), true, 'has reply3Lo')
}) assert.equal(tangle.has(reply3Hi), true, 'has reply3Hi')
).id assert.equal(tangle.has('nonsense'), false, 'does not have nonsense')
})
const [{ id: reply1B }, { id: reply1C }] = await Promise.all([ await t.test('Tangle.getDepth', (t) => {
p(peer.db.feed.publish)({ assert.equal(tangle.getDepth(rootPost), 0, 'depth of rootPost is 0')
account: id, assert.equal(tangle.getDepth(reply1Lo), 1, 'depth of reply1Lo is 1')
keypair: keypairB, assert.equal(tangle.getDepth(reply1Hi), 1, 'depth of reply1Hi is 1')
domain: 'comment', assert.equal(tangle.getDepth(reply2), 2, 'depth of reply2A is 2')
data: { text: 'reply 1B' }, assert.equal(tangle.getDepth(reply3Lo), 3, 'depth of reply3Lo is 3')
tangles: [rootPost], assert.equal(tangle.getDepth(reply3Hi), 3, 'depth of reply3Hi is 3')
}), })
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
reply2 = ( await t.test('Tangle.maxDepth', (t) => {
await p(peer.db.feed.publish)({ assert.equal(tangle.maxDepth, 3, 'max depth is 3')
account: id, })
keypair: keypairA,
domain: 'comment',
data: { text: 'reply 2' },
tangles: [rootPost],
})
).id
const [{ id: reply3B }, { id: reply3C }] = await Promise.all([ await t.test('Tangle.topoSort', (t) => {
p(peer.db.feed.publish)({ const sorted = tangle.topoSort()
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
tangle = peer.db.getTangle(rootPost) assert.deepEqual(sorted, [
}) rootPost,
reply1Lo,
reply1Hi,
reply2,
reply3Lo,
reply3Hi,
])
})
test('Tangle.has', (t) => { await t.test('Tangle.precedes', (t) => {
assert.equal(tangle.has(rootPost), true, 'has rootPost') assert.equal(
assert.equal(tangle.has(reply1Lo), true, 'has reply1Lo') tangle.precedes(rootPost, reply1Lo),
assert.equal(tangle.has(reply1Hi), true, 'has reply1Hi') true,
assert.equal(tangle.has(reply2), true, 'has reply2A') 'rootPost precedes reply1Lo'
assert.equal(tangle.has(reply3Lo), true, 'has reply3Lo') )
assert.equal(tangle.has(reply3Hi), true, 'has reply3Hi') assert.equal(
assert.equal(tangle.has('nonsense'), false, 'does not have nonsense') 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'
)
})
test('Tangle.getDepth', (t) => { await t.test('Tangle.tips', (t) => {
assert.equal(tangle.getDepth(rootPost), 0, 'depth of rootPost is 0') const tips = tangle.tips
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')
})
test('Tangle.maxDepth', (t) => { assert.equal(tips.size, 2, 'there are 2 tips')
assert.equal(tangle.maxDepth, 3, 'max depth is 3') assert.equal(tips.has(reply3Lo), true, 'tips contains reply3Lo')
}) assert.equal(tips.has(reply3Hi), true, 'tips contains reply3Hi')
})
test('Tangle.topoSort', (t) => { await t.test('Tangle.getLipmaaSet', (t) => {
const sorted = tangle.topoSort() assert.equal(tangle.getLipmaaSet(0).size, 0, 'lipmaa 0 (empty)')
assert.deepEqual(sorted, [ assert.equal(tangle.getLipmaaSet(1).size, 1, 'lipmaa 1 (-1)')
rootPost, assert.equal(tangle.getLipmaaSet(1).has(rootPost), true, 'lipmaa 1 (-1)')
reply1Lo,
reply1Hi,
reply2,
reply3Lo,
reply3Hi,
])
})
test('Tangle.precedes', (t) => { assert.equal(tangle.getLipmaaSet(2).size, 2, 'lipmaa 2 (-1)')
assert.equal( assert.equal(tangle.getLipmaaSet(2).has(reply1Lo), true, 'lipmaa 2 (-1)')
tangle.precedes(rootPost, reply1Lo), assert.equal(tangle.getLipmaaSet(2).has(reply1Hi), true, 'lipmaa 2 (-1)')
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'
)
})
test('Tangle.tips', (t) => { assert.equal(tangle.getLipmaaSet(3).size, 1, 'lipmaa 3 (leap!)')
const tips = tangle.tips assert.equal(tangle.getLipmaaSet(3).has(rootPost), true, 'lipmaa 3 (leap!)')
assert.equal(tips.size, 2, 'there are 2 tips') assert.equal(tangle.getLipmaaSet(4).size, 2, 'lipmaa 4 (-1)')
assert.equal(tips.has(reply3Lo), true, 'tips contains reply3Lo') assert.equal(tangle.getLipmaaSet(4).has(reply3Lo), true, 'lipmaa 4 (-1)')
assert.equal(tips.has(reply3Hi), true, 'tips contains reply3Hi') assert.equal(tangle.getLipmaaSet(4).has(reply3Hi), true, 'lipmaa 4 (-1)')
})
test('Tangle.getLipmaaSet', (t) => { assert.equal(tangle.getLipmaaSet(5).size, 0, 'lipmaa 5 (empty)')
assert.equal(tangle.getLipmaaSet(0).size, 0, 'lipmaa 0 (empty)') })
assert.equal(tangle.getLipmaaSet(1).size, 1, 'lipmaa 1 (-1)') await t.test('Tangle.getDeletablesAndErasables basic', (t) => {
assert.equal(tangle.getLipmaaSet(1).has(rootPost), true, 'lipmaa 1 (-1)') const { deletables, erasables } = tangle.getDeletablesAndErasables(reply2)
assert.equal(tangle.getLipmaaSet(2).size, 2, 'lipmaa 2 (-1)') assert.deepEqual([...deletables], [reply1Hi], 'deletables')
assert.equal(tangle.getLipmaaSet(2).has(reply1Lo), true, 'lipmaa 2 (-1)') assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables')
assert.equal(tangle.getLipmaaSet(2).has(reply1Hi), true, 'lipmaa 2 (-1)') })
assert.equal(tangle.getLipmaaSet(3).size, 1, 'lipmaa 3 (leap!)') await t.test('Tangle.getDeletablesAndErasables with many inputs', (t) => {
assert.equal(tangle.getLipmaaSet(3).has(rootPost), true, 'lipmaa 3 (leap!)') const { deletables, erasables } = tangle.getDeletablesAndErasables(
reply3Lo,
reply2
)
assert.equal(tangle.getLipmaaSet(4).size, 2, 'lipmaa 4 (-1)') assert.deepEqual([...deletables], [reply1Hi], 'deletables')
assert.equal(tangle.getLipmaaSet(4).has(reply3Lo), true, 'lipmaa 4 (-1)') assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables')
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 with many inputs again',
(t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(
reply3Lo,
reply3Hi
)
test('Tangle.getDeletablesAndErasables basic', (t) => { assert.deepEqual(
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply2) [...deletables],
[reply1Lo, reply1Hi, reply2],
assert.deepEqual([...deletables], [reply1Hi], 'deletables') 'deletables'
assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables') )
}) assert.deepEqual([...erasables], [rootPost], 'erasables')
}
test('Tangle.getDeletablesAndErasables with many inputs', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(
reply3Lo,
reply2
) )
assert.deepEqual([...deletables], [reply1Hi], 'deletables') await t.test('Tangle.getDeletablesAndErasables with lipmaa', (t) => {
assert.deepEqual([...erasables], [reply1Lo, rootPost], 'erasables') const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
})
test('Tangle.getDeletablesAndErasables with many inputs again', (t) => { assert.deepEqual(
const { deletables, erasables } = tangle.getDeletablesAndErasables( [...deletables],
reply3Lo, [reply1Lo, reply1Hi, reply2],
reply3Hi '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.topoSort after some have been deleted and erased',
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 = peer.db.getTangle(rootPost)
const sorted = tangle2.topoSort()
assert.deepEqual(sorted, [rootPost, reply3Lo, reply3Hi])
}
) )
assert.deepEqual([...deletables], [reply1Lo, reply1Hi, reply2], 'deletables')
assert.deepEqual([...erasables], [rootPost], 'erasables')
})
test('Tangle.getDeletablesAndErasables with lipmaa', (t) => {
const { deletables, erasables } = tangle.getDeletablesAndErasables(reply3Lo)
assert.deepEqual([...deletables], [reply1Lo, reply1Hi, reply2], 'deletables')
assert.deepEqual([...erasables], [rootPost], 'erasables')
})
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)
})
test('Tangle.topoSort after some have been deleted and erased', 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 = peer.db.getTangle(rootPost)
const sorted = tangle2.topoSort()
assert.deepEqual(sorted, [rootPost, reply3Lo, reply3Hi])
})
test('teardown', async (t) => {
await p(peer.close)(true) await p(peer.close)(true)
}) })

View File

@ -4,74 +4,76 @@ const fs = require('node:fs')
const p = require('node:util').promisify const p = require('node:util').promisify
const Log = require('../../lib/log') const Log = require('../../lib/log')
test('Log handles basic binary records', async function (t) { test('Log basics', async function (t) {
const file = '/tmp/ppppp-db-log-test-basic-binary.log' await t.test('Log handles basic binary records', async function (t) {
try { const file = '/tmp/ppppp-db-log-test-basic-binary.log'
fs.unlinkSync(file) try {
} catch (_) {} fs.unlinkSync(file)
const log = Log(file, { blockSize: 2 * 1024 }) } catch (_) {}
const log = Log(file, { blockSize: 2 * 1024 })
const msg1 = Buffer.from('testing') const msg1 = Buffer.from('testing')
const msg2 = Buffer.from('testing2') const msg2 = Buffer.from('testing2')
const offset1 = await p(log.append)(msg1) const offset1 = await p(log.append)(msg1)
assert.equal(offset1, 0) assert.equal(offset1, 0)
const offset2 = await p(log.append)(msg2) const offset2 = await p(log.append)(msg2)
assert.equal(offset2, msg1.length + 2) assert.equal(offset2, msg1.length + 2)
const b1 = await p(log._get)(offset1) const b1 = await p(log._get)(offset1)
assert.equal(b1.toString(), msg1.toString()) assert.equal(b1.toString(), msg1.toString())
const b2 = await p(log._get)(offset2) const b2 = await p(log._get)(offset2)
assert.equal(b2.toString(), msg2.toString()) assert.equal(b2.toString(), msg2.toString())
await p(log.close)() await p(log.close)()
})
const json1 = { text: 'testing' }
const json2 = { test: 'testing2' }
test('Log handles basic json records', async function (t) {
const file = '/tmp/ppppp-db-log-test-basic-json.log'
try {
fs.unlinkSync(file)
} catch (_) {}
const log = Log(file, {
blockSize: 2 * 1024,
codec: require('flumecodec/json'),
}) })
const offset1 = await p(log.append)(json1) const json1 = { text: 'testing' }
assert.equal(offset1, 0) const json2 = { test: 'testing2' }
const offset2 = await p(log.append)(json2) await t.test('Log handles basic json records', async function (t) {
assert.equal(offset2, 20) const file = '/tmp/ppppp-db-log-test-basic-json.log'
try {
fs.unlinkSync(file)
} catch (_) {}
const log = Log(file, {
blockSize: 2 * 1024,
codec: require('flumecodec/json'),
})
const rec1 = await p(log._get)(offset1) const offset1 = await p(log.append)(json1)
assert.deepEqual(rec1, json1) assert.equal(offset1, 0)
const rec2 = await p(log._get)(offset2) const offset2 = await p(log.append)(json2)
assert.deepEqual(rec2, json2) assert.equal(offset2, 20)
await p(log.close)() const rec1 = await p(log._get)(offset1)
}) assert.deepEqual(rec1, json1)
test('Log handles basic json record re-reading', async function (t) { const rec2 = await p(log._get)(offset2)
const file = '/tmp/ppppp-db-log-test-basic-json.log' assert.deepEqual(rec2, json2)
const log = Log(file, {
blockSize: 2 * 1024, await p(log.close)()
codec: require('flumecodec/json'),
}) })
await p(log.onDrain)() await t.test('Log handles basic json record re-reading', async function (t) {
assert.equal(log.since.value, 20) const file = '/tmp/ppppp-db-log-test-basic-json.log'
const log = Log(file, {
blockSize: 2 * 1024,
codec: require('flumecodec/json'),
})
const rec1 = await p(log._get)(0) await p(log.onDrain)()
assert.deepEqual(rec1, json1) assert.equal(log.since.value, 20)
const rec2 = await p(log._get)(20) const rec1 = await p(log._get)(0)
assert.deepEqual(rec2, json2) assert.deepEqual(rec1, json1)
await p(log.close)() const rec2 = await p(log._get)(20)
assert.deepEqual(rec2, json2)
await p(log.close)()
})
}) })

View File

@ -14,176 +14,178 @@ const msg3 = Buffer.from(
'hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db' 'hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db hello offsetty db'
) )
test('Log performing simple delete', async (t) => { test('Log deletes', async (t) => {
const file = '/tmp/ppppp-db-log-test-del.log' await t.test('Simple delete', async (t) => {
try { const file = '/tmp/ppppp-db-log-test-del.log'
fs.unlinkSync(file) try {
} catch (_) {} fs.unlinkSync(file)
const log = Log(file, { blockSize: 2 * 1024 }) } catch (_) {}
const log = Log(file, { blockSize: 2 * 1024 })
const offset1 = await p(log.append)(msg1) const offset1 = await p(log.append)(msg1)
assert.equal(offset1, 0) assert.equal(offset1, 0)
const offset2 = await p(log.append)(msg2) const offset2 = await p(log.append)(msg2)
assert.ok(offset2 > offset1) assert.ok(offset2 > offset1)
const offset3 = await p(log.append)(msg3) const offset3 = await p(log.append)(msg3)
assert.ok(offset3 > offset2) assert.ok(offset3 > offset2)
const buf1 = await p(log._get)(offset1) const buf1 = await p(log._get)(offset1)
assert.equal(buf1.toString(), msg1.toString()) assert.equal(buf1.toString(), msg1.toString())
const buf2 = await p(log._get)(offset2) const buf2 = await p(log._get)(offset2)
assert.equal(buf2.toString(), msg2.toString()) assert.equal(buf2.toString(), msg2.toString())
const buf3 = await p(log._get)(offset3) const buf3 = await p(log._get)(offset3)
assert.equal(buf3.toString(), msg3.toString()) assert.equal(buf3.toString(), msg3.toString())
await p(log.del)(offset2) await p(log.del)(offset2)
await p(log.onDeletesFlushed)() await p(log.onDeletesFlushed)()
await assert.rejects(p(log._get)(offset2), (err) => { await assert.rejects(p(log._get)(offset2), (err) => {
assert.ok(err) assert.ok(err)
assert.equal(err.message, 'Record has been deleted') assert.equal(err.message, 'Record has been deleted')
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD') assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
return true return true
})
await p(log.close)()
}) })
await p(log.close)() await t.test('Deleted records are not invalid upon re-opening', async (t) => {
}) const file = '/tmp/ppppp-db-log-test-del-invalid.log'
try {
fs.unlinkSync(file)
} catch (_) {}
test('Log deleted records are not invalid upon re-opening', async (t) => { const opts = {
const file = '/tmp/ppppp-db-log-test-del-invalid.log' blockSize: 2 * 1024,
try { codec: {
fs.unlinkSync(file) encode(msg) {
} catch (_) {} return Buffer.from(JSON.stringify(msg), 'utf8')
},
const opts = { decode(buf) {
blockSize: 2 * 1024, return JSON.parse(buf.toString('utf8'))
codec: { },
encode(msg) {
return Buffer.from(JSON.stringify(msg), 'utf8')
}, },
decode(buf) { validateRecord(buf) {
return JSON.parse(buf.toString('utf8')) try {
}, JSON.parse(buf.toString('utf8'))
}, return true
validateRecord(buf) { } catch {
try { return false
JSON.parse(buf.toString('utf8'))
return true
} catch {
return false
}
},
}
const log = Log(file, opts)
const offset1 = await p(log.append)({ text: 'm0' })
const offset2 = await p(log.append)({ text: 'm1' })
const offset3 = await p(log.append)({ text: 'm2' })
await p(log.del)(offset2)
await p(log.onDeletesFlushed)()
await p(log.close)()
const log2 = Log(file, opts)
let arr = []
await new Promise((resolve) => {
log2.scan(
(offset, value, size) => {
arr.push(value)
},
(err) => {
assert.ifError(err)
assert.deepEqual(arr, [{ text: 'm0' }, null, { text: 'm2' }])
resolve()
}
)
})
await assert.rejects(p(log2._get)(offset2), (err) => {
assert.ok(err)
assert.equal(err.message, 'Record has been deleted')
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
return true
})
await p(log2.close)()
})
test('Log deletes are handled by scan()', async (t) => {
const file = '/tmp/offset-test_' + Date.now() + '.log'
const log = Log(file, { blockSize: 64 * 1024 })
const buf1 = Buffer.from('hello one')
const buf2 = Buffer.from('hello two')
const offset1 = await p(log.append)(buf1)
const offset2 = await p(log.append)(buf2)
await p(log.del)(offset1)
await p(log.onDrain)()
await p(log.onDeletesFlushed)()
const arr = []
await new Promise((resolve) => {
log.scan(
(offset, rec, length) => {
arr.push(rec)
},
(err) => {
assert.ifError(err)
resolve()
}
)
})
assert.deepEqual(arr, [null, buf2])
await p(log.close)()
})
test('Log can handle many deleted records', { timeout: 60e3 }, async (t) => {
const file = '/tmp/aaol-test-delete-many' + Date.now() + '.log'
const log = Log(file, { blockSize: 64 * 1024 })
const TOTAL = 100000
const offsets = []
const logAppend = p(log.append)
if (process.env.VERBOSE) console.time('append ' + TOTAL)
for (let i = 0; i < TOTAL; i += 1) {
const offset = await logAppend(Buffer.from(`hello ${i}`))
offsets.push(offset)
}
assert('appended records')
if (process.env.VERBOSE) console.timeEnd('append ' + TOTAL)
await p(log.onDrain)()
const logDel = p(log.del)
if (process.env.VERBOSE) console.time('delete ' + TOTAL / 2)
for (let i = 0; i < TOTAL; i += 2) {
await logDel(offsets[i])
}
if (process.env.VERBOSE) console.timeEnd('delete ' + TOTAL / 2)
assert('deleted messages')
await p(log.onDeletesFlushed)()
await new Promise((resolve) => {
let i = 0
log.scan(
(offset, rec, length) => {
if (i % 2 === 0) {
if (rec !== null) assert.fail('record ' + i + ' should be deleted')
} else {
if (rec === null) assert.fail('record ' + i + ' should be present')
} }
i += 1
}, },
(err) => { }
assert.ifError(err) const log = Log(file, opts)
resolve()
} const offset1 = await p(log.append)({ text: 'm0' })
) const offset2 = await p(log.append)({ text: 'm1' })
const offset3 = await p(log.append)({ text: 'm2' })
await p(log.del)(offset2)
await p(log.onDeletesFlushed)()
await p(log.close)()
const log2 = Log(file, opts)
let arr = []
await new Promise((resolve) => {
log2.scan(
(offset, value, size) => {
arr.push(value)
},
(err) => {
assert.ifError(err)
assert.deepEqual(arr, [{ text: 'm0' }, null, { text: 'm2' }])
resolve()
}
)
})
await assert.rejects(p(log2._get)(offset2), (err) => {
assert.ok(err)
assert.equal(err.message, 'Record has been deleted')
assert.equal(err.code, 'ERR_AAOL_DELETED_RECORD')
return true
})
await p(log2.close)()
}) })
await p(log.close)() await t.test('Deletes are noticed by scan()', async (t) => {
const file = '/tmp/offset-test_' + Date.now() + '.log'
const log = Log(file, { blockSize: 64 * 1024 })
const buf1 = Buffer.from('hello one')
const buf2 = Buffer.from('hello two')
const offset1 = await p(log.append)(buf1)
const offset2 = await p(log.append)(buf2)
await p(log.del)(offset1)
await p(log.onDrain)()
await p(log.onDeletesFlushed)()
const arr = []
await new Promise((resolve) => {
log.scan(
(offset, rec, length) => {
arr.push(rec)
},
(err) => {
assert.ifError(err)
resolve()
}
)
})
assert.deepEqual(arr, [null, buf2])
await p(log.close)()
})
await t.test('Many deleted records', { timeout: 60e3 }, async (t) => {
const file = '/tmp/aaol-test-delete-many' + Date.now() + '.log'
const log = Log(file, { blockSize: 64 * 1024 })
const TOTAL = 100000
const offsets = []
const logAppend = p(log.append)
if (process.env.VERBOSE) console.time('append ' + TOTAL)
for (let i = 0; i < TOTAL; i += 1) {
const offset = await logAppend(Buffer.from(`hello ${i}`))
offsets.push(offset)
}
assert('appended records')
if (process.env.VERBOSE) console.timeEnd('append ' + TOTAL)
await p(log.onDrain)()
const logDel = p(log.del)
if (process.env.VERBOSE) console.time('delete ' + TOTAL / 2)
for (let i = 0; i < TOTAL; i += 2) {
await logDel(offsets[i])
}
if (process.env.VERBOSE) console.timeEnd('delete ' + TOTAL / 2)
assert('deleted messages')
await p(log.onDeletesFlushed)()
await new Promise((resolve) => {
let i = 0
log.scan(
(offset, rec, length) => {
if (i % 2 === 0) {
if (rec !== null) assert.fail('record ' + i + ' should be deleted')
} else {
if (rec === null) assert.fail('record ' + i + ' should be present')
}
i += 1
},
(err) => {
assert.ifError(err)
resolve()
}
)
})
await p(log.close)()
})
}) })

View File

@ -9,58 +9,60 @@ var file = '/tmp/ds-test_restart.log'
var msg1 = { text: 'hello world hello world' } var msg1 = { text: 'hello world hello world' }
var msg2 = { text: 'hello world hello world 2' } var msg2 = { text: 'hello world hello world 2' }
test('Log (fix buggy write) simple', async (t) => { test('Log fix buggy write', async (t) => {
try { await t.test('Simple', async (t) => {
fs.unlinkSync(file) try {
} catch (_) {} fs.unlinkSync(file)
const log = Log(file, { } catch (_) {}
block: 16 * 1024, const log = Log(file, {
codec: require('flumecodec/json'), block: 16 * 1024,
codec: require('flumecodec/json'),
})
const offset1 = await p(log.append)(msg1)
assert.equal(offset1, 0)
const offset2 = await p(log.append)(msg2)
assert.equal(offset2, 36)
await p(log.onDrain)()
let arr = []
await new Promise((resolve) => {
log.scan(
(offset, msg, size) => {
arr.push(msg)
},
(err) => {
assert.ifError(err)
resolve()
}
)
})
assert.deepEqual(arr, [msg1, msg2])
await p(log.close)()
}) })
const offset1 = await p(log.append)(msg1) await t.test('Re-read', async (t) => {
assert.equal(offset1, 0) const log = Log(file, {
const offset2 = await p(log.append)(msg2) block: 16 * 1024,
assert.equal(offset2, 36) codec: require('flumecodec/json'),
})
await p(log.onDrain)() await p(log.onDrain)()
let arr = [] let arr = []
await new Promise((resolve) => { await new Promise((resolve) => {
log.scan( log.scan(
(offset, msg, size) => { (offset, msg, size) => {
arr.push(msg) arr.push(msg)
}, },
(err) => { (err) => {
assert.ifError(err) assert.ifError(err)
resolve() resolve()
} }
) )
})
assert.deepEqual(arr, [msg1, msg2])
await p(log.close)()
}) })
assert.deepEqual(arr, [msg1, msg2])
await p(log.close)()
})
test('Log (fix buggy write) reread', async (t) => {
const log = Log(file, {
block: 16 * 1024,
codec: require('flumecodec/json'),
})
await p(log.onDrain)()
let arr = []
await new Promise((resolve) => {
log.scan(
(offset, msg, size) => {
arr.push(msg)
},
(err) => {
assert.ifError(err)
resolve()
}
)
})
assert.deepEqual(arr, [msg1, msg2])
await p(log.close)()
}) })

View File

@ -3,82 +3,84 @@ const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV3 = require('../../lib/msg-v3')
test('MsgV3 invalid domain not a string', (t) => { test('MsgV3 domain validation', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') await t.test('Not a string', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 123, domain: 123,
}) })
}, },
/invalid domain/, /invalid domain/,
'not a string' 'not a string'
) )
}) })
test('MsgV3 invalid domain with "/" character', (t) => { await t.test('"/" character', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'group/init', domain: 'group/init',
}) })
}, },
/invalid domain/, /invalid domain/,
'invalid domain if contains /' 'invalid domain if contains /'
) )
}) })
test('MsgV3 invalid domain with "*" character', (t) => { await t.test('"*" character', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'star*', domain: 'star*',
}) })
}, },
/invalid domain/, /invalid domain/,
'invalid domain if contains *' 'invalid domain if contains *'
) )
}) })
test('MsgV3 invalid domain too short', (t) => { await t.test('Too short', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'xy', domain: 'xy',
}) })
}, },
/shorter than 3/, /shorter than 3/,
'invalid domain if too short' 'invalid domain if too short'
) )
}) })
test('MsgV3 invalid domain too long', (t) => { await t.test('too long', (t) => {
const keypair = Keypair.generate('ed25519', 'alice') const keypair = Keypair.generate('ed25519', 'alice')
assert.throws( assert.throws(
() => { () => {
MsgV3.create({ MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
domain: 'a'.repeat(120), domain: 'a'.repeat(120),
}) })
}, },
/100\+ characters long/, /100\+ characters long/,
'invalid domain if too long' 'invalid domain if too long'
) )
})
}) })

View File

@ -10,338 +10,340 @@ const account = MsgV3.getMsgID(
) )
const pubkeys = new Set([keypair.public]) const pubkeys = new Set([keypair.public])
test('MsgV3 invalid msg with non-array prev', (t) => { test('MsgV3 tangles prev validation', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') await t.test('Non-array is a bad prev', (t) => {
const keypair = Keypair.generate('ed25519', 'alice')
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV3.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV3.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV3.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const msg = MsgV3.create({ const msg = MsgV3.create({
keypair, keypair,
data: { text: 'Hello world!' }, data: { text: 'Hello world!' },
account, account,
accountTips: [account], accountTips: [account],
domain: 'post', domain: 'post',
tangles: { tangles: {
[mootID]: tangle, [mootID]: tangle,
}, },
})
msg.metadata.tangles[mootID].prev = null
const msgID = MsgV3.getMsgID(msg)
const err = MsgV3.validate(msg, tangle, pubkeys, msgID, mootID)
assert.ok(err, 'invalid 2nd msg throws')
assert.match(
err,
/prev ".*" should have been an array/,
'invalid 2nd msg description'
)
}) })
msg.metadata.tangles[mootID].prev = null
const msgID = MsgV3.getMsgID(msg)
const err = MsgV3.validate(msg, tangle, pubkeys, msgID, mootID) await t.test('Number not allowed in prev', (t) => {
assert.ok(err, 'invalid 2nd msg throws') const keypair = Keypair.generate('ed25519', 'alice')
assert.match(
err, const moot = MsgV3.createMoot(account, 'post', keypair)
/prev ".*" should have been an array/, const mootID = MsgV3.getMsgID(moot)
'invalid 2nd msg description'
) const tangle = new MsgV3.Tangle(mootID)
}) tangle.add(mootID, moot)
test('MsgV3 invalid msg with bad prev', (t) => { const msg1 = MsgV3.create({
const keypair = Keypair.generate('ed25519', 'alice') keypair,
data: { text: 'Hello world!' },
const moot = MsgV3.createMoot(account, 'post', keypair) account,
const mootID = MsgV3.getMsgID(moot) accountTips: [account],
domain: 'post',
const tangle = new MsgV3.Tangle(mootID) tangles: {
tangle.add(mootID, moot) [mootID]: tangle,
},
const msg1 = MsgV3.create({ })
keypair, const msgID1 = MsgV3.getMsgID(msg1)
data: { text: 'Hello world!' }, tangle.add(msgID1, msg1)
account,
accountTips: [account], const msg2 = MsgV3.create({
domain: 'post', keypair,
tangles: { data: { text: 'Hello world!' },
[mootID]: tangle, account,
}, accountTips: [account],
}) domain: 'post',
const msgID1 = MsgV3.getMsgID(msg1) tangles: {
tangle.add(msgID1, msg1) [mootID]: tangle,
},
const msg2 = MsgV3.create({ })
keypair, msg2.metadata.tangles[mootID].depth = 1
data: { text: 'Hello world!' }, msg2.metadata.tangles[mootID].prev = [1234]
account, const msgID2 = MsgV3.getMsgID(msg2)
accountTips: [account],
domain: 'post', const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID)
tangles: { assert.ok(err, 'invalid 2nd msg throws')
[mootID]: tangle, assert.match(
}, err,
}) /prev item ".*" should have been a string/,
msg2.metadata.tangles[mootID].depth = 1 'invalid 2nd msg description'
msg2.metadata.tangles[mootID].prev = [1234] )
const msgID2 = MsgV3.getMsgID(msg2) })
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) await t.test('URI not allowed in prev', (t) => {
assert.ok(err, 'invalid 2nd msg throws') const keypair = Keypair.generate('ed25519', 'alice')
assert.match(
err, const moot = MsgV3.createMoot(account, 'post', keypair)
/prev item ".*" should have been a string/, const mootID = MsgV3.getMsgID(moot)
'invalid 2nd msg description'
) const tangle = new MsgV3.Tangle(mootID)
}) tangle.add(mootID, moot)
test('MsgV3 invalid msg with URI in prev', (t) => { const msg1 = MsgV3.create({
const keypair = Keypair.generate('ed25519', 'alice') keypair,
data: { text: 'Hello world!' },
const moot = MsgV3.createMoot(account, 'post', keypair) account,
const mootID = MsgV3.getMsgID(moot) accountTips: [account],
domain: 'post',
const tangle = new MsgV3.Tangle(mootID) tangles: {
tangle.add(mootID, moot) [mootID]: tangle,
},
const msg1 = MsgV3.create({ })
keypair, const msgID1 = MsgV3.getMsgID(msg1)
data: { text: 'Hello world!' }, tangle.add(msgID1, msg1)
account,
accountTips: [account], const msg2 = MsgV3.create({
domain: 'post', keypair,
tangles: { data: { text: 'Hello world!' },
[mootID]: tangle, account,
}, accountTips: [account],
}) domain: 'post',
const msgID1 = MsgV3.getMsgID(msg1) tangles: {
tangle.add(msgID1, msg1) [mootID]: tangle,
},
const msg2 = MsgV3.create({ })
keypair, const msgID2 = MsgV3.getMsgID(msg2)
data: { text: 'Hello world!' }, const randBuf = Buffer.alloc(16).fill(16)
account, const fakeMsgKey1 = `ppppp:message/v3/${base58.encode(randBuf)}`
accountTips: [account], msg2.metadata.tangles[mootID].depth = 1
domain: 'post', msg2.metadata.tangles[mootID].prev = [fakeMsgKey1]
tangles: {
[mootID]: tangle, const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID)
}, assert.ok(err, 'invalid 2nd msg throws')
}) assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description')
const msgID2 = MsgV3.getMsgID(msg2) })
const randBuf = Buffer.alloc(16).fill(16)
const fakeMsgKey1 = `ppppp:message/v3/${base58.encode(randBuf)}` await t.test('Locally unknown prev msgID', (t) => {
msg2.metadata.tangles[mootID].depth = 1 const keypair = Keypair.generate('ed25519', 'alice')
msg2.metadata.tangles[mootID].prev = [fakeMsgKey1]
const moot = MsgV3.createMoot(account, 'post', keypair)
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) const mootID = MsgV3.getMsgID(moot)
assert.ok(err, 'invalid 2nd msg throws')
assert.match(err, /prev item ".*" is a URI/, 'invalid 2nd msg description') const tangle = new MsgV3.Tangle(mootID)
}) tangle.add(mootID, moot)
test('MsgV3 invalid msg with unknown prev', (t) => { const msg1 = MsgV3.create({
const keypair = Keypair.generate('ed25519', 'alice') keypair,
data: { text: 'Hello world!' },
const moot = MsgV3.createMoot(account, 'post', keypair) account,
const mootID = MsgV3.getMsgID(moot) accountTips: [account],
domain: 'post',
const tangle = new MsgV3.Tangle(mootID) tangles: {
tangle.add(mootID, moot) [mootID]: tangle,
},
const msg1 = MsgV3.create({ })
keypair, const msgID1 = MsgV3.getMsgID(msg1)
data: { text: 'Hello world!' }, tangle.add(msgID1, msg1)
account,
accountTips: [account], const unknownMsg = MsgV3.create({
domain: 'post', keypair,
tangles: { data: { text: 'Alien' },
[mootID]: tangle, account,
}, accountTips: [account],
}) domain: 'post',
const msgID1 = MsgV3.getMsgID(msg1) tangles: {
tangle.add(msgID1, msg1) [mootID]: tangle,
},
const unknownMsg = MsgV3.create({ })
keypair, const unknownMsgID = MsgV3.getMsgID(unknownMsg)
data: { text: 'Alien' },
account, const fakeMootID = 'ABCDEabcde' + mootID.substring(10)
accountTips: [account], const tangle2 = new MsgV3.Tangle(fakeMootID)
domain: 'post', tangle2.add(fakeMootID, moot)
tangles: { tangle2.add(unknownMsgID, unknownMsg)
[mootID]: tangle,
}, const msg2 = MsgV3.create({
}) keypair,
const unknownMsgID = MsgV3.getMsgID(unknownMsg) data: { text: 'Hello world!' },
account,
const fakeMootID = 'ABCDEabcde' + mootID.substring(10) accountTips: [account],
const tangle2 = new MsgV3.Tangle(fakeMootID) domain: 'post',
tangle2.add(fakeMootID, moot) tangles: {
tangle2.add(unknownMsgID, unknownMsg) [mootID]: tangle2,
},
const msg2 = MsgV3.create({ })
keypair, const msgID2 = MsgV3.getMsgID(msg2)
data: { text: 'Hello world!' },
account, const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID)
accountTips: [account], assert.ok(err, 'invalid 2nd msg throws')
domain: 'post', assert.match(
tangles: { err,
[mootID]: tangle2, /all prev are locally unknown/,
}, 'invalid 2nd msg description'
}) )
const msgID2 = MsgV3.getMsgID(msg2) })
const err = MsgV3.validate(msg2, tangle, pubkeys, msgID2, mootID) await t.test('Feed msg with the wrong pubkey', (t) => {
assert.ok(err, 'invalid 2nd msg throws') const keypairA = Keypair.generate('ed25519', 'alice')
assert.match( const keypairB = Keypair.generate('ed25519', 'bob')
err,
/all prev are locally unknown/, const accountB = MsgV3.getMsgID(
'invalid 2nd msg description' MsgV3.createAccount(keypairB, 'person', 'MYNONCE')
) )
})
const moot = MsgV3.createMoot(account, 'post', keypair)
test('MsgV3 invalid feed msg with a different pubkey', (t) => { const mootID = MsgV3.getMsgID(moot)
const keypairA = Keypair.generate('ed25519', 'alice') const feedTangle = new MsgV3.Tangle(mootID)
const keypairB = Keypair.generate('ed25519', 'bob') feedTangle.add(mootID, moot)
const accountB = MsgV3.getMsgID( const msg = MsgV3.create({
MsgV3.createAccount(keypairB, 'person', 'MYNONCE') keypair: keypairB,
) data: { text: 'Hello world!' },
account: accountB,
const moot = MsgV3.createMoot(account, 'post', keypair) accountTips: [accountB],
const mootID = MsgV3.getMsgID(moot) domain: 'post',
const feedTangle = new MsgV3.Tangle(mootID) tangles: {
feedTangle.add(mootID, moot) [mootID]: feedTangle,
},
const msg = MsgV3.create({ })
keypair: keypairB, const msgID = MsgV3.getMsgID(msg)
data: { text: 'Hello world!' },
account: accountB, const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID)
accountTips: [accountB], assert.ok(err, 'invalid msg throws')
domain: 'post', assert.match(
tangles: { err,
[mootID]: feedTangle, /pubkey ".*" should have been one of ".*" from the account ".*"/,
}, 'invalid msg'
}) )
const msgID = MsgV3.getMsgID(msg) })
const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID) await t.test('Feed msg with the wrong domain', (t) => {
assert.ok(err, 'invalid msg throws') const keypairA = Keypair.generate('ed25519', 'alice')
assert.match(
err, const moot = MsgV3.createMoot(account, 'post', keypair)
/pubkey ".*" should have been one of ".*" from the account ".*"/, const mootID = MsgV3.getMsgID(moot)
'invalid msg' const feedTangle = new MsgV3.Tangle(mootID)
) feedTangle.add(mootID, moot)
})
const msg = MsgV3.create({
test('MsgV3 invalid feed msg with a different domain', (t) => { keypair: keypairA,
const keypairA = Keypair.generate('ed25519', 'alice') data: { text: 'Hello world!' },
account,
const moot = MsgV3.createMoot(account, 'post', keypair) accountTips: [account],
const mootID = MsgV3.getMsgID(moot) domain: 'comment',
const feedTangle = new MsgV3.Tangle(mootID) tangles: {
feedTangle.add(mootID, moot) [mootID]: feedTangle,
},
const msg = MsgV3.create({ })
keypair: keypairA, const msgID = MsgV3.getMsgID(msg)
data: { text: 'Hello world!' },
account, const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID)
accountTips: [account], assert.ok(err, 'invalid msg throws')
domain: 'comment', assert.match(
tangles: { err,
[mootID]: feedTangle, /domain "comment" should have been feed domain "post"/,
}, 'invalid feed msg'
}) )
const msgID = MsgV3.getMsgID(msg) })
const err = MsgV3.validate(msg, feedTangle, pubkeys, msgID, mootID) await t.test('Feed msg with non-alphabetically sorted prev', (t) => {
assert.ok(err, 'invalid msg throws') const keypair = Keypair.generate('ed25519', 'alice')
assert.match(
err, const moot = MsgV3.createMoot(account, 'post', keypair)
/domain "comment" should have been feed domain "post"/, const mootID = MsgV3.getMsgID(moot)
'invalid feed msg'
) const tangle = new MsgV3.Tangle(mootID)
}) tangle.add(mootID, moot)
test('MsgV3 invalid feed msg with non-alphabetical prev', (t) => { const msg1 = MsgV3.create({
const keypair = Keypair.generate('ed25519', 'alice') keypair,
data: { text: '1' },
const moot = MsgV3.createMoot(account, 'post', keypair) account,
const mootID = MsgV3.getMsgID(moot) accountTips: [account],
domain: 'post',
const tangle = new MsgV3.Tangle(mootID) tangles: {
tangle.add(mootID, moot) [mootID]: tangle,
},
const msg1 = MsgV3.create({ })
keypair, const msgID1 = MsgV3.getMsgID(msg1)
data: { text: '1' },
account, const msg2 = MsgV3.create({
accountTips: [account], keypair,
domain: 'post', data: { text: '2' },
tangles: { account,
[mootID]: tangle, accountTips: [account],
}, domain: 'post',
}) tangles: {
const msgID1 = MsgV3.getMsgID(msg1) [mootID]: tangle,
},
const msg2 = MsgV3.create({ })
keypair, const msgID2 = MsgV3.getMsgID(msg2)
data: { text: '2' },
account, tangle.add(msgID1, msg1)
accountTips: [account], tangle.add(msgID2, msg2)
domain: 'post',
tangles: { const msg3 = MsgV3.create({
[mootID]: tangle, keypair,
}, data: { text: '3' },
}) account,
const msgID2 = MsgV3.getMsgID(msg2) accountTips: [account],
domain: 'post',
tangle.add(msgID1, msg1) tangles: {
tangle.add(msgID2, msg2) [mootID]: tangle,
},
const msg3 = MsgV3.create({ })
keypair, const msgID3 = MsgV3.getMsgID(msg3)
data: { text: '3' },
account, let prevMsgIDs = msg3.metadata.tangles[mootID].prev
accountTips: [account], if (prevMsgIDs[0] < prevMsgIDs[1]) {
domain: 'post', prevMsgIDs = [prevMsgIDs[1], prevMsgIDs[0]]
tangles: { } else {
[mootID]: tangle, prevMsgIDs = [prevMsgIDs[0], prevMsgIDs[1]]
}, }
}) msg3.metadata.tangles[mootID].prev = prevMsgIDs
const msgID3 = MsgV3.getMsgID(msg3)
const err = MsgV3.validate(msg3, tangle, pubkeys, msgID3, mootID)
let prevMsgIDs = msg3.metadata.tangles[mootID].prev assert.ok(err, 'invalid 3rd msg throws')
if (prevMsgIDs[0] < prevMsgIDs[1]) { assert.match(
prevMsgIDs = [prevMsgIDs[1], prevMsgIDs[0]] err,
} else { /prev ".*" should have been alphabetically sorted/,
prevMsgIDs = [prevMsgIDs[0], prevMsgIDs[1]] 'invalid error message'
} )
msg3.metadata.tangles[mootID].prev = prevMsgIDs })
const err = MsgV3.validate(msg3, tangle, pubkeys, msgID3, mootID) await t.test('Feed msg with duplicate prev', (t) => {
assert.ok(err, 'invalid 3rd msg throws') const keypair = Keypair.generate('ed25519', 'alice')
assert.match(
err, const moot = MsgV3.createMoot(account, 'post', keypair)
/prev ".*" should have been alphabetically sorted/, const mootID = MsgV3.getMsgID(moot)
'invalid error message'
) const tangle = new MsgV3.Tangle(mootID)
}) tangle.add(mootID, moot)
test('MsgV3 invalid feed msg with duplicate prev', (t) => { const msg1 = MsgV3.create({
const keypair = Keypair.generate('ed25519', 'alice') keypair,
data: { text: '1' },
const moot = MsgV3.createMoot(account, 'post', keypair) account,
const mootID = MsgV3.getMsgID(moot) accountTips: [account],
domain: 'post',
const tangle = new MsgV3.Tangle(mootID) tangles: {
tangle.add(mootID, moot) [mootID]: tangle,
},
const msg1 = MsgV3.create({ })
keypair, const msgID1 = MsgV3.getMsgID(msg1)
data: { text: '1' },
account, const [prevID] = msg1.metadata.tangles[mootID].prev
accountTips: [account], msg1.metadata.tangles[mootID].prev = [prevID, prevID]
domain: 'post',
tangles: { const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID)
[mootID]: tangle, assert.ok(err, 'invalid 1st msg throws')
}, assert.match(err, /prev ".*" contains duplicates/, 'invalid error message')
}) })
const msgID1 = MsgV3.getMsgID(msg1)
const [prevID] = msg1.metadata.tangles[mootID].prev
msg1.metadata.tangles[mootID].prev = [prevID, prevID]
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID)
assert.ok(err, 'invalid 1st msg throws')
assert.match(err, /prev ".*" contains duplicates/, 'invalid error message')
}) })

View File

@ -3,165 +3,161 @@ const assert = require('node:assert')
const Keypair = require('ppppp-keypair') const Keypair = require('ppppp-keypair')
const MsgV3 = require('../../lib/msg-v3') const MsgV3 = require('../../lib/msg-v3')
test('MsgV3 validate root msg', (t) => { test('MsgV3 validation', async (t) => {
const keypair = Keypair.generate('ed25519', 'alice') await t.test('Correct root msg', (t) => {
const account = MsgV3.getMsgID( const keypair = Keypair.generate('ed25519', 'alice')
MsgV3.createAccount(keypair, 'person', 'alice') const account = MsgV3.getMsgID(
) MsgV3.createAccount(keypair, 'person', 'alice')
const pubkeys = new Set([keypair.public]) )
const pubkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const moot = MsgV3.createMoot(account, 'post', keypair)
const mootID = MsgV3.getMsgID(moot) const mootID = MsgV3.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID) const tangle = new MsgV3.Tangle(mootID)
tangle.add(mootID, moot) tangle.add(mootID, moot)
const err = MsgV3.validate(moot, tangle, pubkeys, mootID, mootID) const err = MsgV3.validate(moot, tangle, pubkeys, mootID, mootID)
assert.ifError(err, 'valid root msg') assert.ifError(err, 'valid root msg')
})
test('MsgV3 validate account tangle', (t) => {
const pubkeys = new Set()
const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys.add(keypair1.public)
const accountMsg0 = MsgV3.createAccount(keypair1, 'person', 'alice')
const account = MsgV3.getMsgID(accountMsg0)
const accountMsg0ID = account
const tangle = new MsgV3.Tangle(account)
tangle.add(accountMsg0ID, accountMsg0)
let err = MsgV3.validate(
accountMsg0,
tangle,
pubkeys,
accountMsg0ID,
account
)
assert.ifError(err, 'valid account root msg')
tangle.add(account, accountMsg0)
const keypair2 = Keypair.generate('ed25519', 'bob')
const accountMsg1 = MsgV3.create({
account: 'self',
accountTips: null,
domain: 'account',
data: { add: keypair2.public },
tangles: {
[account]: tangle,
},
keypair: keypair1, // announcing keypair2 but signing with keypair1
}) })
const accountMsg1ID = MsgV3.getMsgID(accountMsg1)
err = MsgV3.validate( await t.test('Correct account tangle', (t) => {
accountMsg1, const pubkeys = new Set()
tangle, const keypair1 = Keypair.generate('ed25519', 'alice')
pubkeys, pubkeys.add(keypair1.public)
accountMsg1ID,
account
)
assert.ifError(err, 'valid account msg')
})
test('MsgV3 validate 2nd msg with existing root', (t) => { const accountMsg0 = MsgV3.createAccount(keypair1, 'person', 'alice')
const keypair = Keypair.generate('ed25519', 'alice') const account = MsgV3.getMsgID(accountMsg0)
const account = MsgV3.getMsgID( const accountMsg0ID = account
MsgV3.createAccount(keypair, 'person', 'alice')
)
const pubkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const tangle = new MsgV3.Tangle(account)
const mootID = MsgV3.getMsgID(moot) tangle.add(accountMsg0ID, accountMsg0)
const tangle = new MsgV3.Tangle(mootID)
tangle.add(mootID, moot)
const msg1 = MsgV3.create({ let err = MsgV3.validate(
account, accountMsg0,
accountTips: [account], tangle,
domain: 'post', pubkeys,
data: { text: 'Hello world!' }, accountMsg0ID,
tangles: { account
[mootID]: tangle, )
}, assert.ifError(err, 'valid account root msg')
keypair,
tangle.add(account, accountMsg0)
const keypair2 = Keypair.generate('ed25519', 'bob')
const accountMsg1 = MsgV3.create({
account: 'self',
accountTips: null,
domain: 'account',
data: { add: keypair2.public },
tangles: {
[account]: tangle,
},
keypair: keypair1, // announcing keypair2 but signing with keypair1
})
const accountMsg1ID = MsgV3.getMsgID(accountMsg1)
err = MsgV3.validate(accountMsg1, tangle, pubkeys, accountMsg1ID, account)
assert.ifError(err, 'valid account msg')
}) })
const msgID1 = MsgV3.getMsgID(msg1)
tangle.add(msgID1, msg1)
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID) await t.test('2nd msg correct with existing root', (t) => {
assert.ifError(err, 'valid 2nd msg') const keypair = Keypair.generate('ed25519', 'alice')
}) const account = MsgV3.getMsgID(
MsgV3.createAccount(keypair, 'person', 'alice')
)
const pubkeys = new Set([keypair.public])
test('MsgV3 validate 2nd forked msg', (t) => { const moot = MsgV3.createMoot(account, 'post', keypair)
const keypair = Keypair.generate('ed25519', 'alice') const mootID = MsgV3.getMsgID(moot)
const account = MsgV3.getMsgID( const tangle = new MsgV3.Tangle(mootID)
MsgV3.createAccount(keypair, 'person', 'alice') tangle.add(mootID, moot)
)
const pubkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const msg1 = MsgV3.create({
const mootID = MsgV3.getMsgID(moot) account,
const tangle = new MsgV3.Tangle(mootID) accountTips: [account],
tangle.add(mootID, moot) domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
})
const msgID1 = MsgV3.getMsgID(msg1)
tangle.add(msgID1, msg1)
const msg1A = MsgV3.create({ const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID)
account, assert.ifError(err, 'valid 2nd msg')
accountTips: [account],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
}) })
const msgID1A = MsgV3.getMsgID(msg1A)
const msg1B = MsgV3.create({ await t.test('2nd forked msg correct', (t) => {
account, const keypair = Keypair.generate('ed25519', 'alice')
accountTips: [account], const account = MsgV3.getMsgID(
domain: 'post', MsgV3.createAccount(keypair, 'person', 'alice')
data: { text: 'Hello world!' }, )
tangles: { const pubkeys = new Set([keypair.public])
[mootID]: tangle,
}, const moot = MsgV3.createMoot(account, 'post', keypair)
keypair, const mootID = MsgV3.getMsgID(moot)
const tangle = new MsgV3.Tangle(mootID)
tangle.add(mootID, moot)
const msg1A = MsgV3.create({
account,
accountTips: [account],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
})
const msgID1A = MsgV3.getMsgID(msg1A)
const msg1B = MsgV3.create({
account,
accountTips: [account],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
})
const msgID1B = MsgV3.getMsgID(msg1B)
tangle.add(msgID1A, msg1A)
tangle.add(msgID1B, msg1B)
const err = MsgV3.validate(msg1B, tangle, pubkeys, msgID1B, mootID)
assert.ifError(err, 'valid 2nd forked msg')
}) })
const msgID1B = MsgV3.getMsgID(msg1B)
tangle.add(msgID1A, msg1A) await t.test('Correct erased msg', (t) => {
tangle.add(msgID1B, msg1B) const keypair = Keypair.generate('ed25519', 'alice')
const err = MsgV3.validate(msg1B, tangle, pubkeys, msgID1B, mootID) const account = MsgV3.getMsgID(
assert.ifError(err, 'valid 2nd forked msg') MsgV3.createAccount(keypair, 'person', 'alice')
}) )
const pubkeys = new Set([keypair.public])
test('MsgV3 validate erased msg', (t) => { const moot = MsgV3.createMoot(account, 'post', keypair)
const keypair = Keypair.generate('ed25519', 'alice') const mootID = MsgV3.getMsgID(moot)
const account = MsgV3.getMsgID( const tangle = new MsgV3.Tangle(mootID)
MsgV3.createAccount(keypair, 'person', 'alice') tangle.add(mootID, moot)
)
const pubkeys = new Set([keypair.public])
const moot = MsgV3.createMoot(account, 'post', keypair) const msg1 = MsgV3.create({
const mootID = MsgV3.getMsgID(moot) account,
const tangle = new MsgV3.Tangle(mootID) accountTips: [account],
tangle.add(mootID, moot) domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
})
msg1.data = null
const msgID1 = MsgV3.getMsgID(msg1)
const msg1 = MsgV3.create({ const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID)
account, assert.ifError(err, 'valid erased msg')
accountTips: [account],
domain: 'post',
data: { text: 'Hello world!' },
tangles: {
[mootID]: tangle,
},
keypair,
}) })
msg1.data = null
const msgID1 = MsgV3.getMsgID(msg1)
const err = MsgV3.validate(msg1, tangle, pubkeys, msgID1, mootID)
assert.ifError(err, 'valid erased msg')
}) })