diff --git a/lib/index.js b/lib/index.js index fdaec2d..2034a3e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -188,9 +188,9 @@ function initGoals(peer, config) { /** * @public * @param {RecPresent} rec - * @param {CB} cb + * @returns {Purpose} */ - function getRecordPurpose(rec, cb) { + function getRecordPurpose(rec) { assertDBPlugin(peer) let servesAsTrail = false @@ -202,13 +202,13 @@ function initGoals(peer, config) { if (!tangle) break asRoot const [min, max] = crossGoalWithTangle(goal, tangle) if (min > max) break asRoot - if (min === 0) return cb(null, 'goal') + if (min === 0) return 'goal' if (min > 0) servesAsTrail = true } // Check whether this record is a goalful affix of some tangle: const validTangles = - /** @type {Array<[DBTangle, number, number, number]>} */ ([]) + /** @type {Array<[DBTangle, number, number, number, GoalType]>} */ ([]) asAffix: for (const tangleID in rec.msg.metadata.tangles) { if (!goals.has(tangleID)) continue asAffix const goal = /** @type {GoalImpl} */ (goals.get(tangleID)) @@ -219,15 +219,15 @@ function initGoals(peer, config) { if (min > max) continue asAffix const recDepth = tangle.getDepth(rec.id) if (recDepth < 0) continue asAffix - validTangles.push([tangle, min, max, recDepth]) + validTangles.push([tangle, min, max, recDepth, goal.type]) } // (Loop over once without heavy computations and maybe return early:) for (const [, min, max, recDepth] of validTangles) { - if (min <= recDepth && recDepth <= max) return cb(null, 'goal') + if (min <= recDepth && recDepth <= max) return 'goal' } // At this point we know that the record *cannot* serve as 'goal', // so if it serves as trail, that'll do: - if (servesAsTrail) return cb(null, 'trail') + if (servesAsTrail) return 'trail' // Check whether this record is a trail affix of some tangle: // (Loop again with heavy computations now that it's inevitable:) for (const [tangle, min] of validTangles) { @@ -235,27 +235,20 @@ function initGoals(peer, config) { .topoSort() .filter((msgID) => tangle.getDepth(msgID) === min) const { erasables } = tangle.getDeletablesAndErasables(...minMsgIDs) - if (erasables.has(rec.id)) return cb(null, 'trail') + if (erasables.has(rec.id)) return 'trail' } // Check whether this record is a ghost affix of some tangle: - if (validTangles.length > 0) { - const done = /** @type {Multicb>} */ (multicb({ pluck: 1 })) - for (const [tangle] of validTangles) { - peer.db.ghosts.get(tangle.id, done()) - } - done((err, allGhosts) => { - // prettier-ignore - if (err) return cb(new Error('getRecordPurpose() failed to get ghosts', {cause: err})) - for (const ghosts of allGhosts) { - if (ghosts.includes(rec.id)) return cb(null, 'ghost') + for (const [tangle, , , , goalType] of validTangles) { + if (goalType === 'record') { + assertRecordPlugin(peer) + if (peer.record.isGhostable(rec.id, tangle.id)) { + return 'ghost' } - cb(null, 'none') - }) - return + } } - cb(null, 'none') + return 'none' } /** diff --git a/test/goals.test.js b/test/goals.test.js index a8ef3eb..e3d9f83 100644 --- a/test/goals.test.js +++ b/test/goals.test.js @@ -34,7 +34,7 @@ test('set, getByID, list, listen', async (t) => { } { - const purpose = await p(alice.goals.getRecordPurpose)(aliceAccountRoot) + const purpose = alice.goals.getRecordPurpose(aliceAccountRoot) assert.equal(purpose, 'goal', 'rec purpose is "goal"') } @@ -91,17 +91,46 @@ test('getRecordPurpose', async (t) => { const gottenGoal = alice.goals.get(feedID) assert.strictEqual(gottenGoal.id, feedID, 'gotten goal id is correct') - const purpose = await p(alice.goals.getRecordPurpose)(post2) + const purpose = alice.goals.getRecordPurpose(post2) assert.equal(purpose, 'goal', 'purpose is "goal"') alice.goals.set(feedID, 'newest-1') assert('set goal to newest-1') - const purpose2 = await p(alice.goals.getRecordPurpose)(post2) + const purpose2 = alice.goals.getRecordPurpose(post2) assert.equal(purpose2, 'none', 'purpose2 is "none"') - await p(alice.db.ghosts.add)({ msg: post2.id, tangle: feedID, max: 5 }) - const purpose3 = await p(alice.goals.getRecordPurpose)(post2) - assert.equal(purpose3, 'ghost', 'purpose3 is "ghost"') - + await p(alice.close)(true) +}) + +test('getRecordPurpose ghost', async (t) => { + const alice = createPeer({ name: 'alice', record: {ghostSpan: 3} }) + + await alice.db.loaded() + const aliceID = await p(alice.db.account.create)({ + domain: 'account', + _nonce: 'alice', + }) + + await p(alice.record.load)(aliceID) + await p(alice.record.update)('profile', { name: 'alice' }) + await p(alice.record.update)('profile', { name: 'Alice' }) + await p(alice.record.update)('profile', { name: 'Alicia' }) + await p(alice.record.update)('profile', { name: 'ALICIA' }) + await p(alice.record.update)('profile', { name: 'ALICIAA' }) + + const feedID = alice.record.getFeedID('profile') + const tangle = alice.db.getTangle(feedID) + + const msgIDs = tangle.topoSort() + assert.equal(msgIDs.length, 6, 'tangle has root+5 messages') + const recs = msgIDs.map(id => alice.db.getRecord(id)) + + alice.goals.set(feedID, 'record') + assert.equal(alice.goals.getRecordPurpose(recs[1]), 'none') + assert.equal(alice.goals.getRecordPurpose(recs[2]), 'ghost') + assert.equal(alice.goals.getRecordPurpose(recs[3]), 'trail') + assert.equal(alice.goals.getRecordPurpose(recs[4]), 'trail') + assert.equal(alice.goals.getRecordPurpose(recs[5]), 'goal') + await p(alice.close)(true) }) diff --git a/test/util.js b/test/util.js index 0d8e070..3f9ba8d 100644 --- a/test/util.js +++ b/test/util.js @@ -18,6 +18,7 @@ function createPeer(opts) { .use(require('secret-stack/plugins/net')) .use(require('secret-handshake-ext/secret-stack')) .use(require('ppppp-db')) + .use(require('ppppp-record')) .use(require('ssb-box')) .use(require('../lib')) .call(null, {