@agoric/async-flow 0.1.1-upgrade-17-dev-ec448b0.0 → 0.1.1-upgrade-18-dev-ef001c0.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 +0 -28
- package/docs/async-flow-states.key +0 -0
- package/docs/async-flow-states.md +2 -1
- package/docs/async-flow-states.png +0 -0
- package/index.d.ts +1 -2
- package/index.js +1 -2
- package/package.json +18 -19
- package/src/async-flow.d.ts.map +1 -1
- package/src/async-flow.js +47 -3
- package/src/endowments.d.ts +0 -1
- package/src/endowments.d.ts.map +1 -1
- package/src/endowments.js +7 -35
- package/src/equate.d.ts.map +1 -1
- package/src/equate.js +16 -8
- package/src/log-store.d.ts +39 -2
- package/src/log-store.d.ts.map +1 -1
- package/src/log-store.js +121 -22
- package/src/replay-membrane.d.ts +23 -4
- package/src/replay-membrane.d.ts.map +1 -1
- package/src/replay-membrane.js +61 -35
- package/src/type-guards.d.ts.map +1 -1
- package/src/type-guards.js +1 -0
- package/src/types-index.d.ts +2 -0
- package/src/types.d.ts +86 -166
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +213 -0
- package/test/_utils.js +22 -0
- package/test/async-flow-crank.test.js +4 -6
- package/test/async-flow-early-completion.test.js +28 -8
- package/test/async-flow-no-this.js +2 -4
- package/test/async-flow.test.js +13 -5
- package/test/bad-host.test.js +13 -3
- package/test/endowments.test.js +22 -2
- package/test/equate.test.js +5 -5
- package/test/exports.test.js +8 -0
- package/test/log-store.test.js +46 -11
- package/test/prepare-test-env-ava.js +2 -20
- package/test/replay-membrane-eventual.test.js +97 -41
- package/test/snapshots/exports.test.js.md +14 -0
- package/test/snapshots/exports.test.js.snap +0 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +0 -3
- /package/src/{types.js → types-index.js} +0 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
import type { Passable } from '@endo/pass-style';
|
|
3
|
+
import type { Vow, VowTools } from '@agoric/vow';
|
|
4
|
+
import type { LogStore } from './log-store.js';
|
|
5
|
+
import type { Bijection } from './bijection.js';
|
|
6
|
+
import type { EndowmentTools } from './endowments.js';
|
|
7
|
+
|
|
8
|
+
export type FlowState =
|
|
9
|
+
| 'Running'
|
|
10
|
+
| 'Sleeping'
|
|
11
|
+
| 'Replaying'
|
|
12
|
+
| 'Failed'
|
|
13
|
+
| 'Done';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `T` defaults to `any`, not `Passable`, because unwrapped guests include
|
|
17
|
+
* non-passables, like unwrapped functions and unwrapped state records.
|
|
18
|
+
* (Unwrapped functions could be made into Remotables,
|
|
19
|
+
* but since they still could not be made durable, in this context
|
|
20
|
+
* it'd be pointless.)
|
|
21
|
+
*/
|
|
22
|
+
export type Guest<T extends unknown = any> = T;
|
|
23
|
+
export type Host<T extends Passable = Passable> = T;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A HostVow must be durably storable. It corresponds to an
|
|
27
|
+
* ephemeral guest promise.
|
|
28
|
+
*/
|
|
29
|
+
export type HostVow<T extends Passable = Passable> = Host<Vow<T>>;
|
|
30
|
+
|
|
31
|
+
export type GuestAsyncFunc = (
|
|
32
|
+
...activationArgs: Guest[]
|
|
33
|
+
) => Guest<Promise<any>>;
|
|
34
|
+
|
|
35
|
+
export type HostAsyncFuncWrapper = (
|
|
36
|
+
...activationArgs: Host<any>[]
|
|
37
|
+
) => HostVow<any>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The function from the host as it will be available in the guest.
|
|
41
|
+
*
|
|
42
|
+
* Specifically, Vow return values are converted to Promises.
|
|
43
|
+
*/
|
|
44
|
+
export type GuestOf<F extends HostAsyncFuncWrapper> = F extends (
|
|
45
|
+
...args: infer A
|
|
46
|
+
) => Vow<infer R>
|
|
47
|
+
? (...args: A) => Promise<R>
|
|
48
|
+
: F;
|
|
49
|
+
|
|
50
|
+
// from https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts
|
|
51
|
+
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Convert an entire Guest interface into what the host will implement.
|
|
55
|
+
*/
|
|
56
|
+
export type HostInterface<T> = {
|
|
57
|
+
[K in keyof T]: T[K] extends CallableFunction
|
|
58
|
+
? HostOf<T[K]>
|
|
59
|
+
: T[K] extends Record<string, any>
|
|
60
|
+
? Simplify<HostInterface<T[K]>>
|
|
61
|
+
: T[K];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert an entire Host interface into what the Guest will receive.
|
|
66
|
+
*/
|
|
67
|
+
export type GuestInterface<T> = {
|
|
68
|
+
[K in keyof T]: T[K] extends (...args: any[]) => Vow<infer R>
|
|
69
|
+
? (...args: Parameters<T[K]>) => Promise<R>
|
|
70
|
+
: T[K] extends HostAsyncFuncWrapper
|
|
71
|
+
? GuestOf<T[K]>
|
|
72
|
+
: T[K] extends (...args: any[]) => infer R
|
|
73
|
+
? T[K]
|
|
74
|
+
: T[K] extends object
|
|
75
|
+
? GuestInterface<T[K]>
|
|
76
|
+
: T[K];
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The function the host must provide to match an interface the guest expects.
|
|
81
|
+
*
|
|
82
|
+
* Specifically, Promise return values are converted to Vows.
|
|
83
|
+
*/
|
|
84
|
+
export type HostOf<F extends CallableFunction> = F extends (
|
|
85
|
+
...args: infer A
|
|
86
|
+
) => infer R
|
|
87
|
+
? R extends Promise<infer T>
|
|
88
|
+
? (...args: A) => Vow<T extends Passable ? T : HostInterface<T>>
|
|
89
|
+
: (...args: A) => HostInterface<R>
|
|
90
|
+
: F;
|
|
91
|
+
|
|
92
|
+
export type HostArgs<GA extends any[]> = { [K in keyof GA]: HostOf<GA[K]> };
|
|
93
|
+
|
|
94
|
+
export type PreparationOptions = {
|
|
95
|
+
vowTools?: VowTools;
|
|
96
|
+
makeLogStore?: (() => LogStore) | undefined;
|
|
97
|
+
makeBijection?: (() => Bijection) | undefined;
|
|
98
|
+
endowmentTools?: EndowmentTools;
|
|
99
|
+
panicHandler?: (e: any) => void;
|
|
100
|
+
};
|
|
101
|
+
export type OutcomeKind = 'return' | 'throw';
|
|
102
|
+
|
|
103
|
+
export type Outcome =
|
|
104
|
+
| {
|
|
105
|
+
kind: 'return';
|
|
106
|
+
result: any;
|
|
107
|
+
}
|
|
108
|
+
| {
|
|
109
|
+
kind: 'throw';
|
|
110
|
+
problem: any;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export type Ephemera<S extends WeakKey = WeakKey, V extends unknown = any> = {
|
|
114
|
+
for: (self: S) => V;
|
|
115
|
+
resetFor: (self: S) => void;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* This is the type alias for the membrane log entries we currently implement.
|
|
120
|
+
*
|
|
121
|
+
* @see {FutureLogEntry} below for the full membrane log entry, which we do not
|
|
122
|
+
* yet support.
|
|
123
|
+
*/
|
|
124
|
+
export type LogEntry =
|
|
125
|
+
| [op: 'startGeneration', generation: number]
|
|
126
|
+
// ///////////////// From Host to Guest /////////////////////////
|
|
127
|
+
| [op: 'doFulfill', vow: HostVow, fulfillment: Host]
|
|
128
|
+
| [op: 'doReject', vow: HostVow, reason: Host]
|
|
129
|
+
| [op: 'doReturn', callIndex: number, result: Host]
|
|
130
|
+
| [op: 'doThrow', callIndex: number, problem: Host]
|
|
131
|
+
// ///////////////////// From Guest to Host /////////////////////////
|
|
132
|
+
| [
|
|
133
|
+
op: 'checkCall',
|
|
134
|
+
target: Host,
|
|
135
|
+
optVerb: PropertyKey | undefined,
|
|
136
|
+
args: Host[],
|
|
137
|
+
callIndex: number,
|
|
138
|
+
]
|
|
139
|
+
| [
|
|
140
|
+
op: 'checkSendOnly',
|
|
141
|
+
target: Host,
|
|
142
|
+
optVerb: PropertyKey | undefined,
|
|
143
|
+
args: Host[],
|
|
144
|
+
callIndex: number,
|
|
145
|
+
]
|
|
146
|
+
| [
|
|
147
|
+
op: 'checkSend',
|
|
148
|
+
target: Host,
|
|
149
|
+
optVerb: PropertyKey | undefined,
|
|
150
|
+
args: Host[],
|
|
151
|
+
callIndex: number,
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* This would be the type alias for the full membrane log, if we supported:
|
|
156
|
+
* - the guest sending guest-promises and guest-remotables to the host
|
|
157
|
+
* - the guest using `E` to eventual-send to guest wrappers of the host
|
|
158
|
+
* vows and remotables.
|
|
159
|
+
*/
|
|
160
|
+
export type FutureLogEntry =
|
|
161
|
+
| [op: 'startGeneration', generation: number]
|
|
162
|
+
// ///////////////// From Host to Guest ///////////////////////
|
|
163
|
+
| [op: 'doFulfill', vow: HostVow, fulfillment: Host]
|
|
164
|
+
| [op: 'doReject', vow: HostVow, reason: Host]
|
|
165
|
+
| [
|
|
166
|
+
op: 'doCall',
|
|
167
|
+
target: Host,
|
|
168
|
+
optVerb: PropertyKey | undefined,
|
|
169
|
+
args: Host[],
|
|
170
|
+
callIndex: number,
|
|
171
|
+
]
|
|
172
|
+
| [
|
|
173
|
+
op: 'doSendOnly',
|
|
174
|
+
target: Host,
|
|
175
|
+
optVerb: PropertyKey | undefined,
|
|
176
|
+
args: Host[],
|
|
177
|
+
callIndex: number,
|
|
178
|
+
]
|
|
179
|
+
| [
|
|
180
|
+
op: 'doSend',
|
|
181
|
+
target: Host,
|
|
182
|
+
optVerb: PropertyKey | undefined,
|
|
183
|
+
args: Host[],
|
|
184
|
+
callIndex: number,
|
|
185
|
+
]
|
|
186
|
+
| [op: 'doReturn', callIndex: number, result: Host]
|
|
187
|
+
| [op: 'doThrow', callIndex: number, problem: Host]
|
|
188
|
+
// ///////////////////// From Guest to Host /////////////////////////
|
|
189
|
+
| [op: 'checkFulfill', vow: HostVow, fulfillment: Host]
|
|
190
|
+
| [op: 'checkReject', vow: HostVow, reason: Host]
|
|
191
|
+
| [
|
|
192
|
+
op: 'checkCall',
|
|
193
|
+
target: Host,
|
|
194
|
+
optVerb: PropertyKey | undefined,
|
|
195
|
+
args: Host[],
|
|
196
|
+
callIndex: number,
|
|
197
|
+
]
|
|
198
|
+
| [
|
|
199
|
+
op: 'checkSendOnly',
|
|
200
|
+
target: Host,
|
|
201
|
+
optVerb: PropertyKey | undefined,
|
|
202
|
+
args: Host[],
|
|
203
|
+
callIndex: number,
|
|
204
|
+
]
|
|
205
|
+
| [
|
|
206
|
+
op: 'checkSend',
|
|
207
|
+
target: Host,
|
|
208
|
+
optVerb: PropertyKey | undefined,
|
|
209
|
+
args: Host[],
|
|
210
|
+
callIndex: number,
|
|
211
|
+
]
|
|
212
|
+
| [op: 'checkReturn', callIndex: number, result: Host]
|
|
213
|
+
| [op: 'checkThrow', callIndex: number, problem: Host];
|
package/test/_utils.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { prepareAsyncFlowTools } from '../src/async-flow.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import {Zone} from '@agoric/base-zone';
|
|
5
|
+
* @import {PreparationOptions} from '../src/types.js';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {*} t
|
|
10
|
+
* @param {Zone} zone
|
|
11
|
+
* @param {PreparationOptions} [opts]
|
|
12
|
+
*/
|
|
13
|
+
export const prepareTestAsyncFlowTools = (t, zone, opts) => {
|
|
14
|
+
const {
|
|
15
|
+
panicHandler = e => {
|
|
16
|
+
t.log('Panic handler called', e);
|
|
17
|
+
t.fail('Unexpected panic');
|
|
18
|
+
},
|
|
19
|
+
} = opts || {};
|
|
20
|
+
return prepareAsyncFlowTools(zone, { panicHandler, ...opts });
|
|
21
|
+
};
|
|
22
|
+
harden(prepareTestAsyncFlowTools);
|
|
@@ -14,12 +14,10 @@ import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
|
|
|
14
14
|
import { prepareVowTools } from '@agoric/vow';
|
|
15
15
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { prepareTestAsyncFlowTools } from './_utils.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* @import {PromiseKit} from '@endo/promise-kit'
|
|
21
20
|
* @import {Zone} from '@agoric/base-zone'
|
|
22
|
-
* @import {Ephemera} from './types.js';
|
|
23
21
|
*/
|
|
24
22
|
|
|
25
23
|
const neverSettlesP = new Promise(() => {});
|
|
@@ -30,7 +28,7 @@ const neverSettlesP = new Promise(() => {});
|
|
|
30
28
|
*/
|
|
31
29
|
const testPlay1 = async (t, zone) => {
|
|
32
30
|
const vowTools = prepareVowTools(zone);
|
|
33
|
-
const { asyncFlow } =
|
|
31
|
+
const { asyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
34
32
|
vowTools,
|
|
35
33
|
});
|
|
36
34
|
|
|
@@ -48,7 +46,7 @@ const testPlay1 = async (t, zone) => {
|
|
|
48
46
|
*/
|
|
49
47
|
const testPlay2 = async (t, zone) => {
|
|
50
48
|
const vowTools = prepareVowTools(zone);
|
|
51
|
-
const { asyncFlow } =
|
|
49
|
+
const { asyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
52
50
|
vowTools,
|
|
53
51
|
});
|
|
54
52
|
|
|
@@ -63,7 +61,7 @@ const testPlay2 = async (t, zone) => {
|
|
|
63
61
|
*/
|
|
64
62
|
const testPlay3 = async (t, zone) => {
|
|
65
63
|
const vowTools = prepareVowTools(zone);
|
|
66
|
-
const { asyncFlow, allWokenP } =
|
|
64
|
+
const { asyncFlow, allWokenP } = prepareTestAsyncFlowTools(t, zone, {
|
|
67
65
|
vowTools,
|
|
68
66
|
});
|
|
69
67
|
|
|
@@ -15,7 +15,7 @@ import { isVow } from '@agoric/vow/src/vow-utils.js';
|
|
|
15
15
|
import { prepareVowTools } from '@agoric/vow';
|
|
16
16
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { prepareTestAsyncFlowTools } from './_utils.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @import {Zone} from '@agoric/base-zone';
|
|
@@ -59,7 +59,7 @@ const firstLogLen = 7;
|
|
|
59
59
|
const testFirstPlay = async (t, zone) => {
|
|
60
60
|
t.log('firstPlay started');
|
|
61
61
|
const vowTools = prepareVowTools(zone);
|
|
62
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
62
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
63
63
|
vowTools,
|
|
64
64
|
});
|
|
65
65
|
const makeOrchestra = prepareOrchestra(zone);
|
|
@@ -134,12 +134,24 @@ const testFirstPlay = async (t, zone) => {
|
|
|
134
134
|
*
|
|
135
135
|
* @param {any} t
|
|
136
136
|
* @param {Zone} zone
|
|
137
|
+
* @param {unknown} [rejection]
|
|
137
138
|
*/
|
|
138
|
-
const testBadShortReplay = async (t, zone) => {
|
|
139
|
+
const testBadShortReplay = async (t, zone, rejection) => {
|
|
139
140
|
t.log('badShortReplay started');
|
|
140
141
|
const vowTools = prepareVowTools(zone);
|
|
141
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
142
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
142
143
|
vowTools,
|
|
144
|
+
panicHandler: e => {
|
|
145
|
+
t.throws(
|
|
146
|
+
() => {
|
|
147
|
+
throw e;
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
message:
|
|
151
|
+
/^guest (fulfilled with "bad"|rejected with "\[Error: replayProblem\]") before finishing replay$/,
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
},
|
|
143
155
|
});
|
|
144
156
|
prepareOrchestra(zone);
|
|
145
157
|
const { when } = vowTools;
|
|
@@ -149,8 +161,12 @@ const testBadShortReplay = async (t, zone) => {
|
|
|
149
161
|
|
|
150
162
|
const { guestMethod } = {
|
|
151
163
|
async guestMethod(_gOrch7, _g1, _p3) {
|
|
152
|
-
|
|
153
|
-
|
|
164
|
+
resolveStep(eventLoopIteration()); // resolveStep(true) is too fast.
|
|
165
|
+
if (rejection) {
|
|
166
|
+
t.log(' badShortReplay rejecting');
|
|
167
|
+
throw rejection;
|
|
168
|
+
}
|
|
169
|
+
t.log(' badShortReplay return early');
|
|
154
170
|
return 'bad';
|
|
155
171
|
},
|
|
156
172
|
};
|
|
@@ -174,7 +190,7 @@ const testBadShortReplay = async (t, zone) => {
|
|
|
174
190
|
await promiseStep;
|
|
175
191
|
|
|
176
192
|
const replayProblem = flow.getOptFatalProblem();
|
|
177
|
-
t.log(' badShortReplay
|
|
193
|
+
t.log(' badShortReplay replayProblem', replayProblem);
|
|
178
194
|
t.true(replayProblem instanceof Error);
|
|
179
195
|
|
|
180
196
|
const outcome = when(outcomeV);
|
|
@@ -190,7 +206,7 @@ const testBadShortReplay = async (t, zone) => {
|
|
|
190
206
|
t.log('badShortReplay done');
|
|
191
207
|
};
|
|
192
208
|
|
|
193
|
-
test.serial
|
|
209
|
+
test.serial('test durable async-flow early completion', async t => {
|
|
194
210
|
annihilate();
|
|
195
211
|
const zone1 = makeDurableZone(getBaggage(), 'durableRoot');
|
|
196
212
|
await testFirstPlay(t, zone1);
|
|
@@ -200,4 +216,8 @@ test.serial.failing('test durable async-flow early completion', async t => {
|
|
|
200
216
|
nextLife();
|
|
201
217
|
const zone2a = makeDurableZone(getBaggage(), 'durableRoot');
|
|
202
218
|
await testBadShortReplay(t, zone2a);
|
|
219
|
+
|
|
220
|
+
nextLife();
|
|
221
|
+
const zone2b = makeDurableZone(getBaggage(), 'durableRoot');
|
|
222
|
+
await testBadShortReplay(t, zone2b, Error('replayProblem'));
|
|
203
223
|
});
|
|
@@ -7,12 +7,10 @@ import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
|
7
7
|
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
8
8
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { prepareTestAsyncFlowTools } from './_utils.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @import {Zone} from '@agoric/base-zone';
|
|
14
|
-
* @import {Vow, VowTools} from '@agoric/vow'
|
|
15
|
-
* @import {AsyncFlow} from '../src/async-flow.js'
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
16
|
const { apply } = Reflect;
|
|
@@ -23,7 +21,7 @@ const { apply } = Reflect;
|
|
|
23
21
|
*/
|
|
24
22
|
const testPlay = async (t, zone) => {
|
|
25
23
|
const vowTools = prepareVowTools(zone);
|
|
26
|
-
const { asyncFlow } =
|
|
24
|
+
const { asyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
27
25
|
vowTools,
|
|
28
26
|
});
|
|
29
27
|
|
package/test/async-flow.test.js
CHANGED
|
@@ -17,7 +17,7 @@ import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
|
17
17
|
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
18
18
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { prepareTestAsyncFlowTools } from './_utils.js';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* @import {AsyncFlow} from '../src/async-flow.js'
|
|
@@ -62,7 +62,7 @@ const firstLogLen = 7;
|
|
|
62
62
|
const testFirstPlay = async (t, zone) => {
|
|
63
63
|
t.log('firstPlay started');
|
|
64
64
|
const vowTools = prepareVowTools(zone);
|
|
65
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
65
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
66
66
|
vowTools,
|
|
67
67
|
});
|
|
68
68
|
const makeOrchestra = prepareOrchestra(zone);
|
|
@@ -139,8 +139,16 @@ const testFirstPlay = async (t, zone) => {
|
|
|
139
139
|
const testBadReplay = async (t, zone) => {
|
|
140
140
|
t.log('badReplay started');
|
|
141
141
|
const vowTools = prepareVowTools(zone);
|
|
142
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
142
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
143
143
|
vowTools,
|
|
144
|
+
panicHandler: e => {
|
|
145
|
+
t.throws(
|
|
146
|
+
() => {
|
|
147
|
+
throw e;
|
|
148
|
+
},
|
|
149
|
+
{ message: /^replay 3:/ },
|
|
150
|
+
);
|
|
151
|
+
},
|
|
144
152
|
});
|
|
145
153
|
prepareOrchestra(zone);
|
|
146
154
|
const { when } = vowTools;
|
|
@@ -206,7 +214,7 @@ const testBadReplay = async (t, zone) => {
|
|
|
206
214
|
const testGoodReplay = async (t, zone) => {
|
|
207
215
|
t.log('goodReplay started');
|
|
208
216
|
const vowTools = prepareVowTools(zone);
|
|
209
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
217
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
210
218
|
vowTools,
|
|
211
219
|
});
|
|
212
220
|
prepareOrchestra(zone, 2); // Note change in new behavior
|
|
@@ -310,7 +318,7 @@ const testGoodReplay = async (t, zone) => {
|
|
|
310
318
|
const testAfterPlay = async (t, zone) => {
|
|
311
319
|
t.log('testAfterPlay started');
|
|
312
320
|
const vowTools = prepareVowTools(zone);
|
|
313
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
321
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
314
322
|
vowTools,
|
|
315
323
|
});
|
|
316
324
|
prepareOrchestra(zone);
|
package/test/bad-host.test.js
CHANGED
|
@@ -15,7 +15,7 @@ import { makeHeapZone } from '@agoric/zone/heap.js';
|
|
|
15
15
|
import { makeVirtualZone } from '@agoric/zone/virtual.js';
|
|
16
16
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import { prepareTestAsyncFlowTools } from './_utils.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @import {PromiseKit} from '@endo/promise-kit'
|
|
@@ -51,8 +51,16 @@ const prepareBadHost = zone =>
|
|
|
51
51
|
const testBadHostFirstPlay = async (t, zone) => {
|
|
52
52
|
t.log('badHost firstPlay started');
|
|
53
53
|
const vowTools = prepareVowTools(zone);
|
|
54
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
54
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
55
55
|
vowTools,
|
|
56
|
+
panicHandler: e => {
|
|
57
|
+
t.throws(
|
|
58
|
+
() => {
|
|
59
|
+
throw e;
|
|
60
|
+
},
|
|
61
|
+
{ message: '[3]: [0]: cannot yet send guest promises "[Promise]"' },
|
|
62
|
+
);
|
|
63
|
+
},
|
|
56
64
|
});
|
|
57
65
|
const makeBadHost = prepareBadHost(zone);
|
|
58
66
|
const { makeVowKit } = vowTools;
|
|
@@ -103,6 +111,8 @@ const testBadHostFirstPlay = async (t, zone) => {
|
|
|
103
111
|
// Notice that the bad call was not recorded in the log
|
|
104
112
|
]);
|
|
105
113
|
t.log('badHost firstPlay done');
|
|
114
|
+
// Allow panicHandler to be called.
|
|
115
|
+
await eventLoopIteration();
|
|
106
116
|
};
|
|
107
117
|
|
|
108
118
|
/**
|
|
@@ -112,7 +122,7 @@ const testBadHostFirstPlay = async (t, zone) => {
|
|
|
112
122
|
const testBadHostReplay1 = async (t, zone) => {
|
|
113
123
|
t.log('badHost replay1 started');
|
|
114
124
|
const vowTools = prepareVowTools(zone);
|
|
115
|
-
const { asyncFlow, adminAsyncFlow } =
|
|
125
|
+
const { asyncFlow, adminAsyncFlow } = prepareTestAsyncFlowTools(t, zone, {
|
|
116
126
|
vowTools,
|
|
117
127
|
});
|
|
118
128
|
prepareBadHost(zone);
|
package/test/endowments.test.js
CHANGED
|
@@ -16,6 +16,7 @@ import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
|
16
16
|
import { forwardingMethods, prepareEndowmentTools } from '../src/endowments.js';
|
|
17
17
|
import { makeConvertKit } from '../src/convert.js';
|
|
18
18
|
import { prepareBijection } from '../src/bijection.js';
|
|
19
|
+
import { makeEquate } from '../src/equate.js';
|
|
19
20
|
|
|
20
21
|
const { ownKeys } = Reflect;
|
|
21
22
|
|
|
@@ -84,7 +85,16 @@ const testEndowmentPlay = async (t, zone, gen, isDurable) => {
|
|
|
84
85
|
t.is(endowment.state[`${gen}_foo`], `${gen} foo`);
|
|
85
86
|
t.is(wrapped.state.get(`${gen}_foo`), `${gen} foo`);
|
|
86
87
|
|
|
87
|
-
const
|
|
88
|
+
const guestWrappers = new Map();
|
|
89
|
+
const unwrapSpy = (hostWrapped, guestWrapped) => {
|
|
90
|
+
const unwrapped = unwrap(hostWrapped, guestWrapped);
|
|
91
|
+
if (unwrapped !== guestWrapped) {
|
|
92
|
+
guestWrappers.set(hostWrapped, guestWrapped);
|
|
93
|
+
}
|
|
94
|
+
return unwrapped;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const makeBijection = prepareBijection(zone, unwrapSpy);
|
|
88
98
|
const bij = zone.makeOnce('bij', makeBijection);
|
|
89
99
|
|
|
90
100
|
const makeGuestForHostRemotable = hRem => {
|
|
@@ -114,13 +124,23 @@ const testEndowmentPlay = async (t, zone, gen, isDurable) => {
|
|
|
114
124
|
t.is(unwrapped.storable.exo.name(), `${gen} exo`);
|
|
115
125
|
t.is(passStyleOf(unwrapped.far), 'remotable');
|
|
116
126
|
t.is(unwrapped.far.name(), `${gen} far`);
|
|
117
|
-
t.
|
|
127
|
+
t.is(passStyleOf(unwrapped.function), 'remotable');
|
|
118
128
|
t.is(typeof unwrapped.function, 'function');
|
|
119
129
|
t.is(unwrapped.function(), `${gen} function`);
|
|
120
130
|
t.is(unwrapped.array[0](), `${gen} f1`);
|
|
121
131
|
t.false(isPassable(unwrapped.state));
|
|
122
132
|
t.is(typeof unwrapped.state, 'object');
|
|
123
133
|
t.is(unwrapped.state[`${gen}_foo`], `${gen} foo`);
|
|
134
|
+
|
|
135
|
+
const equate = makeEquate(bij);
|
|
136
|
+
|
|
137
|
+
const { state: _1, ...passableUnwrapped } = unwrapped;
|
|
138
|
+
const { state: _2, ...passableWrapped } = wrapped;
|
|
139
|
+
|
|
140
|
+
t.notThrows(() => equate(harden(passableUnwrapped), harden(passableWrapped)));
|
|
141
|
+
for (const [hostWrapped, guestWrapped] of guestWrappers) {
|
|
142
|
+
t.notThrows(() => equate(guestWrapped, hostWrapped));
|
|
143
|
+
}
|
|
124
144
|
};
|
|
125
145
|
|
|
126
146
|
const testEndowmentBadReplay = async (_t, _zone, _gen, _isDurable) => {
|
package/test/equate.test.js
CHANGED
|
@@ -57,24 +57,24 @@ const testEquate = (t, zone, showOnConsole = false) => {
|
|
|
57
57
|
bij.unwrapInit(g1, h1);
|
|
58
58
|
t.notThrows(() => equate(g1, h1));
|
|
59
59
|
t.throws(() => equate(g1, h2), {
|
|
60
|
-
message: '
|
|
60
|
+
message: 'unequal passStyles "remotable" vs "tagged"',
|
|
61
61
|
});
|
|
62
62
|
t.throws(() => equate(g2, h1), {
|
|
63
|
-
message: '
|
|
63
|
+
message: 'unequal passStyles "promise" vs "remotable"',
|
|
64
64
|
});
|
|
65
65
|
bij.unwrapInit(g2, h2);
|
|
66
66
|
equate(g2, h2);
|
|
67
67
|
|
|
68
68
|
t.throws(() => equate(g1, h2), {
|
|
69
|
-
message: '
|
|
69
|
+
message: 'unequal passStyles "remotable" vs "tagged"',
|
|
70
70
|
});
|
|
71
71
|
t.throws(() => equate(g2, h1), {
|
|
72
|
-
message: '
|
|
72
|
+
message: 'unequal passStyles "promise" vs "remotable"',
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
equate(harden([g1, g2]), harden([h1, h2]));
|
|
76
76
|
t.throws(() => equate(harden([g1, g2]), harden([h1, h1])), {
|
|
77
|
-
message: '[1]:
|
|
77
|
+
message: '[1]: unequal passStyles "promise" vs "remotable"',
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
const gErr1 = harden(makeError(X`error ${'redacted message'}`, URIError));
|
package/test/log-store.test.js
CHANGED
|
@@ -15,11 +15,9 @@ import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
|
15
15
|
import { prepareLogStore } from '../src/log-store.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @import {PromiseKit} from '@endo/promise-kit'
|
|
19
18
|
* @import {Zone} from '@agoric/base-zone'
|
|
20
|
-
* @import {Vow
|
|
19
|
+
* @import {Vow} from '@agoric/vow'
|
|
21
20
|
* @import {LogStore} from '../src/log-store.js';
|
|
22
|
-
* @import {Bijection} from '../src/bijection.js';
|
|
23
21
|
*/
|
|
24
22
|
|
|
25
23
|
/**
|
|
@@ -36,15 +34,23 @@ const testLogStorePlay = async (t, zone) => {
|
|
|
36
34
|
|
|
37
35
|
t.is(log.getIndex(), 0);
|
|
38
36
|
t.is(log.getLength(), 0);
|
|
39
|
-
t.throws(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
t.throws(
|
|
38
|
+
() =>
|
|
39
|
+
// @ts-expect-error testing invalid input
|
|
40
|
+
log.pushEntry(['bogus']),
|
|
41
|
+
{
|
|
42
|
+
message:
|
|
43
|
+
/^In "pushEntry" method of \(LogStore\): arg 0: \["bogus"\] - Must match one of/,
|
|
44
|
+
},
|
|
45
|
+
);
|
|
43
46
|
t.false(log.isReplaying());
|
|
44
47
|
t.is(await log.promiseReplayDone(), undefined);
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
const gen0Entries = [['startGeneration', 0]];
|
|
50
|
+
const gen0 = i => gen0Entries.length + i;
|
|
51
|
+
|
|
52
|
+
t.is(log.pushEntry(harden(['doFulfill', v1, 'x'])), gen0(1));
|
|
53
|
+
t.is(log.pushEntry(harden(['doReject', v2, 'y'])), gen0(2));
|
|
48
54
|
t.deepEqual(log.dump(), [
|
|
49
55
|
['doFulfill', v1, 'x'],
|
|
50
56
|
['doReject', v2, 'y'],
|
|
@@ -56,7 +62,8 @@ const testLogStorePlay = async (t, zone) => {
|
|
|
56
62
|
t.is(toPassableCap(log.dump()[1][1]), toPassableCap(v2));
|
|
57
63
|
|
|
58
64
|
t.is(log.getIndex(), 2);
|
|
59
|
-
t.is(log.
|
|
65
|
+
t.is(log.getUnfilteredIndex(), gen0(2));
|
|
66
|
+
t.is(log.getLength(), gen0(2));
|
|
60
67
|
t.false(log.isReplaying());
|
|
61
68
|
t.is(await log.promiseReplayDone(), undefined);
|
|
62
69
|
};
|
|
@@ -75,14 +82,26 @@ const testLogStoreReplay = async (t, zone) => {
|
|
|
75
82
|
const v1 = /** @type {Vow} */ (zone.makeOnce('v1', () => Fail`need v1`));
|
|
76
83
|
const v2 = /** @type {Vow} */ (zone.makeOnce('v2', () => Fail`need v2`));
|
|
77
84
|
|
|
85
|
+
const gen0Entries = [['startGeneration', 0]];
|
|
86
|
+
const gen0 = i => gen0Entries.length + i;
|
|
87
|
+
const gen1Entries = [['startGeneration', 1]];
|
|
88
|
+
const gen1 = i => gen1Entries.length + i;
|
|
89
|
+
|
|
78
90
|
t.is(log.getIndex(), 0);
|
|
79
|
-
t.is(log.
|
|
91
|
+
t.is(log.getUnfilteredIndex(), 0);
|
|
92
|
+
t.is(log.getLength(), gen0(2));
|
|
80
93
|
t.true(log.isReplaying());
|
|
81
94
|
|
|
82
95
|
t.deepEqual(log.dump(), [
|
|
83
96
|
['doFulfill', v1, 'x'],
|
|
84
97
|
['doReject', v2, 'y'],
|
|
85
98
|
]);
|
|
99
|
+
|
|
100
|
+
t.deepEqual(log.dumpUnfiltered(), [
|
|
101
|
+
...gen0Entries,
|
|
102
|
+
['doFulfill', v1, 'x'],
|
|
103
|
+
['doReject', v2, 'y'],
|
|
104
|
+
]);
|
|
86
105
|
// Because t.deepEqual is too tolerant
|
|
87
106
|
// @ts-expect-error data dependent typing
|
|
88
107
|
t.is(toPassableCap(log.dump()[0][1]), toPassableCap(v1));
|
|
@@ -92,8 +111,24 @@ const testLogStoreReplay = async (t, zone) => {
|
|
|
92
111
|
t.deepEqual(log.nextEntry(), ['doFulfill', v1, 'x']);
|
|
93
112
|
t.deepEqual(log.nextEntry(), ['doReject', v2, 'y']);
|
|
94
113
|
t.is(log.getIndex(), 2);
|
|
114
|
+
t.is(log.getUnfilteredIndex(), gen0(2));
|
|
95
115
|
t.false(log.isReplaying());
|
|
96
116
|
t.is(await log.promiseReplayDone(), undefined);
|
|
117
|
+
|
|
118
|
+
t.is(log.pushEntry(harden(['doFulfill', v1, 'x2'])), gen1(gen0(3)));
|
|
119
|
+
t.deepEqual(log.dumpUnfiltered(), [
|
|
120
|
+
...gen0Entries,
|
|
121
|
+
['doFulfill', v1, 'x'],
|
|
122
|
+
['doReject', v2, 'y'],
|
|
123
|
+
...gen1Entries,
|
|
124
|
+
['doFulfill', v1, 'x2'],
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
// Check that a disposed log starts from scratch, but the same generation.
|
|
128
|
+
log.dispose();
|
|
129
|
+
t.deepEqual(log.dumpUnfiltered(), []);
|
|
130
|
+
t.is(log.pushEntry(harden(['doFulfill', v1, 'x3'])), gen1(1));
|
|
131
|
+
t.deepEqual(log.dumpUnfiltered(), [...gen1Entries, ['doFulfill', v1, 'x3']]);
|
|
97
132
|
};
|
|
98
133
|
|
|
99
134
|
await test.serial('test heap log-store', async t => {
|