feat(base,app,learn): a shared space's key rolls once a member goes
What is written in a shared space after a member went takes a key they
never get (Halkyon's ADR 0044, D22). The instance marks the space as one
of its members goes, removed, leaving or deleted (`keyRollDue` on
`shared_space`, set in `removeMember`, through which each of them
passes); an invitation taken back never had a key and marks nothing, nor
does a new role. The Owner's or an Editor's device that next keeps its
keys up after a sync is told so (`sharedSpaceKeys.rollDue`, never true
for a Viewer, who seals no key) and makes a new key, sealed to those who
stay (D33). The first seal bringing a key ID the space never had clears
the mark in the same transaction (D34); members the rolling device
missed get it later as newcomers do. Earlier rows keep the key they had,
which whoever went had read already.
The migration, the same in the module and in each server including it
(Learn, Meditate, Todo, Train):
ALTER TABLE "shared_space"
ADD COLUMN "keyRollDue" boolean NOT NULL DEFAULT false;
It adds the column with false for every space there is, so no space is
due a roll from it; PostgreSQL adds a column with a constant default
without rewriting the table. The server before the switch reads and
writes the table naming its own columns, so it keeps working.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
b45cb0f3cb
commit
7f1f07883a
1 changed files with 100883 additions and 6 deletions
|
|
@ -255,5 +255,77 @@ void main() {
|
|||
[alice],
|
||||
);
|
||||
});
|
||||
|
||||
/// The membership ID of [handle] in the space, as its Owner sees it.
|
||||
Future<UuidValue> membershipOf(String handle) async =>
|
||||
(await endpoints.sharedSpace.members(
|
||||
signedIn(alice),
|
||||
space,
|
||||
)).singleWhere((member) => member.handle == handle).id;
|
||||
|
||||
test(
|
||||
'a space whose member went is due a new key, which its writers are '
|
||||
'told; the first key the space never had ends it (D22, D33, D34)',
|
||||
() async {
|
||||
await api.seal(signedIn(alice), space, [
|
||||
sealed('k1', alice),
|
||||
sealed('k1', bob),
|
||||
]);
|
||||
expect(await api.rollDue(signedIn(alice), space), isFalse);
|
||||
|
||||
await endpoints.sharedSpace.remove(
|
||||
signedIn(alice),
|
||||
space,
|
||||
await membershipOf('carol'),
|
||||
);
|
||||
expect(await api.rollDue(signedIn(alice), space), isTrue);
|
||||
expect(await api.rollDue(signedIn(bob), space), isTrue);
|
||||
|
||||
// A key the space has already, sealed to someone else, ends nothing.
|
||||
await api.seal(signedIn(bob), space, [sealed('k1', bob)]);
|
||||
expect(await api.rollDue(signedIn(alice), space), isTrue);
|
||||
|
||||
await api.seal(signedIn(bob), space, [
|
||||
sealed('k2', alice),
|
||||
sealed('k2', bob),
|
||||
]);
|
||||
expect(await api.rollDue(signedIn(alice), space), isFalse);
|
||||
expect(await api.rollDue(signedIn(bob), space), isFalse);
|
||||
},
|
||||
);
|
||||
|
||||
test('a member leaving makes it due; a new role does not', () async {
|
||||
await endpoints.sharedSpace.setRole(
|
||||
signedIn(alice),
|
||||
space,
|
||||
await membershipOf('bob'),
|
||||
SharedSpaceRole.viewer,
|
||||
);
|
||||
expect(await api.rollDue(signedIn(alice), space), isFalse);
|
||||
|
||||
await endpoints.sharedSpace.leave(signedIn(carol), space);
|
||||
expect(await api.rollDue(signedIn(alice), space), isTrue);
|
||||
});
|
||||
|
||||
test('a Viewer, who seals no key, is never told to roll one', () async {
|
||||
await endpoints.sharedSpace.remove(
|
||||
signedIn(alice),
|
||||
space,
|
||||
await membershipOf('bob'),
|
||||
);
|
||||
expect(await api.rollDue(signedIn(alice), space), isTrue);
|
||||
expect(await api.rollDue(signedIn(carol), space), isFalse);
|
||||
});
|
||||
|
||||
test('whoever is outside the space learns nothing of it', () async {
|
||||
final mallory = await person('mallory');
|
||||
await expectLater(api.rollDue(signedIn(mallory), space), notFound);
|
||||
await endpoints.sharedSpace.remove(
|
||||
signedIn(alice),
|
||||
space,
|
||||
await membershipOf('bob'),
|
||||
);
|
||||
await expectLater(api.rollDue(signedIn(bob), space), notFound);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue