@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.
- package/CHANGELOG.md +1 -0
- package/LICENSE +201 -0
- package/README.md +40 -0
- package/docs/async-flow-states.key +0 -0
- package/docs/async-flow-states.md +15 -0
- package/docs/async-flow-states.png +0 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -0
- package/index.js +1 -0
- package/package.json +67 -0
- package/src/async-flow.d.ts +87 -0
- package/src/async-flow.d.ts.map +1 -0
- package/src/async-flow.js +502 -0
- package/src/bijection.d.ts +28 -0
- package/src/bijection.d.ts.map +1 -0
- package/src/bijection.js +132 -0
- package/src/convert.d.ts +5 -0
- package/src/convert.d.ts.map +1 -0
- package/src/convert.js +131 -0
- package/src/ephemera.d.ts +2 -0
- package/src/ephemera.d.ts.map +1 -0
- package/src/ephemera.js +35 -0
- package/src/equate.d.ts +2 -0
- package/src/equate.d.ts.map +1 -0
- package/src/equate.js +123 -0
- package/src/log-store.d.ts +25 -0
- package/src/log-store.d.ts.map +1 -0
- package/src/log-store.js +165 -0
- package/src/replay-membrane.d.ts +71 -0
- package/src/replay-membrane.d.ts.map +1 -0
- package/src/replay-membrane.js +435 -0
- package/src/type-guards.d.ts +4 -0
- package/src/type-guards.d.ts.map +1 -0
- package/src/type-guards.js +54 -0
- package/src/types.d.ts +70 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.js +164 -0
- package/test/async-flow-crank.test.js +96 -0
- package/test/async-flow-no-this.js +59 -0
- package/test/async-flow.test.js +380 -0
- package/test/bad-host.test.js +205 -0
- package/test/bijection.test.js +118 -0
- package/test/convert.test.js +127 -0
- package/test/equate.test.js +116 -0
- package/test/log-store.test.js +112 -0
- package/test/prepare-test-env-ava.js +28 -0
- package/test/replay-membrane-settlement.test.js +154 -0
- package/test/replay-membrane-zombie.test.js +158 -0
- package/test/replay-membrane.test.js +271 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +13 -0
- 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
|
+
};
|