From 42bdb86981e00f210a4914c9c66b0fd2ed3b7b1d Mon Sep 17 00:00:00 2001 From: mix irving Date: Mon, 25 Sep 2023 09:44:57 +1300 Subject: [PATCH 1/3] case 4.7 tested - completely split group --- lib/epochs/index.js | 12 ++--- test/helpers/replicate.js | 2 +- test/lib/epochs.test.js | 105 +++++++++++++++++++++++++++++++++++--- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/lib/epochs/index.js b/lib/epochs/index.js index 669665d..a2220fd 100644 --- a/lib/epochs/index.js +++ b/lib/epochs/index.js @@ -465,19 +465,17 @@ function BuildPreferredEpoch(ssb, groupId) { } // case 4.6 - groups have overlapping membership, but disjoint - else if ( - intersection(members0, members1).size > 0 - ) { + else if (intersection(members0, members1).size > 0) { // choose one for now, preferredEpoch = tieBreak(tips) // but also kick off resolution fixDisjointEpochsLater(ssb, groupId) // <<< DELAYED SIDE EFFECTS! + } else { + console.log(ssb.name, members0, members1) + return cb(new Error('unknown membership case!')) } - // case 4.7 - disjoint membership (no overlap!) - - // prettier-ignore - else return cb(Error('Membership case not handled yet')) + // in this case peers should not even know about 2 distinct memberships } else return cb(Error(`case of ${tips.length} tips not handled yet`)) if (preferredEpoch.members) delete preferredEpoch.members diff --git a/test/helpers/replicate.js b/test/helpers/replicate.js index f1d55e2..7206d5e 100644 --- a/test/helpers/replicate.js +++ b/test/helpers/replicate.js @@ -55,7 +55,7 @@ module.exports = async function replicate(...peers) { ) } -const runTimer = false +const runTimer = !false async function replicatePair(person1, person2) { let ID = [person1, person2].map(getName).join('-') while (ID.length < 12) ID += ' ' diff --git a/test/lib/epochs.test.js b/test/lib/epochs.test.js index 01103ca..7776372 100644 --- a/test/lib/epochs.test.js +++ b/test/lib/epochs.test.js @@ -16,6 +16,9 @@ function getRootIds(peers) { peers.map((peer) => p(peer.metafeeds.findOrCreate)()) ).then((feeds) => feeds.map((feed) => feed.id)) } +function closeAll(peers) { + return Promise.all(peers.map((peer) => p(peer.close)(true))) +} test('lib/epochs (getEpochs, getMembers)', async (t) => { const run = Run(t) @@ -26,7 +29,7 @@ test('lib/epochs (getEpochs, getMembers)', async (t) => { async function sync(label) { return run(`(sync ${label})`, replicate(peers), { isTest: false }) } - t.teardown(() => peers.forEach((peer) => peer.close(true))) + t.teardown(async () => await closeAll(peers)) const [aliceId, bobId, oscarId] = await getRootIds(peers) await run( @@ -153,7 +156,7 @@ test('lib/epochs (getMissingMembers)', async (t) => { { isTest: false } ) } - t.teardown(() => peers.forEach((peer) => peer.close(true))) + t.teardown(async () => await closeAll(peers)) await run( 'start tribes', @@ -260,7 +263,7 @@ test('lib/epochs (getPreferredEpoch - 4.4. same membership)', async (t) => { Server({ name: 'bob' }), Server({ name: 'oscar' }), ] - t.teardown(() => peers.forEach((peer) => peer.close(true))) + t.teardown(async () => await closeAll(peers)) const [alice, bob, oscar] = peers const [bobId, oscarId] = await getRootIds([bob, oscar]) @@ -381,7 +384,7 @@ test('lib/epochs (getPreferredEpoch - 4.5. subset membership)', async (t) => { Server({ name: 'carol' }), Server({ name: 'oscar' }), ] - t.teardown(() => peers.forEach((peer) => peer.close(true))) + t.teardown(async () => await closeAll(peers)) const [alice, bob, carol, oscar] = peers const [bobId, carolId, oscarId] = await getRootIds([bob, carol, oscar]) @@ -457,7 +460,7 @@ test('lib/epochs (getPreferredEpoch - 4.6. overlapping membership)', async (t) = Server({ name: 'carol' }), Server({ name: 'oscar' }), ] - t.teardown(() => peers.forEach((peer) => peer.close(true))) + t.teardown(async () => await closeAll(peers)) const [alice, bob, carol, oscar] = peers const [bobId, carolId, oscarId] = await getRootIds([bob, carol, oscar]) @@ -481,6 +484,7 @@ test('lib/epochs (getPreferredEpoch - 4.6. overlapping membership)', async (t) = 'others accept invites', Promise.all([ bob.tribes2.acceptInvite(group.id), + carol.tribes2.acceptInvite(group.id), oscar.tribes2.acceptInvite(group.id), ]) ) @@ -520,8 +524,97 @@ test('lib/epochs (getPreferredEpoch - 4.6. overlapping membership)', async (t) = t.end() }) -test.skip('lib/epochs (getPreferredEpoch - 4.7. disjoint membership)', async (t) => { +test('lib/epochs (getPreferredEpoch - 4.7. disjoint membership)', async (t) => { // there is no conflict in this case (doesn't need testing?) + // alice starts a group, adds bob, carol, oscar + // simultaneously: + // - alice excludes carol, oscar + // - oscar excludes alice, bob + // + // the group split! + + const run = Run(t) + + // + const peers = [ + Server({ name: 'alice' }), + Server({ name: 'bob' }), + Server({ name: 'carol' }), + Server({ name: 'oscar' }), + ] + t.teardown(async () => await closeAll(peers)) + + const [alice, bob, carol, oscar] = peers + const [aliceId, bobId, carolId, oscarId] = await getRootIds([ + alice, + bob, + carol, + oscar, + ]) + await run( + 'start tribes', + Promise.all(peers.map((peer) => peer.tribes2.start())) + ) + + const group = await run('alice creates a group', alice.tribes2.create({})) + + await run('(sync dm feeds)', replicate(alice, bob, carol, oscar)) + + await run( + 'alice invites bob, carol, oscar', + alice.tribes2.addMembers(group.id, [bobId, carolId, oscarId], {}) + ) + + await run('(sync dm feeds)', replicate(alice, bob, carol, oscar)) + + await run( + 'others accept invites', + Promise.all([ + bob.tribes2.acceptInvite(group.id), + carol.tribes2.acceptInvite(group.id), + oscar.tribes2.acceptInvite(group.id), + ]) + ) + // + + await Promise.all([ + run( + 'alice excludes carol, oscar', + alice.tribes2.excludeMembers(group.id, [carolId, oscarId], {}) + ), + run( + 'oscar excludes alice, bob', + oscar.tribes2.excludeMembers(group.id, [aliceId, bobId], {}) + ), + ]) + + await run( + '(sync exclusions)', + Promise.all([ + replicate(alice, bob), + replicate(oscar, carol), + replicate(alice, oscar), + ]) + ) + + const DELAY = 1000 + console.log('sleep', DELAY) // eslint-disable-line + await p(setTimeout)(DELAY) + + const aliceTips = await Epochs(alice).getTipEpochs(group.id) + const oscarTips = await Epochs(oscar).getTipEpochs(group.id) + t.equal(aliceTips.length, 1, 'alice sees only one tip') + t.equal(oscarTips.length, 1, 'oscar sees only one tip') + + const [alicePreferred, bobPreferred, carolPreferred, oscarPreferred] = + await Promise.all( + peers.map((peer) => Epochs(peer).getPreferredEpoch(group.id)) + ) + + t.deepEqual(alicePreferred, bobPreferred, 'alice and bob agree epoch') + t.deepEqual(carolPreferred, oscarPreferred, 'carol and oscar agree epoch') + t.notDeepEqual(alicePreferred, oscarPreferred, 'the group is split') + t.end() }) From 7e660db21c108ad5fa6b06bfa9aa413c89fd2180 Mon Sep 17 00:00:00 2001 From: mix irving Date: Mon, 25 Sep 2023 09:48:30 +1300 Subject: [PATCH 2/3] turn off replication logging --- test/helpers/replicate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/replicate.js b/test/helpers/replicate.js index 7206d5e..f1d55e2 100644 --- a/test/helpers/replicate.js +++ b/test/helpers/replicate.js @@ -55,7 +55,7 @@ module.exports = async function replicate(...peers) { ) } -const runTimer = !false +const runTimer = false async function replicatePair(person1, person2) { let ID = [person1, person2].map(getName).join('-') while (ID.length < 12) ID += ' ' From df5954ecae806a788b621c53e221ef9fa83f235f Mon Sep 17 00:00:00 2001 From: mix irving Date: Mon, 25 Sep 2023 10:15:50 +1300 Subject: [PATCH 3/3] fix linting error --- lib/epochs/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/epochs/index.js b/lib/epochs/index.js index a2220fd..88d28df 100644 --- a/lib/epochs/index.js +++ b/lib/epochs/index.js @@ -471,7 +471,6 @@ function BuildPreferredEpoch(ssb, groupId) { // but also kick off resolution fixDisjointEpochsLater(ssb, groupId) // <<< DELAYED SIDE EFFECTS! } else { - console.log(ssb.name, members0, members1) return cb(new Error('unknown membership case!')) } // case 4.7 - disjoint membership (no overlap!)