nervur 0.19.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -101
- package/dist/being/being.d.ts +7 -15
- package/dist/being/being.js +16 -67
- package/dist/being/digest.d.ts +1 -1
- package/dist/being/digest.js +7 -27
- package/dist/being/faculty.d.ts +9 -0
- package/dist/being/faculty.js +68 -0
- package/dist/being/index.d.ts +4 -5
- package/dist/being/index.js +5 -5
- package/dist/being/types.d.ts +35 -62
- package/dist/being/types.js +3 -57
- package/dist/being/words.d.ts +13 -0
- package/dist/being/words.js +14 -0
- package/dist/contract/index.d.ts +26 -0
- package/dist/contract/index.js +19 -0
- package/dist/crypto/aes.d.ts +3 -0
- package/dist/crypto/aes.js +24 -0
- package/dist/crypto/bytes.d.ts +6 -0
- package/dist/crypto/bytes.js +33 -0
- package/dist/crypto/ed25519.d.ts +3 -0
- package/dist/crypto/ed25519.js +85 -0
- package/dist/crypto/hash.d.ts +2 -0
- package/dist/crypto/hash.js +11 -0
- package/dist/crypto/index.d.ts +7 -0
- package/dist/crypto/index.js +10 -0
- package/dist/crypto/json.d.ts +8 -0
- package/dist/crypto/json.js +250 -0
- package/dist/crypto/mlkem.d.ts +12 -0
- package/dist/crypto/mlkem.js +36 -0
- package/dist/crypto/subtle.d.ts +10 -0
- package/dist/crypto/subtle.js +29 -0
- package/dist/crypto/x25519.d.ts +2 -0
- package/dist/crypto/x25519.js +21 -0
- package/dist/folder/index.d.ts +13 -0
- package/dist/folder/index.js +125 -0
- package/dist/harbor/dock.d.ts +19 -0
- package/dist/harbor/dock.js +92 -0
- package/dist/harbor/harbor.d.ts +22 -0
- package/dist/harbor/harbor.js +84 -0
- package/dist/harbor/index.d.ts +4 -10
- package/dist/harbor/index.js +5 -18
- package/dist/harbor/registry.d.ts +7 -0
- package/dist/harbor/registry.js +13 -0
- package/dist/harbor/terrain.d.ts +8 -0
- package/dist/harbor/terrain.js +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/pointer/bodies.d.ts +20 -0
- package/dist/pointer/bodies.js +45 -0
- package/dist/pointer/index.d.ts +2 -0
- package/dist/pointer/index.js +4 -0
- package/dist/pointer/world.d.ts +34 -0
- package/dist/pointer/world.js +76 -0
- package/dist/quo/door.d.ts +34 -0
- package/dist/quo/door.js +172 -0
- package/dist/quo/index.d.ts +8 -0
- package/dist/quo/index.js +11 -0
- package/dist/quo/invitation.d.ts +7 -0
- package/dist/quo/invitation.js +15 -0
- package/dist/quo/keys.d.ts +40 -0
- package/dist/quo/keys.js +79 -0
- package/dist/quo/payload.d.ts +15 -0
- package/dist/quo/payload.js +55 -0
- package/dist/quo/relations.d.ts +40 -0
- package/dist/quo/relations.js +33 -0
- package/dist/quo/reply.d.ts +13 -0
- package/dist/quo/reply.js +35 -0
- package/dist/quo/seal.d.ts +42 -0
- package/dist/quo/seal.js +78 -0
- package/dist/quo/standing.d.ts +39 -0
- package/dist/quo/standing.js +90 -0
- package/dist/ward/allowance.d.ts +13 -9
- package/dist/ward/allowance.js +28 -67
- package/dist/ward/cells.d.ts +6 -6
- package/dist/ward/cells.js +67 -179
- package/dist/ward/index.d.ts +5 -10
- package/dist/ward/index.js +6 -13
- package/dist/ward/partition.d.ts +44 -60
- package/dist/ward/partition.js +56 -285
- package/dist/ward/stance.d.ts +29 -20
- package/dist/ward/stance.js +173 -406
- package/dist/ward/ward-being.d.ts +33 -0
- package/dist/ward/ward-being.js +76 -0
- package/dist/ward/ward.d.ts +43 -11
- package/dist/ward/ward.js +215 -363
- package/package.json +19 -33
- package/src/being/being.ts +31 -78
- package/src/being/digest.ts +7 -35
- package/src/being/faculty.ts +66 -0
- package/src/being/index.ts +5 -6
- package/src/being/types.ts +47 -145
- package/src/being/words.ts +31 -0
- package/src/contract/index.ts +44 -0
- package/src/crypto/aes.ts +26 -0
- package/src/crypto/bytes.ts +37 -0
- package/src/crypto/ed25519.ts +84 -0
- package/src/crypto/hash.ts +14 -0
- package/src/crypto/index.ts +10 -0
- package/src/crypto/json.ts +241 -0
- package/src/crypto/mlkem.ts +38 -0
- package/src/crypto/subtle.ts +33 -0
- package/src/crypto/x25519.ts +21 -0
- package/src/folder/index.ts +134 -0
- package/src/harbor/dock.ts +101 -0
- package/src/harbor/harbor.ts +105 -0
- package/src/harbor/index.ts +5 -19
- package/src/harbor/registry.ts +20 -0
- package/src/harbor/terrain.ts +13 -0
- package/src/index.ts +20 -0
- package/src/pointer/bodies.ts +47 -0
- package/src/pointer/index.ts +4 -0
- package/src/pointer/world.ts +91 -0
- package/src/quo/door.ts +178 -0
- package/src/quo/index.ts +11 -0
- package/src/quo/invitation.ts +15 -0
- package/src/quo/keys.ts +91 -0
- package/src/quo/payload.ts +61 -0
- package/src/quo/relations.ts +62 -0
- package/src/quo/reply.ts +38 -0
- package/src/quo/seal.ts +101 -0
- package/src/quo/standing.ts +111 -0
- package/src/stand/main.ts +19 -0
- package/src/stand/stand.ts +178 -0
- package/src/ward/allowance.ts +37 -75
- package/src/ward/cells.ts +63 -176
- package/src/ward/index.ts +6 -17
- package/src/ward/partition.ts +86 -326
- package/src/ward/stance.ts +185 -420
- package/src/ward/ward-being.ts +97 -0
- package/src/ward/ward.ts +229 -363
- package/dist/being/lent.d.ts +0 -32
- package/dist/being/lent.js +0 -72
- package/dist/being/silence.d.ts +0 -12
- package/dist/being/silence.js +0 -41
- package/dist/conformance/assert.d.ts +0 -11
- package/dist/conformance/assert.js +0 -106
- package/dist/conformance/beings.d.ts +0 -199
- package/dist/conformance/beings.js +0 -188
- package/dist/conformance/estate.d.ts +0 -5
- package/dist/conformance/estate.js +0 -388
- package/dist/conformance/index.d.ts +0 -80
- package/dist/conformance/index.js +0 -819
- package/dist/conformance/reach.d.ts +0 -10
- package/dist/conformance/reach.js +0 -72
- package/dist/conformance/store.d.ts +0 -5
- package/dist/conformance/store.js +0 -113
- package/dist/harbor/box.d.ts +0 -121
- package/dist/harbor/box.js +0 -121
- package/dist/harbor/core.d.ts +0 -55
- package/dist/harbor/core.js +0 -662
- package/dist/harbor/dial.d.ts +0 -9
- package/dist/harbor/dial.js +0 -81
- package/dist/harbor/memory.d.ts +0 -29
- package/dist/harbor/memory.js +0 -104
- package/dist/harbor/reach.d.ts +0 -36
- package/dist/harbor/reach.js +0 -199
- package/dist/harbor/store.d.ts +0 -35
- package/dist/harbor/store.js +0 -62
- package/dist/vector/cases.d.ts +0 -42
- package/dist/vector/cases.js +0 -223
- package/dist/vector/index.d.ts +0 -6
- package/dist/vector/index.js +0 -8
- package/dist/vector/stand.d.ts +0 -9
- package/dist/vector/stand.js +0 -77
- package/dist/vector/world.d.ts +0 -144
- package/dist/vector/world.js +0 -209
- package/dist/ward/arithmetic.d.ts +0 -31
- package/dist/ward/arithmetic.js +0 -249
- package/dist/ward/door.d.ts +0 -18
- package/dist/ward/door.js +0 -186
- package/dist/ward/ground.d.ts +0 -29
- package/dist/ward/ground.js +0 -56
- package/dist/ward/heirs.d.ts +0 -13
- package/dist/ward/heirs.js +0 -117
- package/dist/ward/json.d.ts +0 -2
- package/dist/ward/json.js +0 -163
- package/dist/ward/owner.d.ts +0 -13
- package/dist/ward/owner.js +0 -220
- package/dist/ward/seal.d.ts +0 -52
- package/dist/ward/seal.js +0 -150
- package/src/being/lent.ts +0 -72
- package/src/being/silence.ts +0 -46
- package/src/conformance/assert.ts +0 -100
- package/src/conformance/beings.ts +0 -188
- package/src/conformance/estate.ts +0 -412
- package/src/conformance/index.ts +0 -965
- package/src/conformance/reach.ts +0 -83
- package/src/conformance/store.ts +0 -125
- package/src/harbor/box.ts +0 -131
- package/src/harbor/core.ts +0 -699
- package/src/harbor/dial.ts +0 -112
- package/src/harbor/memory.ts +0 -123
- package/src/harbor/reach.ts +0 -221
- package/src/harbor/store.ts +0 -91
- package/src/vector/cases.ts +0 -257
- package/src/vector/index.ts +0 -11
- package/src/vector/stand.ts +0 -76
- package/src/vector/world.ts +0 -232
- package/src/ward/arithmetic.ts +0 -251
- package/src/ward/door.ts +0 -186
- package/src/ward/ground.ts +0 -142
- package/src/ward/heirs.ts +0 -116
- package/src/ward/json.ts +0 -144
- package/src/ward/owner.ts +0 -214
- package/src/ward/seal.ts +0 -178
|
@@ -1,819 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The ward conformance suite. The beings are fixed; the ward is under test.
|
|
3
|
-
//
|
|
4
|
-
// A ward is handed in as a probe:
|
|
5
|
-
//
|
|
6
|
-
// make({ lends }) -> world. `lends` maps a name the box lends to the
|
|
7
|
-
// class of the being it lends under it; the probe
|
|
8
|
-
// stands one of each in a ward its harbor roots,
|
|
9
|
-
// and every ward it boots is grounded to lend them
|
|
10
|
-
// world.boot(key, Class, cells?, { isPublic }) -> handle
|
|
11
|
-
// world.heir(invitation) -> { current, announced, fresh } | undefined
|
|
12
|
-
// what the inviting ward holds for that heir
|
|
13
|
-
// world.oneWard true when every being shares one ward
|
|
14
|
-
// handle.pk the ward pk she lives behind
|
|
15
|
-
// handle.being the object, as the test drives it
|
|
16
|
-
// handle.cells her cells, as the ward keeps them
|
|
17
|
-
// handle.down = true | false her door is unreachable (needs canDown)
|
|
18
|
-
// handle.minted.has(pk) the last eight pks her side minted
|
|
19
|
-
// handle.forgeKnock(invitation) make her side knock with a key of its own, unannounced
|
|
20
|
-
// world.census() every partition in the world, as values
|
|
21
|
-
// world.migrate(key) her ward moves to another harbor, same seed, same pk
|
|
22
|
-
//
|
|
23
|
-
// Every assertion here is the truth's. Nothing here knows how a ward is built.
|
|
24
|
-
import { assert } from './assert.js';
|
|
25
|
-
import { silence, answered, isSilence, isUnreached, told } from '../being/silence.js';
|
|
26
|
-
import { digest } from '../being/digest.js';
|
|
27
|
-
import { Printer, Shop, Customer, Echo, Maker, Member } from './beings.js';
|
|
28
|
-
import { estate } from './estate.js';
|
|
29
|
-
export { Printer, Shop, Customer, Echo, Maker, Member };
|
|
30
|
-
// The two shapes the suite asserts, spelled here for the whole folder. They
|
|
31
|
-
// are spelled again in `src/ward/seal.ts`, and that is the point: this suite
|
|
32
|
-
// is what a second kit runs against its own ward, so it states the shapes it
|
|
33
|
-
// expects instead of importing ours, and nothing under `src/conformance`
|
|
34
|
-
// reaches into `src/ward` for anything.
|
|
35
|
-
export const HEX64 = /^[0-9a-f]{64}$/; // one key, one secret, one digest
|
|
36
|
-
const HEX128 = /^[0-9a-f]{128}$/; // a ward pk: the signing pk then the padlock
|
|
37
|
-
// Her ward's word, as she receives it.
|
|
38
|
-
const said = (x, w) => told(x) === w;
|
|
39
|
-
// A world whose harness stands a ward up again. The two members are optional
|
|
40
|
-
// on the probe and required of a harness that says it can, so a chapter that
|
|
41
|
-
// needs them says so here rather than reaching past a maybe at every line.
|
|
42
|
-
const able = (w) => w;
|
|
43
|
-
// The same, for a world whose store can be made to refuse one row.
|
|
44
|
-
const picky = (w) => w;
|
|
45
|
-
// `canRefuse` is the one flag that is off unless a harness says otherwise. A
|
|
46
|
-
// harness whose store cannot be made to say no is the common case, and a
|
|
47
|
-
// harbor that leaves `keep` out of its ground is a harbor for which no store
|
|
48
|
-
// can refuse: both skip the chapter rather than fail it, and both say so at
|
|
49
|
-
// the call.
|
|
50
|
-
export function conform(label, make, { canDown = true, canLend = true, canRestart = true, canRefuse = false, test }) {
|
|
51
|
-
const t = (name, fn) => test(`[${label}] ${name}`, {}, fn);
|
|
52
|
-
const down = (name, fn) => test(`[${label}] ${name}`, { skip: !canDown && 'nothing to cut inside one ward' }, fn);
|
|
53
|
-
const lends = (name, fn) => test(`[${label}] ${name}`, { skip: !canLend && 'this harness stands no ward of its harbor to lend from' }, fn);
|
|
54
|
-
const kept = (name, fn) => test(`[${label}] ${name}`, { skip: !canRestart && 'this harness cannot hand a ward a partition and stand it up again' }, fn);
|
|
55
|
-
const refused = (name, fn) => test(`[${label}] ${name}`, { skip: !canRefuse && 'this harness has no store that can be made to refuse a row' }, fn);
|
|
56
|
-
async function world({ printerInvitesShop = true } = {}) {
|
|
57
|
-
const w = await make();
|
|
58
|
-
const printer = await w.boot('P', Printer);
|
|
59
|
-
const shop = await w.boot('S', Shop);
|
|
60
|
-
const alice = await w.boot('A', Customer);
|
|
61
|
-
const bob = await w.boot('B', Customer);
|
|
62
|
-
if (printerInvitesShop)
|
|
63
|
-
await shop.being.keepPrinter((await printer.being.invite('shop')));
|
|
64
|
-
return { w, printer, shop, alice, bob };
|
|
65
|
-
}
|
|
66
|
-
const bp = (x) => x;
|
|
67
|
-
// ---- invite
|
|
68
|
-
t('invite mints the occupant and a heir: the invitation is the far ward pk, the heir pk, and the heir secret. nothing of her.', async () => {
|
|
69
|
-
const { w, shop } = await world();
|
|
70
|
-
const inv = await shop.being.invite('gold');
|
|
71
|
-
assert.deepEqual(Object.keys(shop.cells.occupants), ['cust1']);
|
|
72
|
-
assert.deepEqual(Object.keys(inv).sort(), ['heir', 'secret', 'ward']);
|
|
73
|
-
assert.match(inv.ward, HEX128);
|
|
74
|
-
assert.match(inv.heir, HEX64);
|
|
75
|
-
assert.match(inv.secret, HEX64);
|
|
76
|
-
assert.equal(inv.ward, shop.pk);
|
|
77
|
-
assert.ok(shop.minted.has(inv.heir)); // rotation one: her ward minted the occupant's first key
|
|
78
|
-
assert.deepEqual(w.heir(inv), { current: inv.heir, announced: null, fresh: true }); // and keeps only its pk
|
|
79
|
-
for (const word of [inv.heir, inv.secret])
|
|
80
|
-
assert.ok(!JSON.stringify(shop.cells).includes(word)); // never in her cells
|
|
81
|
-
});
|
|
82
|
-
t('two invites, two heirs, nothing shared', async () => {
|
|
83
|
-
const { shop } = await world();
|
|
84
|
-
const i1 = await shop.being.invite();
|
|
85
|
-
const i2 = await shop.being.invite();
|
|
86
|
-
assert.notEqual(i1.heir, i2.heir);
|
|
87
|
-
assert.notEqual(i1.secret, i2.secret);
|
|
88
|
-
assert.equal(i1.ward, i2.ward);
|
|
89
|
-
});
|
|
90
|
-
t('remove before any knock: the heir is forgotten, and a knock on it by whoever holds it hears removed', async () => {
|
|
91
|
-
const { w, shop, alice } = await world();
|
|
92
|
-
const inv = await shop.being.invite();
|
|
93
|
-
shop.being.occupants.remove('cust1');
|
|
94
|
-
assert.equal(w.heir(inv), undefined);
|
|
95
|
-
assert.ok(said(await alice.being.join(inv), 'removed'));
|
|
96
|
-
assert.deepEqual(alice.cells.standings, {});
|
|
97
|
-
});
|
|
98
|
-
// ---- consume, the knock
|
|
99
|
-
t('a valid knock binds the id to her own key and spends the heir; the asker is { id }', async () => {
|
|
100
|
-
const { w, shop, alice } = await world();
|
|
101
|
-
const inv = await shop.being.invite('gold');
|
|
102
|
-
assert.deepEqual(await alice.being.join(inv), { welcome: true });
|
|
103
|
-
const h = w.heir(inv);
|
|
104
|
-
assert.equal(h.fresh, false);
|
|
105
|
-
assert.notEqual(h.current, inv.heir); // rotation two: the heir is spent
|
|
106
|
-
assert.ok(alice.minted.has(h.current)); // her own key speaks now
|
|
107
|
-
assert.ok(!shop.minted.has(h.current)); // which the inviter never held
|
|
108
|
-
assert.equal(shop.cells.occupants.cust1.notes.asks, 1);
|
|
109
|
-
});
|
|
110
|
-
t('a second knock with the same invitation, from another: silence, nothing rebinds', async () => {
|
|
111
|
-
const { w, shop, alice, bob } = await world();
|
|
112
|
-
const inv = await shop.being.invite();
|
|
113
|
-
await alice.being.join(inv);
|
|
114
|
-
const bound = w.heir(inv).current;
|
|
115
|
-
assert.ok(isSilence(await bob.being.join(inv)));
|
|
116
|
-
assert.equal(w.heir(inv).current, bound); // still alice's
|
|
117
|
-
assert.deepEqual(bob.cells.standings, {});
|
|
118
|
-
});
|
|
119
|
-
t('a knock with the wrong secret, or a forged heir, is silence and never reaches her', async () => {
|
|
120
|
-
const { w, shop, alice } = await world();
|
|
121
|
-
let reached = 0;
|
|
122
|
-
const real = shop.being.answer.bind(shop.being);
|
|
123
|
-
shop.being.answer = (...a) => (reached++, real(...a));
|
|
124
|
-
const inv = await shop.being.invite();
|
|
125
|
-
const other = await shop.being.invite();
|
|
126
|
-
assert.ok(isSilence(await alice.being.knock({ ward: inv.ward, heir: 'f'.repeat(64), secret: inv.secret }, 'hello')));
|
|
127
|
-
assert.ok(isSilence(await alice.being.knock({ ward: inv.ward, heir: inv.heir, secret: other.secret }, 'hello'))); // a real secret, the wrong heir
|
|
128
|
-
assert.ok(isSilence(await alice.being.knock({ ward: inv.ward, heir: inv.heir, secret: '0'.repeat(64) }, 'hello')));
|
|
129
|
-
assert.equal(reached, 0);
|
|
130
|
-
assert.equal(w.heir(inv).fresh, true); // still unbound
|
|
131
|
-
});
|
|
132
|
-
t('the same knocker may knock again: her key is bound, no heir needed, it is an ask, and it announces her next key', async () => {
|
|
133
|
-
const { w, shop, alice } = await world();
|
|
134
|
-
const inv = await shop.being.invite();
|
|
135
|
-
assert.deepEqual(await alice.being.join(inv), { welcome: true });
|
|
136
|
-
const k1 = w.heir(inv).current;
|
|
137
|
-
assert.equal(w.heir(inv).announced, null); // a knock announces her key and dies; nothing further yet
|
|
138
|
-
assert.deepEqual(await alice.being.knock(inv, 'hello'), { welcome: true });
|
|
139
|
-
assert.equal(shop.cells.occupants.cust1.notes.asks, 2);
|
|
140
|
-
const h = w.heir(inv);
|
|
141
|
-
assert.equal(h.current, k1); // k1 spoke
|
|
142
|
-
assert.ok(h.announced && alice.minted.has(h.announced) && h.announced !== k1); // and vouched for the next
|
|
143
|
-
});
|
|
144
|
-
t('a knocker who presents a key of her own, unannounced, on a bound heir: silence', async () => {
|
|
145
|
-
const { w, shop, alice, bob } = await world();
|
|
146
|
-
const inv = await shop.being.invite();
|
|
147
|
-
await alice.being.join(inv);
|
|
148
|
-
const bound = w.heir(inv).current;
|
|
149
|
-
bob.forgeKnock(inv);
|
|
150
|
-
assert.ok(isSilence(await bob.being.knock(inv, 'hello')));
|
|
151
|
-
assert.equal(w.heir(inv).current, bound);
|
|
152
|
-
assert.equal(shop.cells.occupants.cust1.notes.asks, 1);
|
|
153
|
-
});
|
|
154
|
-
t('ids never cross: hers for me and mine for her are unrelated', async () => {
|
|
155
|
-
const { shop, alice } = await world();
|
|
156
|
-
await alice.being.join(await shop.being.invite());
|
|
157
|
-
assert.deepEqual(Object.keys(alice.cells.standings), ['shop']);
|
|
158
|
-
assert.deepEqual(Object.keys(shop.cells.occupants), ['cust1']);
|
|
159
|
-
});
|
|
160
|
-
down('a standing is born only after an answer; silence or unreached births nothing; an unreached knock does not spend the heir', async () => {
|
|
161
|
-
const { w, shop, alice } = await world();
|
|
162
|
-
const inv = await shop.being.invite();
|
|
163
|
-
shop.down = true;
|
|
164
|
-
assert.ok(isUnreached(await alice.being.join(inv)));
|
|
165
|
-
assert.deepEqual(alice.cells.standings, {});
|
|
166
|
-
assert.equal(w.heir(inv).fresh, true);
|
|
167
|
-
shop.down = false;
|
|
168
|
-
assert.deepEqual(await alice.being.join(inv), { welcome: true }); // the same invitation still works
|
|
169
|
-
const inv2 = await shop.being.invite();
|
|
170
|
-
shop.being.occupants.remove('cust2'); // she changed her mind before the knock
|
|
171
|
-
assert.ok(said(await alice.being.join(inv2), 'removed'));
|
|
172
|
-
assert.deepEqual(Object.keys(alice.cells.standings), ['shop']);
|
|
173
|
-
});
|
|
174
|
-
// ---- the names that are nobody's to take
|
|
175
|
-
//
|
|
176
|
-
// Two lists, refused at both ends of the same namespace. The ward speaks
|
|
177
|
-
// under its own words, so a being who could mint one would be a being whose
|
|
178
|
-
// ward is indistinguishable from an occupant of hers. And standings are
|
|
179
|
-
// reached by name, so a relation taken as one of the calls on them would be
|
|
180
|
-
// a relation nothing can reach. A kit that ported invite without either
|
|
181
|
-
// list passes every other scene in this file.
|
|
182
|
-
t('the reserved ids and the calls on standings are refused at invite and at take, and nothing is written for them', async () => {
|
|
183
|
-
// alice hosts here rather than the shop, because the shop's own `invite`
|
|
184
|
-
// takes a tier and mints the id itself. This one is the stance's, which
|
|
185
|
-
// is where the rule lives.
|
|
186
|
-
const { w, alice, bob } = await world();
|
|
187
|
-
// `__proto__` is in the list because a partition cannot hold it. A kit
|
|
188
|
-
// that refuses it only where it writes her cells writes the ward's half
|
|
189
|
-
// first and then throws, which leaves one relation in two standings and a
|
|
190
|
-
// throw where this table promises a null.
|
|
191
|
-
for (const id of ['OWNER', 'PUBLIC', 'knock', 'take', 'remove', '__proto__']) {
|
|
192
|
-
assert.equal(await alice.being.invite(id), null, `${id} is not hers to mint`);
|
|
193
|
-
assert.equal(Object.hasOwn(alice.cells.occupants, id), false, `and nothing was written for ${id}`);
|
|
194
|
-
// The other end: a guest is answered and then refused the name.
|
|
195
|
-
const inv = (await alice.being.invite(`for-${id}`));
|
|
196
|
-
assert.deepEqual(await bob.being.knock(inv, 'hi'), { heard: 'hi' }, `the knock for ${id} was answered`);
|
|
197
|
-
assert.equal(await bob.being.take(id, inv), null, `take refuses ${id} for the same reason`);
|
|
198
|
-
assert.equal(Object.hasOwn(bob.cells.standings, id), false, `and nothing was written for ${id}`);
|
|
199
|
-
// The ward's half too, and not only hers: a standing filed here with no
|
|
200
|
-
// record of it in her cells is a lane nobody can see and nobody removes.
|
|
201
|
-
assert.equal(Object.hasOwn(w.census().binds.B.standings, id), false, `and the ward wrote no keys for ${id}`);
|
|
202
|
-
assert.equal(await bob.being.take(`as-${id}`, inv), `as-${id}`, 'and an ordinary id takes the same invitation');
|
|
203
|
-
}
|
|
204
|
-
// And the three calls are still the calls, not records that shadowed them.
|
|
205
|
-
for (const call of ['knock', 'take', 'remove'])
|
|
206
|
-
assert.equal(typeof bob.being.standings[call], 'function', `standings.${call} is the call`);
|
|
207
|
-
});
|
|
208
|
-
// ---- the owner
|
|
209
|
-
//
|
|
210
|
-
// The second pointer a ward hands back. Every ask below answers an object,
|
|
211
|
-
// because the owner hears objects and never a word: a refusal is `{ error }`
|
|
212
|
-
// with its reason, which is what a side renders.
|
|
213
|
-
t('the owner boots, marks one public being, invites, knocks, removes and unboots, and refuses each in an object', async () => {
|
|
214
|
-
const { w, shop, alice } = await world();
|
|
215
|
-
const ask = w.owner('S');
|
|
216
|
-
const bp = (await ask());
|
|
217
|
-
assert.deepEqual(bp.asks.map((a) => a.name), ['boot', 'public', 'invite', 'knock', 'remove', 'unboot', 'ask'], 'seven asks, in this order');
|
|
218
|
-
for (const a of bp.asks) {
|
|
219
|
-
assert.equal(typeof a.description, 'string', `${a.name} says what it is for`);
|
|
220
|
-
assert.equal(a.input.type, 'object', `${a.name} takes one object`);
|
|
221
|
-
}
|
|
222
|
-
assert.match(bp.notes.pk, HEX128);
|
|
223
|
-
assert.equal(typeof bp.notes.beings, 'object');
|
|
224
|
-
assert.deepEqual(await ask('nosuch'), { error: 'unknown ask' });
|
|
225
|
-
// boot: a key already taken is refused, and so is a class nobody holds.
|
|
226
|
-
assert.deepEqual(await ask('boot', { key: 'S', class: 'Shop' }), { error: 'no such class, or key taken' });
|
|
227
|
-
assert.deepEqual(await ask('boot', { key: 'fresh', class: 'NoSuchClass' }), { error: 'no such class, or key taken' });
|
|
228
|
-
assert.deepEqual(await ask('boot', { key: 42, class: 'Shop' }), { error: 'no such class, or key taken' }, 'a name is a string or it is nothing');
|
|
229
|
-
// public: one per ward, null takes the mark off, and the ward is nobody's
|
|
230
|
-
// public being.
|
|
231
|
-
assert.deepEqual(await ask('public', { key: 'nobody' }), { error: 'no such being' });
|
|
232
|
-
assert.deepEqual(await ask('public', { key: bp.notes.pk }), { error: 'no such being' }, 'the ward is a being to nobody outside');
|
|
233
|
-
// invite and remove, on a being of the ward, by id.
|
|
234
|
-
assert.deepEqual(await ask('invite', { being: 'nobody', id: 'x' }), { error: 'no such being' });
|
|
235
|
-
const inv = (await ask('invite', { being: 'S', id: 'by-owner', notes: { tier: 'gold' } }));
|
|
236
|
-
assert.match(inv.heir, HEX64);
|
|
237
|
-
assert.deepEqual(shop.cells.occupants['by-owner'].notes, { tier: 'gold' }, 'the notes she minted under are hers to read');
|
|
238
|
-
assert.deepEqual(await ask('remove', { being: 'S', id: 'nothing-of-hers' }), { error: 'nothing to remove' });
|
|
239
|
-
assert.deepEqual(await ask('remove', { being: 'S', id: 'by-owner' }), { removed: 'by-owner' });
|
|
240
|
-
assert.equal(Object.hasOwn(shop.cells.occupants, 'by-owner'), false);
|
|
241
|
-
// knock: the owner knocks for a being of hers and takes the standing.
|
|
242
|
-
const forA = (await ask('invite', { being: 'S', id: 'for-alice' }));
|
|
243
|
-
const knocked = (await w.owner('A'))('knock', { being: 'A', id: 'via-owner', invitation: forA, method: 'hello' });
|
|
244
|
-
assert.deepEqual(await knocked, { taken: 'via-owner', answer: { welcome: true } });
|
|
245
|
-
assert.deepEqual(Object.keys(alice.cells.standings), ['via-owner']);
|
|
246
|
-
assert.deepEqual(await alice.being.standings['via-owner'].ask('hello'), { welcome: true }, 'and it is an ordinary standing');
|
|
247
|
-
// unboot: the inverse of boot, and the ward refuses itself.
|
|
248
|
-
assert.deepEqual(await ask('unboot', { being: bp.notes.pk }), { error: 'no such being' });
|
|
249
|
-
assert.deepEqual(await ask('unboot', { being: 'nobody' }), { error: 'no such being' });
|
|
250
|
-
const gone = (await ask('unboot', { being: 'S' }));
|
|
251
|
-
assert.equal(gone.unbooted, 'S');
|
|
252
|
-
assert.ok(gone.removed.includes('for-alice'), 'her relations went with her, and the owner is told which');
|
|
253
|
-
assert.ok(said(await alice.being.standings['via-owner'].ask('hello'), 'removed'), 'and her occupant hears removed at the door');
|
|
254
|
-
});
|
|
255
|
-
// The owner's seventh ask: reaching into a being and asking her. It is the
|
|
256
|
-
// third asker of the ward-to-being edge, and the ward is the only one who
|
|
257
|
-
// fills it in: nothing outside a ward names OWNER, and no side holds a
|
|
258
|
-
// being's answer to judge it again. Strictly less than the unboot beside
|
|
259
|
-
// it, which takes her out with every relation she holds.
|
|
260
|
-
t('the owner asks a being of the ward, as owner or, with no being named, as nobody at the public being', async () => {
|
|
261
|
-
const { w } = await world();
|
|
262
|
-
const ask = w.owner('S');
|
|
263
|
-
const echo = w.owner('E');
|
|
264
|
-
await w.boot('E', Echo);
|
|
265
|
-
// Named: she is asked as the owner, and hears the owner's name.
|
|
266
|
-
assert.deepEqual(await echo('ask', { being: 'E', method: 'echo', args: { x: 1 } }), { from: 'OWNER', x: 1 }, 'the ward names the asker, and the name is the owner');
|
|
267
|
-
assert.deepEqual(bp(await ask('ask', { being: 'S' })).asks.map((a) => a.name), ['hello', 'buy'], 'no method is her describe, and a gate that reads an occupant record shows the owner nothing: refund is hers to nobody here');
|
|
268
|
-
assert.deepEqual(await ask('ask', { being: 'S', method: 'hello' }), { welcome: true });
|
|
269
|
-
assert.deepEqual(await ask('ask', { being: 'S', method: 'nosuch' }), { error: 'unknown ask' }, 'her own answer, as any asker gets it');
|
|
270
|
-
// Nobody named, and nobody home: one refusal, and never a fourth word.
|
|
271
|
-
assert.deepEqual(await echo('ask', {}), { error: 'no such being' }, 'a ward with no public being has nobody to ask as nobody');
|
|
272
|
-
assert.deepEqual(await echo('public', { key: 'E' }), { public: 'E' });
|
|
273
|
-
assert.deepEqual(await echo('ask', { method: 'echo', args: { x: 2 } }), { from: null, x: 2 }, 'with no being named it is the public being, asked as nobody');
|
|
274
|
-
// What it refuses, each in an object, because the owner hears objects.
|
|
275
|
-
const pk = bp(await ask()).notes.pk;
|
|
276
|
-
assert.deepEqual(await ask('ask', { being: 'nobody' }), { error: 'no such being' });
|
|
277
|
-
assert.deepEqual(await ask('ask', { being: pk }), { error: 'no such being' }, 'the ward is a being to nobody outside, here as at invite and unboot');
|
|
278
|
-
assert.deepEqual(await ask('ask', { being: 42 }), { error: 'no such being' }, 'a name is a string or it is nothing');
|
|
279
|
-
assert.deepEqual(await ask('ask', { being: 'S', method: 42 }), { error: 'an ask is named by a word' }, 'and so is an ask');
|
|
280
|
-
});
|
|
281
|
-
// The one public being, marked and unmarked through the owner. A second
|
|
282
|
-
// one would leave the first holding every relation she had, reachable by
|
|
283
|
-
// nobody at the bare pk and told by nobody that she had been replaced.
|
|
284
|
-
t('a ward has one public being: a second is refused, null takes the mark off, and she keeps everything she held', async () => {
|
|
285
|
-
const { w, alice } = await world();
|
|
286
|
-
const ask = w.owner('A');
|
|
287
|
-
assert.deepEqual(await ask('public', { key: 'A' }), { public: 'A' });
|
|
288
|
-
assert.deepEqual(await ask('public', { key: 'A' }), { public: 'A' }, 'marking the same one again is not a second');
|
|
289
|
-
const bp = (await ask());
|
|
290
|
-
assert.equal(bp.notes.beings.A.public, true, 'and the owner sees which one she is');
|
|
291
|
-
const inv = (await alice.being.invite('kept'));
|
|
292
|
-
assert.deepEqual(await ask('public', { key: null }), { public: null }, 'null takes the mark off');
|
|
293
|
-
const off = (await ask());
|
|
294
|
-
assert.equal(off.notes.beings.A.public, false);
|
|
295
|
-
assert.ok(Object.hasOwn(alice.cells.occupants, 'kept'), 'retiring her is not destroying her: every relation she held is still hers');
|
|
296
|
-
assert.equal(inv.ward, alice.pk);
|
|
297
|
-
});
|
|
298
|
-
// A relation she dropped is nobody's. The word is her own ward's, said to
|
|
299
|
-
// her and never crossing a door, because there is no door left to ask.
|
|
300
|
-
t('a standing she dropped is dropped: the word is hers, nothing crosses, and the far side is never told', async () => {
|
|
301
|
-
const { shop, alice } = await world();
|
|
302
|
-
await alice.being.join(await shop.being.invite());
|
|
303
|
-
const standing = alice.being.standings.shop;
|
|
304
|
-
alice.being.standings.remove('shop');
|
|
305
|
-
assert.deepEqual(alice.cells.standings, {}, 'gone from her cells');
|
|
306
|
-
assert.equal(alice.being.standings.shop, undefined, 'and gone from her stance');
|
|
307
|
-
assert.ok(said(await standing.ask('hello'), 'dropped'), 'a handle she kept over the drop reaches nothing, and says so');
|
|
308
|
-
assert.ok(Object.hasOwn(shop.cells.occupants, 'cust1'), 'the far side holds her seat still: dropping is hers alone and she tells nobody');
|
|
309
|
-
});
|
|
310
|
-
// ---- a being may make
|
|
311
|
-
//
|
|
312
|
-
// `boot` is on every stance, so a kit that never wired it hands its beings a
|
|
313
|
-
// call that answers null for ever and nothing else here would notice.
|
|
314
|
-
t('a being boots one of her ward s classes from inside her own answer, and may ask for a way back to what she made', async () => {
|
|
315
|
-
const { w } = await world();
|
|
316
|
-
const maker = await w.boot('MK', Maker);
|
|
317
|
-
// A harbor holds the classes its device gave it, so one Member is booted
|
|
318
|
-
// to put the class there. What the maker does after that is the ward's.
|
|
319
|
-
await w.boot('MEM', Member);
|
|
320
|
-
assert.deepEqual(await maker.being.open({ class: 'Member', key: 'made-1' }), { made: 'made-1' });
|
|
321
|
-
assert.deepEqual(await maker.being.open({ class: 'Member', key: 'made-1' }), { made: null }, 'a key already taken makes nobody');
|
|
322
|
-
assert.deepEqual(await maker.being.open({ class: 'NoSuchClass', key: 'made-2' }), { made: null }, 'a class the harbor does not hold makes nobody');
|
|
323
|
-
assert.deepEqual(maker.cells.made, ['made-1']);
|
|
324
|
-
// With an id she holds a standing to what she made, and the being made
|
|
325
|
-
// knows her by her own key and by nothing else.
|
|
326
|
-
assert.deepEqual(await maker.being.open({ class: 'Member', key: 'made-3', id: 'mine' }), { made: 'made-3' });
|
|
327
|
-
assert.deepEqual(await maker.being.say({ id: 'mine' }), { said: { pong: 'MK' } }, 'she asks through the relation, and arrives under her own key');
|
|
328
|
-
assert.deepEqual(await maker.being.say({ id: 'nothing' }), { said: null });
|
|
329
|
-
});
|
|
330
|
-
// The count, reached the one way a suite that owns no bytes can reach it:
|
|
331
|
-
// a caller's numbers are in her own partition, so winding them back and
|
|
332
|
-
// standing her ward up again makes her next ask carry a number the far
|
|
333
|
-
// door has already honoured. A kit with no once-only delivery answers it
|
|
334
|
-
// and passes every other chapter here clean.
|
|
335
|
-
kept('a number already honoured is repeated: the far being is not asked twice, and nothing is written for it', async () => {
|
|
336
|
-
const { w, shop, alice } = await world();
|
|
337
|
-
const inv = await shop.being.invite();
|
|
338
|
-
assert.deepEqual(await alice.being.join(inv), { welcome: true });
|
|
339
|
-
assert.deepEqual(await alice.being.buy('a hat'), { ok: true, receipt: 'receipt for a hat' });
|
|
340
|
-
const sold = shop.cells.sales.length;
|
|
341
|
-
// Her keys for the shop, wound back to the number she has just spent.
|
|
342
|
-
const kept = able(w);
|
|
343
|
-
const p = kept.partition('A');
|
|
344
|
-
const keys = p.bind.A.standings;
|
|
345
|
-
const mine = keys.shop;
|
|
346
|
-
const back = mine.seq - 1;
|
|
347
|
-
mine.seq = back;
|
|
348
|
-
await kept.restart('A', p);
|
|
349
|
-
const again = await alice.being.buy('a second hat');
|
|
350
|
-
assert.ok(said(again, 'repeated'), `a number already honoured is repeated, not ${JSON.stringify(again)}`);
|
|
351
|
-
assert.equal(shop.cells.sales.length, sold, 'and she was never asked');
|
|
352
|
-
// The count carries on from where it was: the next number is fresh.
|
|
353
|
-
assert.deepEqual(await alice.being.buy('a third hat'), { ok: true, receipt: 'receipt for a third hat' });
|
|
354
|
-
});
|
|
355
|
-
// A restart is silent by construction: the ward comes back on the same
|
|
356
|
-
// seed with the same pk, and every relation it holds still points at it.
|
|
357
|
-
// What must survive with it is what the door has honoured, or a restart is
|
|
358
|
-
// a stretch of time in which every number can be replayed.
|
|
359
|
-
kept('a restart is silent: same pk, every relation still stands, and the far door still refuses what it spent', async () => {
|
|
360
|
-
const { w, shop, alice } = await world();
|
|
361
|
-
const inv = await shop.being.invite();
|
|
362
|
-
assert.deepEqual(await alice.being.join(inv), { welcome: true });
|
|
363
|
-
assert.deepEqual(await alice.being.buy('a hat'), { ok: true, receipt: 'receipt for a hat' });
|
|
364
|
-
const pk = shop.pk;
|
|
365
|
-
const able2 = able(w);
|
|
366
|
-
const mark = JSON.stringify(able2.partition('S').heirs);
|
|
367
|
-
await able2.restart('S');
|
|
368
|
-
assert.equal(shop.pk, pk, 'a ward that came back changed its pk');
|
|
369
|
-
assert.equal(JSON.stringify(able2.partition('S').heirs), mark, 'and it came back holding what it had honoured');
|
|
370
|
-
assert.deepEqual(await alice.being.buy('a second hat'), { ok: true, receipt: 'receipt for a second hat' }, 'the relation goes on, and she was never told');
|
|
371
|
-
assert.deepEqual(alice.cells.standings.shop.id, 'shop', 'her side was not told either');
|
|
372
|
-
});
|
|
373
|
-
// Notes are the one thing on a record that a being seeds and a ward keeps
|
|
374
|
-
// without ever reading. A kit that files what it was handed rather than a
|
|
375
|
-
// copy of it leaves the seeder a hand inside her cells: it writes without
|
|
376
|
-
// the ward being told, so what a restart brings back is not what she read.
|
|
377
|
-
t('notes are values and are kept as a copy: what the seeder still holds is not what the record holds', async () => {
|
|
378
|
-
const { alice } = await world();
|
|
379
|
-
const seeded = { tier: { deep: 'gold' } };
|
|
380
|
-
assert.ok((await alice.being.invite('noted', seeded)) !== null, 'she minted with notes');
|
|
381
|
-
seeded.tier.deep = 'moved';
|
|
382
|
-
assert.deepEqual(alice.cells.occupants.noted.notes, { tier: { deep: 'gold' } }, 'the record is the copy, all the way down');
|
|
383
|
-
// And notes no harbor could write back are no invitation, for the reason
|
|
384
|
-
// every other write is refused: a record it could not keep.
|
|
385
|
-
assert.equal(await alice.being.invite('bad-notes', { n: 0 / 0 }), null);
|
|
386
|
-
assert.equal(Object.hasOwn(alice.cells.occupants, 'bad-notes'), false, 'and nothing was written for it');
|
|
387
|
-
});
|
|
388
|
-
// The wait, and the one thing a kit can get wrong that nothing else here
|
|
389
|
-
// would notice. A ward bounds every ask itself, so a relation cycle ends:
|
|
390
|
-
// A asks B, B asking back holds A's lane, and the third ask can never be
|
|
391
|
-
// answered. A kit with no bound waits for ever and hangs, and every other
|
|
392
|
-
// chapter in this suite would still pass it. The word is `late`, which
|
|
393
|
-
// promises nothing either way, and the relation lives afterwards.
|
|
394
|
-
t('the wait is bounded, so a cycle ends: the ask that cannot be answered is late, and the relation lives', async () => {
|
|
395
|
-
const { w } = await world();
|
|
396
|
-
const a = await w.boot('MA', Member);
|
|
397
|
-
const b = await w.boot('MB', Member);
|
|
398
|
-
const toB = (await b.being.invite('from-a'));
|
|
399
|
-
const toA = (await a.being.invite('from-b'));
|
|
400
|
-
assert.ok(answered(await a.being.knock(toB, 'ping')), 'a knocks b');
|
|
401
|
-
assert.equal(await a.being.take('b', toB), 'b');
|
|
402
|
-
assert.ok(answered(await b.being.knock(toA, 'ping')), 'b knocks a');
|
|
403
|
-
assert.equal(await b.being.take('a', toA), 'a');
|
|
404
|
-
// A asks B; B, answering, asks A back; A, answering that, asks B on the
|
|
405
|
-
// lane the first ask still holds. Every link is a legal ask.
|
|
406
|
-
const out = (await a.being.relay({ id: 'b', method: 'relay', args: { id: 'a', method: 'relay', args: { id: 'b', method: 'ping' }, time: 200 } }));
|
|
407
|
-
assert.equal(out.said, null, 'the outer ask was answered');
|
|
408
|
-
assert.equal(out.out?.said, 'late', 'and what it carries is the ask that could not be, said as late');
|
|
409
|
-
// The relation is not the worse for it: the lane is free again and the
|
|
410
|
-
// count moved on, which is what a bound is for.
|
|
411
|
-
const after = (await a.being.relay({ id: 'b', method: 'ping' }));
|
|
412
|
-
assert.equal(after.said, null, 'and she asks again on the same standing');
|
|
413
|
-
});
|
|
414
|
-
// Lend is boot's other half: what the device already has, reached the one
|
|
415
|
-
// way anything is reached. The device is one standing the ward holds at
|
|
416
|
-
// the box's being, and a lend is an ask on it; the being lent stands in
|
|
417
|
-
// a ward the harbor roots, so nothing here can look at her from the
|
|
418
|
-
// outside, and what the ward owes is what is asserted. She is handed an
|
|
419
|
-
// id, the standing is an ordinary one, and the invitation is nowhere she
|
|
420
|
-
// can read.
|
|
421
|
-
lends('what the device lends is a being: she is handed an id, asks on an ordinary standing, and never sees the invitation', async () => {
|
|
422
|
-
const w = await make({ lends: { pen: 'Member' } });
|
|
423
|
-
const maker = await w.boot('MK', Maker);
|
|
424
|
-
assert.deepEqual(await maker.being.borrow({ name: 'pen', id: 'pen' }), { lent: 'pen' }, 'the id she gave is the id she gets');
|
|
425
|
-
// An ordinary standing from there on: she asks on it, and arrives at that
|
|
426
|
-
// being's door as somebody the far side named.
|
|
427
|
-
const said = (await maker.being.say({ id: 'pen' }));
|
|
428
|
-
assert.equal(typeof said.said?.pong, 'string', 'she arrived under an id the far being minted');
|
|
429
|
-
// The keys are the ward's. Nothing of the relation is in her cells, which
|
|
430
|
-
// is the whole difference between this standing and one she gave away.
|
|
431
|
-
const c = w.census();
|
|
432
|
-
const heir = c.binds.MK.standings.pen.heir;
|
|
433
|
-
assert.equal(typeof heir, 'string', 'the ward bound a heir for it');
|
|
434
|
-
assert.equal(JSON.stringify(c.cells.MK).includes(heir), false, 'and the invitation reached her nowhere');
|
|
435
|
-
// Null is every kind of no, and she cannot tell them apart.
|
|
436
|
-
assert.deepEqual(await maker.being.borrow({ name: 'gps', id: 'x' }), { lent: null }, 'a name this box does not lend');
|
|
437
|
-
assert.deepEqual(await maker.being.borrow({ name: 'pen', id: 'pen' }), { lent: null }, 'an id she already holds');
|
|
438
|
-
assert.deepEqual(await maker.being.borrow({ name: 'pen', id: 'OWNER' }), { lent: null }, 'a word the ward reserves');
|
|
439
|
-
});
|
|
440
|
-
lends('a harbor with nothing to lend lends nothing, and says so the same way', async () => {
|
|
441
|
-
const w = await make();
|
|
442
|
-
const maker = await w.boot('MK', Maker);
|
|
443
|
-
assert.deepEqual(await maker.being.borrow({ name: 'pen', id: 'pen' }), { lent: null });
|
|
444
|
-
assert.equal(w.census().binds.MK.standings.pen, undefined, 'and nothing was written for it');
|
|
445
|
-
});
|
|
446
|
-
// The case the estate's walk reaches by weight and this reaches by hand: a
|
|
447
|
-
// seat kicked after it answered and before it was taken. She holds a key
|
|
448
|
-
// the door bound for her, so she is the one person who hears why, and she
|
|
449
|
-
// hears it every time. A kit that forgot to keep the last keys of a
|
|
450
|
-
// forgotten heir answers her silence, which is what it says to a stranger,
|
|
451
|
-
// and she cannot tell being kicked from never having been let in.
|
|
452
|
-
t('a relation kicked between the knock and the take: she hears removed, every time, and the take births nothing', async () => {
|
|
453
|
-
const { shop, alice, bob } = await world();
|
|
454
|
-
const inv = await shop.being.invite();
|
|
455
|
-
assert.deepEqual(await alice.being.knock(inv, 'hello'), { welcome: true }, 'she knocked and was answered');
|
|
456
|
-
shop.being.occupants.remove('cust1'); // kicked, and she is not told
|
|
457
|
-
for (let i = 0; i < 3; i++)
|
|
458
|
-
assert.ok(said(await alice.being.knock(inv, 'hello'), 'removed'), `knock ${i}: a spent heir stays spent, and she hears why every time`);
|
|
459
|
-
// And the take still births one: it reads the knock record, which says
|
|
460
|
-
// she was answered, and the far side kicking her afterwards is not
|
|
461
|
-
// something her side can know or should guess at. The standing is hers,
|
|
462
|
-
// and it is the standing that hears removed from then on. A kit that
|
|
463
|
-
// refused the take here would leave her with no record to read the
|
|
464
|
-
// refusal through, and nothing to drop.
|
|
465
|
-
assert.equal(await alice.being.take('shop', inv), 'shop', 'the take reads the knock record, and the knock was answered');
|
|
466
|
-
assert.ok(said(await alice.being.standings.shop.ask('hello'), 'removed'), 'and the standing is where she hears it now');
|
|
467
|
-
alice.being.standings.remove('shop');
|
|
468
|
-
assert.deepEqual(alice.cells.standings, {}, 'hers to drop, like any other');
|
|
469
|
-
// And only to her: the same invitation in anyone else's hand is silence,
|
|
470
|
-
// because the keys the door kept are hers and nobody else's.
|
|
471
|
-
assert.ok(isSilence(await bob.being.knock(inv, 'hello')), 'a stranger holding the same invitation learns nothing at all');
|
|
472
|
-
});
|
|
473
|
-
t('take without a knock, or after a knock that was not answered, births nothing', async () => {
|
|
474
|
-
const { shop, alice } = await world();
|
|
475
|
-
const inv = await shop.being.invite();
|
|
476
|
-
assert.equal(await alice.being.take('shop', inv), null);
|
|
477
|
-
shop.being.occupants.remove('cust1');
|
|
478
|
-
assert.ok(said(await alice.being.knock(inv, 'hello'), 'removed'));
|
|
479
|
-
assert.equal(await alice.being.take('shop', inv), null);
|
|
480
|
-
assert.deepEqual(alice.cells.standings, {});
|
|
481
|
-
});
|
|
482
|
-
// ---- asks
|
|
483
|
-
t('an occupant asks, a standing is asked, an object comes back', async () => {
|
|
484
|
-
const { alice, printer, shop } = await world();
|
|
485
|
-
await alice.being.join(await shop.being.invite());
|
|
486
|
-
assert.deepEqual(await alice.being.buy('hat'), { ok: true, receipt: 'receipt for hat' });
|
|
487
|
-
assert.equal(printer.cells.jobs.length, 1);
|
|
488
|
-
assert.equal(shop.cells.sales[0].by, 'cust1');
|
|
489
|
-
});
|
|
490
|
-
t('every answered ask rotates: each call speaks as the key the last one announced, and announces a fresh one, all minted by her side', async () => {
|
|
491
|
-
const { w, shop, alice } = await world();
|
|
492
|
-
const inv = await shop.being.invite();
|
|
493
|
-
await alice.being.join(inv);
|
|
494
|
-
const seen = new Set([w.heir(inv).current]);
|
|
495
|
-
let expectNext = w.heir(inv).current; // after the knock, her key is current and nothing is announced
|
|
496
|
-
for (let i = 0; i < 3; i++) {
|
|
497
|
-
await alice.being.buy('hat');
|
|
498
|
-
const h = w.heir(inv);
|
|
499
|
-
assert.equal(h.current, expectNext); // this call spoke as what was announced before
|
|
500
|
-
assert.ok(h.announced && alice.minted.has(h.announced) && !seen.has(h.announced)); // and announced a fresh key
|
|
501
|
-
seen.add(h.announced);
|
|
502
|
-
expectNext = h.announced;
|
|
503
|
-
}
|
|
504
|
-
assert.equal(shop.cells.sales.length, 3);
|
|
505
|
-
assert.equal(seen.size, 4);
|
|
506
|
-
});
|
|
507
|
-
t('asks in flight together on one standing: every one is answered, the rotation stays a single line, and the relation lives', async () => {
|
|
508
|
-
const { w, shop, alice } = await world();
|
|
509
|
-
const inv = await shop.being.invite();
|
|
510
|
-
await alice.being.join(inv);
|
|
511
|
-
const bound = w.heir(inv).current; // the key her side bound at the knock
|
|
512
|
-
const out = await Promise.all(Array.from({ length: 5 }, (_, i) => alice.being.buy(`hat${i}`)));
|
|
513
|
-
for (const [i, o] of out.entries())
|
|
514
|
-
assert.deepEqual(o, { ok: true, receipt: `receipt for hat${i}` }, `ask ${i} was answered`);
|
|
515
|
-
assert.equal(shop.cells.sales.length, 5); // each arrived once. none was lost, none was doubled
|
|
516
|
-
// five asks, five rotations, one line: the door's key is the fifth her
|
|
517
|
-
// side minted, and the side that did not mint it never held it.
|
|
518
|
-
const h = w.heir(inv);
|
|
519
|
-
assert.notEqual(h.current, bound);
|
|
520
|
-
assert.ok(alice.minted.has(h.current));
|
|
521
|
-
assert.ok(!shop.minted.has(h.current));
|
|
522
|
-
// and the two sides still agree, which is the whole of it: a relation
|
|
523
|
-
// that survived the crowd is a relation that still answers.
|
|
524
|
-
assert.deepEqual(await alice.being.buy('after'), { ok: true, receipt: 'receipt for after' });
|
|
525
|
-
});
|
|
526
|
-
t('two knocks in flight on one invitation: one heir, one binding, and she may still ask', async () => {
|
|
527
|
-
const { w, shop, alice } = await world();
|
|
528
|
-
const inv = await shop.being.invite();
|
|
529
|
-
const [a, b] = await Promise.all([alice.being.join(inv), alice.being.join(inv)]);
|
|
530
|
-
// the same knocker knocking twice at once is not two occupants: the heir
|
|
531
|
-
// is spent once, and both calls speak for the one relation she is building.
|
|
532
|
-
assert.deepEqual(Object.keys(shop.cells.occupants), ['cust1']);
|
|
533
|
-
for (const r of [a, b])
|
|
534
|
-
assert.ok(!isSilence(r) && !isUnreached(r), 'both knocks were answered');
|
|
535
|
-
const h = w.heir(inv);
|
|
536
|
-
assert.equal(h.fresh, false);
|
|
537
|
-
assert.ok(alice.minted.has(h.current)); // her own key speaks, exactly as after one knock
|
|
538
|
-
// What she does with the standing afterwards is not this test's. A take
|
|
539
|
-
// reads the relation where the knock left it, and a knock still in flight
|
|
540
|
-
// has not left it anywhere yet: the standing is born a step behind the
|
|
541
|
-
// door, and its first ask speaks under a number already honoured.
|
|
542
|
-
});
|
|
543
|
-
t('an inherited name is not an ask through a ward either: her cells do not come back for the asking', async () => {
|
|
544
|
-
const { shop, alice } = await world();
|
|
545
|
-
await alice.being.join(await shop.being.invite()); // plain: refund is hidden from her
|
|
546
|
-
shop.cells.ledger = { swiss: 'CH-99' };
|
|
547
|
-
for (const name of ['valueOf', 'toString', 'constructor', '__proto__', 'hasOwnProperty']) {
|
|
548
|
-
const out = await alice.being.standings.shop.ask(name);
|
|
549
|
-
assert.deepEqual(out, { error: 'unknown ask' }, name);
|
|
550
|
-
assert.ok(!JSON.stringify(out).includes('CH-99'), `${name} carried her cells out`);
|
|
551
|
-
}
|
|
552
|
-
// the blueprint never named one of them, and the gate is the same gate
|
|
553
|
-
const blueprint = bp(await alice.being.learn());
|
|
554
|
-
for (const ask of blueprint.asks)
|
|
555
|
-
assert.ok(!['valueOf', 'toString', 'constructor'].includes(ask.name));
|
|
556
|
-
assert.deepEqual(await alice.being.standings.shop.ask('refund'), { error: 'unknown ask' }); // still hidden, still the same answer
|
|
557
|
-
});
|
|
558
|
-
t('symmetry: the customer offers a way back inside args; the shop takes a gold one', async () => {
|
|
559
|
-
const { shop, alice, bob } = await world();
|
|
560
|
-
await alice.being.join(await shop.being.invite('gold'));
|
|
561
|
-
await bob.being.join(await shop.being.invite('plain'));
|
|
562
|
-
assert.deepEqual(Object.keys(shop.cells.standings), ['prn', 'vip-cust1']);
|
|
563
|
-
assert.deepEqual(alice.cells.heard, [{ from: 'shop-back', method: 'hi' }]);
|
|
564
|
-
assert.deepEqual(bob.cells.heard, []);
|
|
565
|
-
assert.deepEqual(await shop.being.standings['vip-cust1'].ask('hi'), { heard: 'hi' });
|
|
566
|
-
});
|
|
567
|
-
t('the printer never invited the shop: no standing, an error object, no throw', async () => {
|
|
568
|
-
const { alice, shop } = await world({ printerInvitesShop: false });
|
|
569
|
-
await alice.being.join(await shop.being.invite());
|
|
570
|
-
assert.deepEqual(await alice.being.buy('hat'), { error: 'no printer' });
|
|
571
|
-
});
|
|
572
|
-
t('the empty ask is describe; shaped per asker', async () => {
|
|
573
|
-
const { shop, alice, bob } = await world();
|
|
574
|
-
await alice.being.join(await shop.being.invite('gold'));
|
|
575
|
-
await bob.being.join(await shop.being.invite());
|
|
576
|
-
const names = (x) => bp(x).asks.map((a) => a.name);
|
|
577
|
-
assert.deepEqual(names(await alice.being.learn()), ['hello', 'buy', 'refund']);
|
|
578
|
-
assert.deepEqual(names(await bob.being.learn()), ['hello', 'buy']);
|
|
579
|
-
assert.deepEqual(await alice.being.standings.shop.ask('refund'), { ok: true }, 'and what she is shown she can ask');
|
|
580
|
-
});
|
|
581
|
-
t('the digest is per relation: two occupants, two digests of one being, both true', async () => {
|
|
582
|
-
const { shop, alice, bob } = await world();
|
|
583
|
-
await alice.being.join(await shop.being.invite('gold'));
|
|
584
|
-
await bob.being.join(await shop.being.invite());
|
|
585
|
-
await alice.being.learn();
|
|
586
|
-
await bob.being.learn();
|
|
587
|
-
assert.notEqual(alice.cells.standings.shop.digest, bob.cells.standings.shop.digest);
|
|
588
|
-
});
|
|
589
|
-
t('describe caches blueprint and digest in the asker cells', async () => {
|
|
590
|
-
const { shop, alice } = await world();
|
|
591
|
-
await alice.being.join(await shop.being.invite());
|
|
592
|
-
const out = await alice.being.learn();
|
|
593
|
-
assert.deepEqual(alice.cells.standings.shop.blueprint, out);
|
|
594
|
-
assert.equal(alice.cells.standings.shop.digest, await digest(out));
|
|
595
|
-
});
|
|
596
|
-
t('a changed standing is noticed by digest, without carrying the blueprint', async () => {
|
|
597
|
-
const { alice, printer, shop } = await world();
|
|
598
|
-
await alice.being.join(await shop.being.invite());
|
|
599
|
-
await shop.being.standings.prn.ask();
|
|
600
|
-
printer.cells.asks.push({ name: 'scan', input: {} }); // she changed
|
|
601
|
-
await alice.being.buy('hat');
|
|
602
|
-
const st = shop.cells.standings.prn;
|
|
603
|
-
assert.notEqual(st.seen, st.digest);
|
|
604
|
-
assert.equal(shop.cells.occupants.cust1.notes.printerChanged, true);
|
|
605
|
-
});
|
|
606
|
-
t('the digest rides on every answer: seen updates without a describe from her side', async () => {
|
|
607
|
-
const { alice, printer, shop } = await world();
|
|
608
|
-
await alice.being.join(await shop.being.invite());
|
|
609
|
-
let describes = 0;
|
|
610
|
-
const real = printer.being.answer.bind(printer.being);
|
|
611
|
-
printer.being.answer = (a, m, x) => (m === undefined && describes++, real(a, m, x));
|
|
612
|
-
await alice.being.buy('hat'); // shop asks printer once
|
|
613
|
-
const st = shop.cells.standings.prn;
|
|
614
|
-
assert.equal(st.seen, await digest((await real({ id: 'shop' }))));
|
|
615
|
-
assert.equal(describes, 1); // the far ward ran her describe, in process, once per answer
|
|
616
|
-
assert.equal(st.digest, null); // she never fetched it herself
|
|
617
|
-
});
|
|
618
|
-
t('a silent refresh leaves seen untouched', async () => {
|
|
619
|
-
const { alice, printer, shop } = await world();
|
|
620
|
-
await alice.being.join(await shop.being.invite());
|
|
621
|
-
await shop.being.standings.prn.ask();
|
|
622
|
-
const before = shop.cells.standings.prn.seen;
|
|
623
|
-
const real = printer.being.answer.bind(printer.being);
|
|
624
|
-
printer.being.answer = async (a, m, x) => (m === undefined ? silence : real(a, m, x));
|
|
625
|
-
await alice.being.buy('hat');
|
|
626
|
-
assert.equal(shop.cells.standings.prn.seen, before);
|
|
627
|
-
});
|
|
628
|
-
down('unreached is the ward word, distinct from silence, carries nothing, safe to retry', async () => {
|
|
629
|
-
const { alice, printer, shop } = await world();
|
|
630
|
-
await alice.being.join(await shop.being.invite());
|
|
631
|
-
printer.down = true;
|
|
632
|
-
assert.deepEqual(await alice.being.buy('hat'), { error: 'printer unreachable', retry: true });
|
|
633
|
-
const out = await shop.being.standings.prn.ask('print', { doc: 'x' });
|
|
634
|
-
assert.ok(isUnreached(out) && !isSilence(out));
|
|
635
|
-
assert.deepEqual(Object.keys(out), []);
|
|
636
|
-
printer.down = false;
|
|
637
|
-
assert.deepEqual(await alice.being.buy('hat'), { ok: true, receipt: 'receipt for hat' });
|
|
638
|
-
});
|
|
639
|
-
t('a removed occupant hears removed under the key the door bound, her heir is forgotten; a dropped standing is gone', async () => {
|
|
640
|
-
const { w, alice, shop } = await world();
|
|
641
|
-
const inv = await shop.being.invite();
|
|
642
|
-
await alice.being.join(inv);
|
|
643
|
-
shop.being.occupants.remove('cust1');
|
|
644
|
-
assert.equal(w.heir(inv), undefined);
|
|
645
|
-
assert.ok(said(await alice.being.buy('hat'), 'removed'));
|
|
646
|
-
alice.being.standings.remove('shop');
|
|
647
|
-
assert.equal(alice.being.standings.shop, undefined);
|
|
648
|
-
assert.ok(isSilence(await alice.being.knock(inv, 'hello'))); // her bound key went with the standing: a re-knock is as the heir, which is spent, a stranger
|
|
649
|
-
const again = await shop.being.invite(); // cust2. a new heir
|
|
650
|
-
assert.notEqual(again.heir, inv.heir);
|
|
651
|
-
assert.deepEqual(await alice.being.knock(again, 'hello'), { welcome: true });
|
|
652
|
-
});
|
|
653
|
-
t('dropping a standing while an ask on it is in flight does not crash', async () => {
|
|
654
|
-
const { alice, shop } = await world();
|
|
655
|
-
await alice.being.join(await shop.being.invite());
|
|
656
|
-
const p = alice.being.buy('hat');
|
|
657
|
-
alice.being.standings.remove('shop');
|
|
658
|
-
assert.deepEqual(await p, { ok: true, receipt: 'receipt for hat' });
|
|
659
|
-
assert.equal(shop.cells.sales.length, 1);
|
|
660
|
-
});
|
|
661
|
-
t('a being that throws is the word threw to whom the door has bound; the ward stays up', async () => {
|
|
662
|
-
const w = await make();
|
|
663
|
-
class Bomb {
|
|
664
|
-
s;
|
|
665
|
-
constructor(s) {
|
|
666
|
-
this.s = s;
|
|
667
|
-
}
|
|
668
|
-
answer(_, m) {
|
|
669
|
-
if (m === undefined)
|
|
670
|
-
return { asks: [], notes: {} };
|
|
671
|
-
throw new Error('boom');
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
const x = await w.boot('X', Bomb);
|
|
675
|
-
const a = await w.boot('A', Customer);
|
|
676
|
-
const inv = (await x.being.s.occupants.invite('a'));
|
|
677
|
-
assert.ok(said(await a.being.knock(inv, 'any'), 'threw'));
|
|
678
|
-
assert.deepEqual(await a.being.knock(inv), { asks: [], notes: {} });
|
|
679
|
-
});
|
|
680
|
-
t('null is an answer, silence is not', async () => {
|
|
681
|
-
const w = await make();
|
|
682
|
-
class Nul {
|
|
683
|
-
s;
|
|
684
|
-
constructor(s) {
|
|
685
|
-
this.s = s;
|
|
686
|
-
}
|
|
687
|
-
answer(_, m) {
|
|
688
|
-
return m === undefined ? { asks: [], notes: {} } : null;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const n = await w.boot('N', Nul);
|
|
692
|
-
const a = await w.boot('A', Customer);
|
|
693
|
-
const out = await a.being.knock((await n.being.s.occupants.invite('a')), 'x');
|
|
694
|
-
assert.equal(out, null);
|
|
695
|
-
assert.ok(!isSilence(out));
|
|
696
|
-
});
|
|
697
|
-
t('the raw shape is a being through a ward, exactly like one on the base class', async () => {
|
|
698
|
-
const w = await make();
|
|
699
|
-
const e = await w.boot('E', Echo);
|
|
700
|
-
const a = await w.boot('A', Customer);
|
|
701
|
-
const inv = (await e.being.s.occupants.invite('a'));
|
|
702
|
-
assert.deepEqual(await a.being.knock(inv, 'echo', { x: 1 }), { from: 'a', x: 1 });
|
|
703
|
-
assert.equal(await a.being.take('echo', inv), 'echo');
|
|
704
|
-
assert.deepEqual(await a.being.standings.echo.ask('echo', { y: 2 }), { from: 'a', y: 2 });
|
|
705
|
-
assert.equal(a.cells.standings.echo.seen, await digest({ asks: [{ name: 'echo', input: {} }], notes: {} }));
|
|
706
|
-
});
|
|
707
|
-
// ---- a save the store refuses
|
|
708
|
-
// The door has already said yes by the time a store says no, so the answer
|
|
709
|
-
// is the last thing that can still carry the refusal. A being whose row was
|
|
710
|
-
// not kept has a memory running ahead of what is on the device, and every
|
|
711
|
-
// door that kept answering success would be telling an asker about writes
|
|
712
|
-
// that are gone at the next reload.
|
|
713
|
-
refused('a save the store refuses is the answer of the arrival that wrote it: threw at the door, silence at the ask pointer', async () => {
|
|
714
|
-
const w = await make();
|
|
715
|
-
const no = picky(w);
|
|
716
|
-
const printer = await w.boot('P', Printer);
|
|
717
|
-
const alice = await w.boot('A', Customer);
|
|
718
|
-
const inv = (await printer.being.invite('a'));
|
|
719
|
-
assert.deepEqual(await alice.being.knock(inv, 'print', { doc: 'one' }), { printed: 'one' });
|
|
720
|
-
no.refuse('P', true);
|
|
721
|
-
const out = await alice.being.knock(inv, 'print', { doc: 'two' });
|
|
722
|
-
assert.ok(said(out, 'threw'), `a refused save is threw to a key the door holds, not ${JSON.stringify(out)}`);
|
|
723
|
-
const owned = await no.owner('P')('invite', { being: 'P', id: 'later' });
|
|
724
|
-
assert.ok(isSilence(owned), `a refused save is silence at the ask pointer, not ${JSON.stringify(owned)}`);
|
|
725
|
-
// The ward is up, and a stranger hears nothing about the store either.
|
|
726
|
-
assert.ok(isSilence(await alice.being.knock({ ward: printer.pk, heir: 'f'.repeat(64), secret: 'f'.repeat(64) }, 'print')));
|
|
727
|
-
no.refuse('P', false);
|
|
728
|
-
assert.deepEqual(await alice.being.knock(inv, 'print', { doc: 'three' }), { printed: 'three' }, 'the store took it, so the arrival is answered');
|
|
729
|
-
});
|
|
730
|
-
// The caller's own row, refused while she is in the middle of a call. A
|
|
731
|
-
// knock is one call and two halves: she writes her side before the bytes go
|
|
732
|
-
// out and again when they come back, and the far door writes the heir it
|
|
733
|
-
// spent. What a refusal puts back is what she wrote and never what the
|
|
734
|
-
// relation stands on, because her keys and the door's rotate at different
|
|
735
|
-
// moments and neither holds the seed of a key it has moved past: a kit that
|
|
736
|
-
// rolls back either speaks under a key the other will not admit, and that
|
|
737
|
-
// relation never speaks again. The proof is that she is still heard
|
|
738
|
-
// afterwards, and it holds wherever the far half lives, because a kit that
|
|
739
|
-
// put the relation back would fail here with both beings of one ward and
|
|
740
|
-
// with the far one on another device alike.
|
|
741
|
-
refused('a refusal under a call puts back the whole call, and the relation speaks again after it', async () => {
|
|
742
|
-
const w = await make();
|
|
743
|
-
const no = picky(w);
|
|
744
|
-
const printer = await w.boot('P', Printer);
|
|
745
|
-
const alice = await w.boot('A', Customer);
|
|
746
|
-
const inv = (await printer.being.invite('a'));
|
|
747
|
-
assert.deepEqual(await alice.being.knock(inv, 'print', { doc: 'one' }), { printed: 'one' });
|
|
748
|
-
no.refuse('A', true);
|
|
749
|
-
await alice.being.knock(inv, 'print', { doc: 'two' });
|
|
750
|
-
no.refuse('A', false);
|
|
751
|
-
assert.deepEqual(await alice.being.knock(inv, 'print', { doc: 'three' }), { printed: 'three' }, 'the relation is whole: a half put back would never be heard again');
|
|
752
|
-
});
|
|
753
|
-
// ---- public
|
|
754
|
-
t('a public being is asked by anyone as {}; a knock on her needs no heir and may be taken; a ward with none is silence; an unknown pk is unreached', async () => {
|
|
755
|
-
const w = await make();
|
|
756
|
-
// A being written by hand: no base class, one answer, and the stance is
|
|
757
|
-
// all she is handed. She is a being to the ward on those terms alone.
|
|
758
|
-
class Pub {
|
|
759
|
-
s;
|
|
760
|
-
constructor(s) {
|
|
761
|
-
this.s = s;
|
|
762
|
-
this.s.cells.askers ??= [];
|
|
763
|
-
}
|
|
764
|
-
answer(asker, m, args) {
|
|
765
|
-
if (m === undefined)
|
|
766
|
-
return { asks: [{ name: 'time', input: {} }], notes: {} };
|
|
767
|
-
this.s.cells.askers.push(asker);
|
|
768
|
-
return { time: 12, got: args ?? {} };
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
const pub = await w.boot('PUB', Pub, {}, { isPublic: true });
|
|
772
|
-
const priv = await w.boot('PRIV', Pub); // same class, not public
|
|
773
|
-
const a = await w.boot('A', Customer);
|
|
774
|
-
const b = await w.boot('B', Customer);
|
|
775
|
-
const at = (h) => ({ ward: h.pk });
|
|
776
|
-
assert.deepEqual(await a.being.knock(at(pub), 'time', { x: 1 }), { time: 12, got: { x: 1 } });
|
|
777
|
-
assert.deepEqual(await b.being.knock(at(pub), 'time'), { time: 12, got: {} });
|
|
778
|
-
assert.deepEqual(pub.cells.askers, [{}, {}]);
|
|
779
|
-
assert.deepEqual(pub.cells.occupants, {}); // she never invited anyone
|
|
780
|
-
if (!w.oneWard)
|
|
781
|
-
assert.ok(isSilence(await a.being.knock(at(priv), 'time'))); // a ward with no public being: silence
|
|
782
|
-
assert.ok(isUnreached(await a.being.knock({ ward: 'a'.repeat(128) }, 'time'))); // no door anywhere: unreached
|
|
783
|
-
assert.ok(said(await a.being.knock({ ward: 'not a pk' }, 'time'), 'invitation')); // not even an address
|
|
784
|
-
assert.equal(await a.being.take('clock', at(pub)), 'clock');
|
|
785
|
-
assert.deepEqual(await a.being.standings.clock.ask('time'), { time: 12, got: {} });
|
|
786
|
-
assert.equal(a.cells.standings.clock.seen, await digest((await a.being.standings.clock.ask())));
|
|
787
|
-
});
|
|
788
|
-
// ---- keys stay where they belong
|
|
789
|
-
t("two rotations: the heir is the inviter's, the bound key is the knocker's, and neither side minted the other's", async () => {
|
|
790
|
-
const { w, shop, alice, bob } = await world();
|
|
791
|
-
const i1 = await shop.being.invite('gold');
|
|
792
|
-
const i2 = await shop.being.invite();
|
|
793
|
-
assert.ok(shop.minted.has(i1.heir) && shop.minted.has(i2.heir));
|
|
794
|
-
await alice.being.join(i1);
|
|
795
|
-
await bob.being.join(i2);
|
|
796
|
-
const h1 = w.heir(i1), h2 = w.heir(i2);
|
|
797
|
-
assert.ok(alice.minted.has(h1.current) && !shop.minted.has(h1.current) && !bob.minted.has(h1.current));
|
|
798
|
-
assert.ok(bob.minted.has(h2.current) && !shop.minted.has(h2.current) && !alice.minted.has(h2.current));
|
|
799
|
-
assert.notEqual(h1.current, h2.current); // no key serves two relations
|
|
800
|
-
assert.ok(!alice.minted.has(i1.heir)); // alice never minted anything of the shop's
|
|
801
|
-
});
|
|
802
|
-
t('her cells carry no key: heir, secret, and bound key live in the bind table only', async () => {
|
|
803
|
-
const { w, shop, alice } = await world();
|
|
804
|
-
const inv = await shop.being.invite();
|
|
805
|
-
await alice.being.join(inv);
|
|
806
|
-
await alice.being.learn();
|
|
807
|
-
const words = [inv.heir, inv.secret, w.heir(inv).current];
|
|
808
|
-
for (const cells of [shop.cells, alice.cells])
|
|
809
|
-
for (const word of words)
|
|
810
|
-
assert.ok(!JSON.stringify(cells).includes(word), `${word} in cells`);
|
|
811
|
-
});
|
|
812
|
-
// ---- the estate
|
|
813
|
-
estate(label, make, { canDown, test });
|
|
814
|
-
}
|
|
815
|
-
// The store and reach suites, beside the ward's: every store a harbor keeps
|
|
816
|
-
// wards in and every reach it carries bytes by passes its own, on the
|
|
817
|
-
// terrain it is written for.
|
|
818
|
-
export { conformStore } from './store.js';
|
|
819
|
-
export { conformReach } from './reach.js';
|