@agoric/async-flow 0.1.1-dev-16095c5.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/LICENSE +201 -0
  3. package/README.md +40 -0
  4. package/docs/async-flow-states.key +0 -0
  5. package/docs/async-flow-states.md +15 -0
  6. package/docs/async-flow-states.png +0 -0
  7. package/index.d.ts +2 -0
  8. package/index.d.ts.map +1 -0
  9. package/index.js +1 -0
  10. package/package.json +67 -0
  11. package/src/async-flow.d.ts +87 -0
  12. package/src/async-flow.d.ts.map +1 -0
  13. package/src/async-flow.js +502 -0
  14. package/src/bijection.d.ts +28 -0
  15. package/src/bijection.d.ts.map +1 -0
  16. package/src/bijection.js +132 -0
  17. package/src/convert.d.ts +5 -0
  18. package/src/convert.d.ts.map +1 -0
  19. package/src/convert.js +131 -0
  20. package/src/ephemera.d.ts +2 -0
  21. package/src/ephemera.d.ts.map +1 -0
  22. package/src/ephemera.js +35 -0
  23. package/src/equate.d.ts +2 -0
  24. package/src/equate.d.ts.map +1 -0
  25. package/src/equate.js +123 -0
  26. package/src/log-store.d.ts +25 -0
  27. package/src/log-store.d.ts.map +1 -0
  28. package/src/log-store.js +165 -0
  29. package/src/replay-membrane.d.ts +71 -0
  30. package/src/replay-membrane.d.ts.map +1 -0
  31. package/src/replay-membrane.js +435 -0
  32. package/src/type-guards.d.ts +4 -0
  33. package/src/type-guards.d.ts.map +1 -0
  34. package/src/type-guards.js +54 -0
  35. package/src/types.d.ts +70 -0
  36. package/src/types.d.ts.map +1 -0
  37. package/src/types.js +164 -0
  38. package/test/async-flow-crank.test.js +96 -0
  39. package/test/async-flow-no-this.js +59 -0
  40. package/test/async-flow.test.js +380 -0
  41. package/test/bad-host.test.js +205 -0
  42. package/test/bijection.test.js +118 -0
  43. package/test/convert.test.js +127 -0
  44. package/test/equate.test.js +116 -0
  45. package/test/log-store.test.js +112 -0
  46. package/test/prepare-test-env-ava.js +28 -0
  47. package/test/replay-membrane-settlement.test.js +154 -0
  48. package/test/replay-membrane-zombie.test.js +158 -0
  49. package/test/replay-membrane.test.js +271 -0
  50. package/tsconfig.build.json +11 -0
  51. package/tsconfig.json +13 -0
  52. package/typedoc.json +8 -0
@@ -0,0 +1,205 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ } from './prepare-test-env-ava.js';
8
+
9
+ import { Fail } from '@endo/errors';
10
+ import { M } from '@endo/patterns';
11
+ import { makePromiseKit } from '@endo/promise-kit';
12
+ import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
13
+ import { prepareVowTools } from '@agoric/vow';
14
+ import { makeHeapZone } from '@agoric/zone/heap.js';
15
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
16
+ import { makeDurableZone } from '@agoric/zone/durable.js';
17
+
18
+ import { prepareAsyncFlowTools } from '../src/async-flow.js';
19
+
20
+ const nonPassableFunc = () => 'non-passable-function';
21
+ harden(nonPassableFunc);
22
+ const guestCreatedPromise = harden(Promise.resolve('guest-created'));
23
+ let badResult;
24
+
25
+ /**
26
+ * @param {Zone} zone
27
+ */
28
+ const prepareBadHost = zone =>
29
+ zone.exoClass(
30
+ 'BadHost',
31
+ M.interface('BadHost', {}, { defaultGuards: 'raw' }),
32
+ () => ({}),
33
+ {
34
+ badMethod(_badArg = undefined) {
35
+ return badResult;
36
+ },
37
+ },
38
+ );
39
+
40
+ /** @typedef {ReturnType<ReturnType<prepareBadHost>>} BadHost */
41
+
42
+ /**
43
+ * @param {any} t
44
+ * @param {Zone} zone
45
+ */
46
+ const testBadHostFirstPlay = async (t, zone) => {
47
+ t.log('badHost firstPlay started');
48
+ const vowTools = prepareVowTools(zone);
49
+ const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
50
+ vowTools,
51
+ });
52
+ const makeBadHost = prepareBadHost(zone);
53
+ const { makeVowKit } = vowTools;
54
+
55
+ const { vow: v1, resolver: _r1 } = zone.makeOnce('v1', () => makeVowKit());
56
+ // purposely violate rule that guestMethod is closed.
57
+ const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
58
+
59
+ const { guestMethod } = {
60
+ async guestMethod(badGuest, _p1) {
61
+ // nothing bad yet baseline
62
+ t.is(badGuest.badMethod(), undefined);
63
+
64
+ t.throws(() => badGuest.badMethod(guestCreatedPromise), {
65
+ message:
66
+ 'In a Failed state: see getFailures() or getOptFatalProblem() for more information',
67
+ });
68
+
69
+ resolveStep(true);
70
+ t.log(' badHost firstPlay about to return "bogus"');
71
+ // Must not settle outcomeVow
72
+ return 'bogus';
73
+ },
74
+ };
75
+
76
+ const wrapperFunc = asyncFlow(zone, 'AsyncFlow1', guestMethod);
77
+
78
+ const badHost = zone.makeOnce('badHost', () => makeBadHost());
79
+
80
+ const outcomeV = zone.makeOnce('outcomeV', () => wrapperFunc(badHost, v1));
81
+
82
+ const flow = adminAsyncFlow.getFlowForOutcomeVow(outcomeV);
83
+ await promiseStep;
84
+
85
+ const fatalProblem = flow.getOptFatalProblem();
86
+ t.throws(
87
+ () => {
88
+ throw fatalProblem;
89
+ },
90
+ {
91
+ message: '[3]: [0]: cannot yet send guest promises "[Promise]"',
92
+ },
93
+ );
94
+
95
+ t.deepEqual(flow.dump(), [
96
+ ['checkCall', badHost, 'badMethod', [], 0],
97
+ ['doReturn', 0, undefined],
98
+ // Notice that the bad call was not recorded in the log
99
+ ]);
100
+ t.log('badHost firstPlay done');
101
+ };
102
+
103
+ /**
104
+ * @param {any} t
105
+ * @param {Zone} zone
106
+ */
107
+ const testBadHostReplay1 = async (t, zone) => {
108
+ t.log('badHost replay1 started');
109
+ const vowTools = prepareVowTools(zone);
110
+ const { asyncFlow, adminAsyncFlow } = prepareAsyncFlowTools(zone, {
111
+ vowTools,
112
+ });
113
+ prepareBadHost(zone);
114
+
115
+ // const { vow: v1, resolver: r1 } = zone.makeOnce('v1', () => Fail`need v1`);
116
+ // purposely violate rule that guestMethod is closed.
117
+ const { promise: promiseStep, resolve: resolveStep } = makePromiseKit();
118
+
119
+ const { guestMethod } = {
120
+ async guestMethod(badGuest, p1) {
121
+ // nothing bad yet baseline
122
+ t.is(badGuest.badMethod(), undefined);
123
+
124
+ // purposely violate rule that guestMethod is closed.
125
+ badResult = nonPassableFunc;
126
+
127
+ let gErr;
128
+ try {
129
+ badGuest.badMethod();
130
+ } catch (err) {
131
+ gErr = err;
132
+ }
133
+ t.throws(
134
+ () => {
135
+ throw gErr;
136
+ },
137
+ {
138
+ message:
139
+ 'converting badMethod result: Remotables must be explicitly declared: "[Function nonPassableFunc]"',
140
+ },
141
+ );
142
+ t.log(' badHost replay1 guest error caused by host error', gErr);
143
+
144
+ // show that flow is not Failed by host error
145
+ badResult = 'fine';
146
+ t.is(badGuest.badMethod(), 'fine');
147
+
148
+ resolveStep(true);
149
+ t.log(' badHost replay1 to hang awaiting p1');
150
+ // awaiting a promise that won't be resolved until next incarnation
151
+ await p1;
152
+ t.fail('must not reach here in replay 1');
153
+ },
154
+ };
155
+
156
+ asyncFlow(zone, 'AsyncFlow1', guestMethod);
157
+
158
+ const badHost = zone.makeOnce('badHost', () => Fail`need badHost`);
159
+
160
+ const outcomeV = zone.makeOnce('outcomeV', () => Fail`need outcomeV`);
161
+
162
+ const flow = adminAsyncFlow.getFlowForOutcomeVow(outcomeV);
163
+ await promiseStep;
164
+
165
+ const logDump = flow.dump();
166
+
167
+ t.deepEqual(logDump, [
168
+ ['checkCall', badHost, 'badMethod', [], 0],
169
+ ['doReturn', 0, undefined],
170
+ ['checkCall', badHost, 'badMethod', [], 2],
171
+ [
172
+ 'doThrow',
173
+ 2,
174
+ Error(
175
+ 'converting badMethod result: Remotables must be explicitly declared: "[Function nonPassableFunc]"',
176
+ ),
177
+ ],
178
+ ['checkCall', badHost, 'badMethod', [], 4],
179
+ ['doReturn', 4, 'fine'],
180
+ ]);
181
+ t.log('badHost replay1 done');
182
+ };
183
+
184
+ test.serial('test heap async-flow bad host', async t => {
185
+ const zone = makeHeapZone('heapRoot');
186
+ return testBadHostFirstPlay(t, zone);
187
+ });
188
+
189
+ test.serial('test virtual async-flow bad host', async t => {
190
+ annihilate();
191
+ const zone = makeVirtualZone('virtualRoot');
192
+ return testBadHostFirstPlay(t, zone);
193
+ });
194
+
195
+ test.serial('test durable async-flow bad host', async t => {
196
+ annihilate();
197
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
198
+ await testBadHostFirstPlay(t, zone1);
199
+
200
+ await eventLoopIteration();
201
+
202
+ nextLife();
203
+ const zone3 = makeDurableZone(getBaggage(), 'durableRoot');
204
+ return testBadHostReplay1(t, zone3);
205
+ });
@@ -0,0 +1,118 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ } from './prepare-test-env-ava.js';
8
+
9
+ import { Far } from '@endo/pass-style';
10
+ import { prepareVowTools } from '@agoric/vow';
11
+ import { isVow, toPassableCap } from '@agoric/vow/src/vow-utils.js';
12
+ import { makeHeapZone } from '@agoric/zone/heap.js';
13
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
14
+ import { makeDurableZone } from '@agoric/zone/durable.js';
15
+
16
+ import { prepareBijection } from '../src/bijection.js';
17
+
18
+ /**
19
+ * @param {any} t
20
+ * @param {Zone} zone
21
+ */
22
+ const testBijection = (t, zone) => {
23
+ const { makeVowKit } = prepareVowTools(zone);
24
+ const makeBijection = prepareBijection(zone);
25
+ const bij = zone.makeOnce('bij', makeBijection);
26
+
27
+ const h1 = zone.exo('h1', undefined, {});
28
+ const h2 = zone.exo('h2', undefined, {});
29
+ const h3 = zone.makeOnce('h3', () => makeVowKit().vow);
30
+ t.true(isVow(h3));
31
+
32
+ const g1 = Far('g1', {});
33
+ const g2 = Far('g2', {});
34
+ const g3 = harden(Promise.resolve('g3'));
35
+
36
+ t.false(bij.has(g1, h1));
37
+ t.throws(() => bij.guestToHost(g1), {
38
+ message: 'guestToHost key not found: "[Alleged: g1]"',
39
+ });
40
+ t.throws(() => bij.hostToGuest(h1), {
41
+ message: 'hostToGuest key not found: "[Alleged: h1]"',
42
+ });
43
+ t.false(bij.hasGuest(g1));
44
+ t.false(bij.hasHost(h1));
45
+ t.false(bij.hasGuest(h1));
46
+ t.false(bij.hasHost(g1));
47
+
48
+ bij.init(g1, h1);
49
+
50
+ t.true(bij.has(g1, h1));
51
+ t.is(toPassableCap(bij.guestToHost(g1)), toPassableCap(h1));
52
+ t.is(bij.hostToGuest(h1), g1);
53
+ t.true(bij.hasGuest(g1));
54
+ t.true(bij.hasHost(h1));
55
+ t.false(bij.hasGuest(h1));
56
+ t.false(bij.hasHost(g1));
57
+
58
+ t.throws(() => bij.init(g1, h2), {
59
+ message:
60
+ 'guestToHost key already bound: "[Alleged: g1]" -> "[Alleged: h1]" vs "[Alleged: h2]"',
61
+ });
62
+ t.throws(() => bij.init(g2, h1), {
63
+ message:
64
+ 'hostToGuest key already bound: "[Alleged: h1]" -> "[Alleged: g1]" vs "[Alleged: g2]"',
65
+ });
66
+ t.throws(() => bij.has(g1, h2), {
67
+ message:
68
+ 'internal: g->h "[Alleged: g1]" -> "[Alleged: h2]" vs "[Alleged: h1]"',
69
+ });
70
+ t.false(bij.has(g2, h2));
71
+ bij.init(g2, h2);
72
+ t.true(bij.has(g2, h2));
73
+
74
+ t.false(bij.has(g3, h3));
75
+ bij.init(g3, h3);
76
+ t.true(bij.has(g3, h3));
77
+ t.false(bij.has(h3, g3));
78
+ };
79
+
80
+ test('test heap bijection', t => {
81
+ const zone = makeHeapZone('heapRoot');
82
+ testBijection(t, zone);
83
+ });
84
+
85
+ test.serial('test virtual bijection', t => {
86
+ annihilate();
87
+ const zone = makeVirtualZone('virtualRoot');
88
+ testBijection(t, zone);
89
+ });
90
+
91
+ test.serial('test durable bijection', t => {
92
+ annihilate();
93
+
94
+ nextLife();
95
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
96
+ testBijection(t, zone1);
97
+
98
+ // Bijections persist but revive empty since all the guests disappear anyway
99
+
100
+ nextLife();
101
+ const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
102
+ testBijection(t, zone2);
103
+ });
104
+
105
+ test('test heap bijection reset', t => {
106
+ const zone = makeHeapZone('heapRoot');
107
+ const makeBijection = prepareBijection(zone);
108
+ const bij = makeBijection();
109
+
110
+ const h1 = zone.exo('h1', undefined, {});
111
+ const g1 = Far('g1', {});
112
+
113
+ t.false(bij.has(g1, h1));
114
+ bij.init(g1, h1);
115
+ t.true(bij.has(g1, h1));
116
+ bij.reset();
117
+ t.false(bij.has(g1, h1));
118
+ });
@@ -0,0 +1,127 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ asyncFlowVerbose,
8
+ } from './prepare-test-env-ava.js';
9
+
10
+ import { X, makeError, q } from '@endo/errors';
11
+ import { Far, getInterfaceOf, makeTagged, passStyleOf } from '@endo/pass-style';
12
+ import { prepareVowTools } from '@agoric/vow';
13
+ import { isVow } from '@agoric/vow/src/vow-utils.js';
14
+ import { makeHeapZone } from '@agoric/zone/heap.js';
15
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
16
+ import { makeDurableZone } from '@agoric/zone/durable.js';
17
+
18
+ import { makeConvertKit } from '../src/convert.js';
19
+ import { prepareBijection } from '../src/bijection.js';
20
+
21
+ /**
22
+ * @param {any} t
23
+ * @param {Zone} zone
24
+ * @param {boolean} [showOnConsole]
25
+ */
26
+ const testConvert = (t, zone, showOnConsole = false) => {
27
+ const { makeVowKit } = prepareVowTools(zone);
28
+ const makeBijection = prepareBijection(zone);
29
+ const bij = zone.makeOnce('bij', makeBijection);
30
+
31
+ const makeGuestForHostRemotable = hRem => {
32
+ const iface = getInterfaceOf(hRem);
33
+ return Far(`${iface} guest wrapper`, {});
34
+ };
35
+
36
+ const makeGuestForHostVow = _hVow => Promise.resolve('guest P');
37
+
38
+ const { guestToHost, hostToGuest } = makeConvertKit(
39
+ bij,
40
+ makeGuestForHostRemotable,
41
+ makeGuestForHostVow,
42
+ );
43
+
44
+ t.is(hostToGuest(8), 8);
45
+ const h1 = zone.exo('h1', undefined, {});
46
+ const h2 = zone.exo('h2', undefined, {});
47
+ const h3 = zone.makeOnce('h3', () => makeVowKit().vow);
48
+ t.true(isVow(h3));
49
+
50
+ const g1 = hostToGuest(h1);
51
+ const g2 = hostToGuest(h2);
52
+ const g3 = hostToGuest(h3);
53
+ t.is(passStyleOf(g1), 'remotable');
54
+ t.is(passStyleOf(g2), 'remotable');
55
+ t.is(passStyleOf(g3), 'promise');
56
+ t.not(g1, g2);
57
+ t.is(hostToGuest(h1), g1);
58
+ t.is(hostToGuest(h2), g2);
59
+ t.is(hostToGuest(h3), g3);
60
+
61
+ const h4 = makeError(X`open ${'redacted'} ${q('quoted')}`);
62
+ const g4a = hostToGuest(h4);
63
+ const g4b = hostToGuest(h4);
64
+ t.not(g4a, g4b);
65
+ t.deepEqual(g4a, g4b);
66
+
67
+ t.is(guestToHost(g1), h1);
68
+ t.is(guestToHost(g2), h2);
69
+ t.is(guestToHost(g3), h3);
70
+
71
+ t.deepEqual(guestToHost(harden([g1, g3])), [h1, h3]);
72
+ t.deepEqual(hostToGuest(harden([h1, h3])), [g1, g3]);
73
+ t.deepEqual(guestToHost(harden(URIError('msg'))), URIError('msg'));
74
+ t.deepEqual(hostToGuest(harden(URIError('msg'))), URIError('msg'));
75
+
76
+ // guestToHost and hostToGuest does its own hardening
77
+ t.deepEqual(guestToHost([g1, g3]), [h1, h3]);
78
+ t.deepEqual(hostToGuest([h1, h3]), [g1, g3]);
79
+ t.deepEqual(guestToHost(URIError('msg')), URIError('msg'));
80
+ t.deepEqual(hostToGuest(URIError('msg')), URIError('msg'));
81
+
82
+ t.deepEqual(guestToHost({ o1: g1, o2: g2 }), { o1: h1, o2: h2 });
83
+ t.deepEqual(hostToGuest({ o1: h1, o2: h2 }), { o1: g1, o2: g2 });
84
+ t.deepEqual(guestToHost(makeTagged('t', g1)), makeTagged('t', h1));
85
+ t.deepEqual(hostToGuest(makeTagged('t', h1)), makeTagged('t', g1));
86
+
87
+ const gErr1 = makeError(X`error ${'redacted message'}`, URIError);
88
+ const hErr1 = guestToHost(gErr1);
89
+ const gErr2 = hostToGuest(hErr1);
90
+
91
+ t.not(gErr1, hErr1);
92
+ t.not(hErr1, gErr2);
93
+ t.not(gErr1, gErr2);
94
+ t.is(gErr1.name, 'URIError');
95
+ t.is(hErr1.name, 'URIError');
96
+ t.is(gErr2.name, 'URIError');
97
+
98
+ if (showOnConsole) {
99
+ t.log('gErr2', gErr2);
100
+ }
101
+ };
102
+
103
+ test('test heap convert', t => {
104
+ const zone = makeHeapZone('heapRoot');
105
+ testConvert(t, zone, asyncFlowVerbose());
106
+ });
107
+
108
+ test.serial('test virtual convert', t => {
109
+ annihilate();
110
+ const zone = makeVirtualZone('virtualRoot');
111
+ testConvert(t, zone);
112
+ });
113
+
114
+ test.serial('test durable convert', t => {
115
+ annihilate();
116
+
117
+ nextLife();
118
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
119
+ testConvert(t, zone1);
120
+
121
+ // These converters keep their state only in the bijection,
122
+ // which loses all its memory between incarnations.
123
+
124
+ nextLife();
125
+ const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
126
+ testConvert(t, zone2);
127
+ });
@@ -0,0 +1,116 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ asyncFlowVerbose,
8
+ } from './prepare-test-env-ava.js';
9
+
10
+ import { X, makeError } from '@endo/errors';
11
+ import { Far } from '@endo/pass-style';
12
+ import { prepareVowTools } from '@agoric/vow';
13
+ import { isVow } from '@agoric/vow/src/vow-utils.js';
14
+ import { makeHeapZone } from '@agoric/zone/heap.js';
15
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
16
+ import { makeDurableZone } from '@agoric/zone/durable.js';
17
+
18
+ import { prepareBijection } from '../src/bijection.js';
19
+ import { makeEquate } from '../src/equate.js';
20
+
21
+ /**
22
+ * @param {any} t
23
+ * @param {Zone} zone
24
+ * @param {boolean} [showOnConsole]
25
+ */
26
+ const testEquate = (t, zone, showOnConsole = false) => {
27
+ const { makeVowKit } = prepareVowTools(zone);
28
+ const makeBijection = prepareBijection(zone);
29
+ const bij = zone.makeOnce('bij', makeBijection);
30
+
31
+ t.throws(() => zone.makeOnce('equate', () => makeEquate(bij)), {
32
+ message: 'maker return value "[Function equate]" is not storable',
33
+ });
34
+
35
+ const equate = makeEquate(bij);
36
+
37
+ equate(8, 8);
38
+ t.throws(() => equate(8, 9), {
39
+ message: 'unequal 8 vs 9',
40
+ });
41
+
42
+ const h1 = zone.exo('h1', undefined, {});
43
+ const h2 = zone.makeOnce('h2', () => makeVowKit().vow);
44
+ t.true(isVow(h2));
45
+
46
+ const g1 = Far('g1', {});
47
+ const g2 = harden(Promise.resolve('g2'));
48
+
49
+ t.throws(() => equate(g1, h1), {
50
+ message:
51
+ 'cannot yet send guest remotables to host "[Alleged: g1]" vs "[Alleged: h1]"',
52
+ });
53
+ bij.init(g1, h1);
54
+ t.notThrows(() => equate(g1, h1));
55
+ t.throws(() => equate(g1, h2), {
56
+ message: 'internal: g->h "[Alleged: g1]" -> "[Vow]" vs "[Alleged: h1]"',
57
+ });
58
+ t.throws(() => equate(g2, h1), {
59
+ message: 'internal: unexpected h->g "[Alleged: h1]" -> "[Alleged: g1]"',
60
+ });
61
+ bij.init(g2, h2);
62
+ equate(g2, h2);
63
+
64
+ t.throws(() => equate(g1, h2), {
65
+ message: 'internal: g->h "[Alleged: g1]" -> "[Vow]" vs "[Alleged: h1]"',
66
+ });
67
+ t.throws(() => equate(g2, h1), {
68
+ message: 'internal: g->h "[Promise]" -> "[Alleged: h1]" vs "[Vow]"',
69
+ });
70
+
71
+ equate(harden([g1, g2]), harden([h1, h2]));
72
+ t.throws(() => equate(harden([g1, g2]), harden([h1, h1])), {
73
+ message: '[1]: internal: g->h "[Promise]" -> "[Alleged: h1]" vs "[Vow]"',
74
+ });
75
+
76
+ const gErr1 = harden(makeError(X`error ${'redacted message'}`, URIError));
77
+ const hErr1 = harden(makeError(X`another message`, URIError));
78
+ const gErr2 = harden(makeError(X`another error`, TypeError));
79
+
80
+ equate(gErr1, hErr1);
81
+ t.throws(() => equate(gErr2, hErr1), {
82
+ message: 'error name: unequal "TypeError" vs "URIError"',
83
+ });
84
+
85
+ if (showOnConsole) {
86
+ // To see the annotation chain. Once we're synced with the next ses-ava,
87
+ // change this to a t.log, so we will see the annotation chain in context.
88
+ t.log('hErr1', hErr1);
89
+ }
90
+ };
91
+
92
+ test('test heap equate', t => {
93
+ const zone = makeHeapZone('heapRoot');
94
+ testEquate(t, zone, asyncFlowVerbose());
95
+ });
96
+
97
+ test.serial('test virtual equate', t => {
98
+ annihilate();
99
+ const zone = makeVirtualZone('virtualRoot');
100
+ testEquate(t, zone);
101
+ });
102
+
103
+ test.serial('test durable equate', t => {
104
+ annihilate();
105
+
106
+ nextLife();
107
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
108
+ testEquate(t, zone1);
109
+
110
+ // equate keeps its state only in the bijection,
111
+ // which loses all its memory between incarnations.
112
+
113
+ nextLife();
114
+ const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
115
+ testEquate(t, zone2);
116
+ });
@@ -0,0 +1,112 @@
1
+ // eslint-disable-next-line import/order
2
+ import {
3
+ test,
4
+ getBaggage,
5
+ annihilate,
6
+ nextLife,
7
+ } from './prepare-test-env-ava.js';
8
+
9
+ import { Fail } from '@endo/errors';
10
+ import { prepareVowTools, toPassableCap } from '@agoric/vow';
11
+ import { makeHeapZone } from '@agoric/zone/heap.js';
12
+ import { makeVirtualZone } from '@agoric/zone/virtual.js';
13
+ import { makeDurableZone } from '@agoric/zone/durable.js';
14
+
15
+ import { prepareLogStore } from '../src/log-store.js';
16
+
17
+ /**
18
+ * @param {any} t
19
+ * @param {Zone} zone
20
+ */
21
+ const testLogStorePlay = async (t, zone) => {
22
+ const { makeVowKit } = prepareVowTools(zone);
23
+ const makeLogStore = prepareLogStore(zone);
24
+
25
+ const log = zone.makeOnce('log', () => makeLogStore());
26
+ const v1 = zone.makeOnce('v1', () => makeVowKit().vow);
27
+ const v2 = zone.makeOnce('v2', () => makeVowKit().vow);
28
+
29
+ t.is(log.getIndex(), 0);
30
+ t.is(log.getLength(), 0);
31
+ t.throws(() => log.pushEntry(['bogus']), {
32
+ message:
33
+ /^In "pushEntry" method of \(LogStore\): arg 0: \["bogus"\] - Must match one of/,
34
+ });
35
+ t.false(log.isReplaying());
36
+ t.is(await log.promiseReplayDone(), undefined);
37
+
38
+ t.is(log.pushEntry(harden(['doFulfill', v1, 'x'])), 1);
39
+ t.is(log.pushEntry(harden(['doReject', v2, 'y'])), 2);
40
+ t.deepEqual(log.dump(), [
41
+ ['doFulfill', v1, 'x'],
42
+ ['doReject', v2, 'y'],
43
+ ]);
44
+ // Because t.deepEqual is too tolerant
45
+ // @ts-expect-error data dependent typing
46
+ t.is(toPassableCap(log.dump()[0][1]), toPassableCap(v1));
47
+ // @ts-expect-error data dependent typing
48
+ t.is(toPassableCap(log.dump()[1][1]), toPassableCap(v2));
49
+
50
+ t.is(log.getIndex(), 2);
51
+ t.is(log.getLength(), 2);
52
+ t.false(log.isReplaying());
53
+ t.is(await log.promiseReplayDone(), undefined);
54
+ };
55
+
56
+ /**
57
+ * @param {any} t
58
+ * @param {Zone} zone
59
+ */
60
+ const testLogStoreReplay = async (t, zone) => {
61
+ prepareVowTools(zone);
62
+ prepareLogStore(zone);
63
+
64
+ const log = /** @type {LogStore} */ (
65
+ zone.makeOnce('log', () => Fail`need log`)
66
+ );
67
+ const v1 = /** @type {Vow} */ (zone.makeOnce('v1', () => Fail`need v1`));
68
+ const v2 = /** @type {Vow} */ (zone.makeOnce('v2', () => Fail`need v2`));
69
+
70
+ t.is(log.getIndex(), 0);
71
+ t.is(log.getLength(), 2);
72
+ t.true(log.isReplaying());
73
+
74
+ t.deepEqual(log.dump(), [
75
+ ['doFulfill', v1, 'x'],
76
+ ['doReject', v2, 'y'],
77
+ ]);
78
+ // Because t.deepEqual is too tolerant
79
+ // @ts-expect-error data dependent typing
80
+ t.is(toPassableCap(log.dump()[0][1]), toPassableCap(v1));
81
+ // @ts-expect-error data dependent typing
82
+ t.is(toPassableCap(log.dump()[1][1]), toPassableCap(v2));
83
+
84
+ t.deepEqual(log.nextEntry(), ['doFulfill', v1, 'x']);
85
+ t.deepEqual(log.nextEntry(), ['doReject', v2, 'y']);
86
+ t.is(log.getIndex(), 2);
87
+ t.false(log.isReplaying());
88
+ t.is(await log.promiseReplayDone(), undefined);
89
+ };
90
+
91
+ await test('test heap log-store', async t => {
92
+ const zone = makeHeapZone('heapRoot');
93
+ return testLogStorePlay(t, zone);
94
+ });
95
+
96
+ test.serial('test virtual log-store', async t => {
97
+ annihilate();
98
+ const zone = makeVirtualZone('virtualRoot');
99
+ return testLogStorePlay(t, zone);
100
+ });
101
+
102
+ test.serial('test durable log-store', async t => {
103
+ annihilate();
104
+
105
+ nextLife();
106
+ const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
107
+ await testLogStorePlay(t, zone1);
108
+
109
+ nextLife();
110
+ const zone2 = makeDurableZone(getBaggage(), 'durableRoot');
111
+ return testLogStoreReplay(t, zone2);
112
+ });
@@ -0,0 +1,28 @@
1
+ import '@agoric/swingset-liveslots/tools/prepare-test-env.js';
2
+ import { wrapTest } from '@endo/ses-ava';
3
+ import rawTest from 'ava';
4
+
5
+ import { environmentOptionsListHas } from '@endo/env-options';
6
+ import { reincarnate } from '@agoric/swingset-liveslots/tools/setup-vat-data.js';
7
+
8
+ export const test = wrapTest(rawTest);
9
+
10
+ /** @type {ReturnType<typeof reincarnate>} */
11
+ let incarnation;
12
+
13
+ export const annihilate = () => {
14
+ incarnation = reincarnate({ relaxDurabilityRules: false });
15
+ };
16
+
17
+ export const getBaggage = () => {
18
+ return incarnation.fakeVomKit.cm.provideBaggage();
19
+ };
20
+
21
+ export const nextLife = () => {
22
+ incarnation = reincarnate(incarnation);
23
+ };
24
+
25
+ export const asyncFlowVerbose = () => {
26
+ // TODO figure out how we really want to control this.
27
+ return environmentOptionsListHas('DEBUG', 'async-flow-verbose');
28
+ };