Skip to content

Commit ed5f4ea

Browse files
committed
feat: add tests for RemoteHandle module
1 parent 527c850 commit ed5f4ea

1 file changed

Lines changed: 287 additions & 0 deletions

File tree

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import type { VatOneResolution } from '@agoric/swingset-liveslots';
2+
import type { Logger } from '@metamask/logger';
3+
import { describe, it, expect, vi, beforeEach } from 'vitest';
4+
5+
import type { KernelQueue } from './KernelQueue.ts';
6+
import { RemoteHandle } from './RemoteHandle.ts';
7+
import { makeKernelStore } from './store/index.ts';
8+
import type { KernelStore } from './store/index.ts';
9+
import type { Message, RemoteComms, RRef } from './types.ts';
10+
import { makeMapKernelDatabase } from '../test/storage.ts';
11+
12+
let mockKernelStore: KernelStore;
13+
let mockRemoteComms: RemoteComms;
14+
let mockKernelQueue: KernelQueue;
15+
const mockRemoteId = 'r0';
16+
const mockRemotePeerId = 'remotePeerId';
17+
18+
/**
19+
* Fabricate a mock remote for testing purposes
20+
*
21+
* @param logger - A logger, if you care.
22+
*
23+
* @returns a new RemoteHandle suitable for use in testing.
24+
*/
25+
export function makeRemote(logger?: Logger): RemoteHandle {
26+
return RemoteHandle.make({
27+
remoteId: mockRemoteId,
28+
peerId: mockRemotePeerId,
29+
kernelStore: mockKernelStore,
30+
kernelQueue: mockKernelQueue,
31+
remoteComms: mockRemoteComms,
32+
logger,
33+
});
34+
}
35+
36+
describe('RemoteHandle', () => {
37+
beforeEach(() => {
38+
mockKernelStore = makeKernelStore(makeMapKernelDatabase());
39+
const mockRedeemLocalOcapURL = vi.fn();
40+
mockRedeemLocalOcapURL.mockReturnValue('ko100');
41+
mockRemoteComms = {
42+
getPeerId: () => 'myPeerId',
43+
sendRemoteMessage: vi.fn(),
44+
issueOcapURL: vi.fn(),
45+
redeemLocalOcapURL: mockRedeemLocalOcapURL,
46+
};
47+
mockKernelQueue = {
48+
run: vi.fn(),
49+
enqueueMessage: vi.fn(),
50+
enqueueSend: vi.fn(),
51+
enqueueNotify: vi.fn(),
52+
resolvePromises: vi.fn(),
53+
} as unknown as KernelQueue;
54+
});
55+
56+
it('deliverMessage calls sendRemoteMessage with correct delivery message', async () => {
57+
const remote = makeRemote();
58+
const target: RRef = 'ro+1';
59+
const message: Message = {
60+
methargs: { body: '["method",["arg1","arg2"]]', slots: [] },
61+
result: 'rp-2',
62+
};
63+
const crankResult = await remote.deliverMessage(target, message);
64+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
65+
mockRemotePeerId,
66+
JSON.stringify({
67+
method: 'deliver',
68+
params: ['message', target, message],
69+
}),
70+
);
71+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
72+
});
73+
74+
it('deliverNotify calls sendRemoteMessage with correct delivery message', async () => {
75+
const remote = makeRemote();
76+
const resolutions: VatOneResolution[] = [
77+
['rp-3', false, { body: '"resolved value"', slots: [] }],
78+
];
79+
80+
const crankResult = await remote.deliverNotify(resolutions);
81+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
82+
mockRemotePeerId,
83+
JSON.stringify({
84+
method: 'deliver',
85+
params: ['notify', resolutions],
86+
}),
87+
);
88+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
89+
});
90+
91+
it('deliverDropExports calls sendRemoteMessage with correct delivery message', async () => {
92+
const remote = makeRemote();
93+
const rrefs: RRef[] = ['ro+4', 'ro+5'];
94+
95+
const crankResult = await remote.deliverDropExports(rrefs);
96+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
97+
mockRemotePeerId,
98+
JSON.stringify({
99+
method: 'deliver',
100+
params: ['dropExports', rrefs],
101+
}),
102+
);
103+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
104+
});
105+
106+
it('deliverRetireExports calls sendRemoteMessage with correct delivery message', async () => {
107+
const remote = makeRemote();
108+
const rrefs: RRef[] = ['ro+4', 'ro+5'];
109+
110+
const crankResult = await remote.deliverRetireExports(rrefs);
111+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
112+
mockRemotePeerId,
113+
JSON.stringify({
114+
method: 'deliver',
115+
params: ['retireExports', rrefs],
116+
}),
117+
);
118+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
119+
});
120+
121+
it('deliverRetireImports calls sendRemoteMessage with correct delivery message', async () => {
122+
const remote = makeRemote();
123+
const rrefs: RRef[] = ['ro+4', 'ro+5'];
124+
125+
const crankResult = await remote.deliverRetireImports(rrefs);
126+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
127+
mockRemotePeerId,
128+
JSON.stringify({
129+
method: 'deliver',
130+
params: ['retireImports', rrefs],
131+
}),
132+
);
133+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
134+
});
135+
136+
it('deliverBringOutYourDead does not call sendRemoteMessage', async () => {
137+
const remote = makeRemote();
138+
139+
const crankResult = await remote.deliverBringOutYourDead();
140+
expect(mockRemoteComms.sendRemoteMessage).not.toHaveBeenCalled();
141+
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
142+
});
143+
144+
it('redeemOcapURL calls sendRemoteMessage correctly and handles expected reply (success)', async () => {
145+
const remote = makeRemote();
146+
const mockOcapURL = 'as if it was a URL';
147+
const mockURLResolutionRRef = 'ro+6';
148+
const mockURLResolutionKRef = 'ko1';
149+
const expectedReplyKey = '1';
150+
151+
const urlPromise = remote.redeemOcapURL(mockOcapURL);
152+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
153+
mockRemotePeerId,
154+
JSON.stringify({
155+
method: 'redeemURL',
156+
params: [mockOcapURL, expectedReplyKey],
157+
}),
158+
);
159+
const redeemURLReply = {
160+
method: 'redeemURLReply',
161+
params: [true, expectedReplyKey, mockURLResolutionRRef],
162+
};
163+
await remote.handleRemoteMessage(JSON.stringify(redeemURLReply));
164+
const kref = await urlPromise;
165+
expect(kref).toBe(mockURLResolutionKRef);
166+
expect(
167+
mockKernelStore.translateRefEtoK(mockRemoteId, mockURLResolutionRRef),
168+
).toBe(mockURLResolutionKRef);
169+
});
170+
171+
it('redeemOcapURL calls sendRemoteMessage correctly and handles expected reply (failure)', async () => {
172+
const remote = makeRemote();
173+
const mockOcapURL = 'as if it was a URL';
174+
const expectedReplyKey = '1';
175+
176+
const urlPromise = remote.redeemOcapURL(mockOcapURL);
177+
expect(mockRemoteComms.sendRemoteMessage).toHaveBeenCalledWith(
178+
mockRemotePeerId,
179+
JSON.stringify({
180+
method: 'redeemURL',
181+
params: [mockOcapURL, expectedReplyKey],
182+
}),
183+
);
184+
const redeemURLReply = {
185+
method: 'redeemURLReply',
186+
params: [false, expectedReplyKey],
187+
};
188+
await remote.handleRemoteMessage(JSON.stringify(redeemURLReply));
189+
await expect(urlPromise).rejects.toThrow(
190+
`vitest ignores this string but lint complains if it's not here`,
191+
);
192+
});
193+
194+
it('handleRemoteMessage handles deliver message', async () => {
195+
const remote = makeRemote();
196+
const targetRRef = 'ro+1';
197+
const targetKRef = 'ko1';
198+
const resultRRef = 'rp+2';
199+
const resultKRef = 'kp1';
200+
const message: Message = {
201+
methargs: { body: '["method",["arg1","arg2"]]', slots: [] },
202+
result: resultRRef,
203+
};
204+
const delivery = JSON.stringify({
205+
method: 'deliver',
206+
params: ['message', targetRRef, message],
207+
});
208+
const reply = await remote.handleRemoteMessage(delivery);
209+
expect(reply).toBe('');
210+
expect(mockKernelQueue.enqueueSend).toHaveBeenCalledWith(targetKRef, {
211+
methargs: message.methargs,
212+
result: resultKRef,
213+
});
214+
expect(mockKernelStore.translateRefEtoK(mockRemoteId, targetRRef)).toBe(
215+
targetKRef,
216+
);
217+
expect(mockKernelStore.translateRefEtoK(mockRemoteId, resultRRef)).toBe(
218+
resultKRef,
219+
);
220+
});
221+
222+
it('handleRemoteMessage handles deliver notify', async () => {
223+
const remote = makeRemote();
224+
const promiseRRef = 'rp+3';
225+
const promiseKRef = 'kp1';
226+
const resolutions: VatOneResolution[] = [
227+
[promiseRRef, false, { body: '"resolved value"', slots: [] }],
228+
];
229+
const notify = JSON.stringify({
230+
method: 'deliver',
231+
params: ['notify', resolutions],
232+
});
233+
const reply = await remote.handleRemoteMessage(notify);
234+
expect(reply).toBe('');
235+
expect(mockKernelQueue.resolvePromises).toHaveBeenCalledWith(mockRemoteId, [
236+
[promiseKRef, false, { body: '"resolved value"', slots: [] }],
237+
]);
238+
});
239+
240+
it('handleRemoteMessage handles bogus deliver', async () => {
241+
const remote = makeRemote();
242+
const delivery = JSON.stringify({
243+
method: 'deliver',
244+
params: ['bogus'],
245+
});
246+
await expect(remote.handleRemoteMessage(delivery)).rejects.toThrow(
247+
'unknown remote delivery method bogus',
248+
);
249+
});
250+
251+
it('handleRemoteMessage handles redeemURL request', async () => {
252+
const remote = makeRemote();
253+
const mockOcapURL = 'as if it was a URL';
254+
const mockReplyKey = 'replyKey';
255+
const replyKRef = 'ko100';
256+
const replyRRef = 'ro+1';
257+
const request = JSON.stringify({
258+
method: 'redeemURL',
259+
params: [mockOcapURL, mockReplyKey],
260+
});
261+
mockKernelStore.kv.set(`e.nextObjectId.r0`, `1`); // mock effects of stuff that was never called
262+
const reply = await remote.handleRemoteMessage(request);
263+
expect(mockRemoteComms.redeemLocalOcapURL).toHaveBeenCalledWith(
264+
mockOcapURL,
265+
);
266+
expect(reply).toBe(
267+
JSON.stringify({
268+
method: 'redeemURLReply',
269+
params: [true, mockReplyKey, replyRRef],
270+
}),
271+
);
272+
expect(
273+
mockKernelStore.translateRefKtoE(mockRemoteId, replyKRef, false),
274+
).toBe(replyRRef);
275+
});
276+
277+
it('handleRemoteMessage rejects bogus message type', async () => {
278+
const remote = makeRemote();
279+
const request = JSON.stringify({
280+
method: 'bogus',
281+
params: [],
282+
});
283+
await expect(remote.handleRemoteMessage(request)).rejects.toThrow(
284+
'unknown remote message type bogus',
285+
);
286+
});
287+
});

0 commit comments

Comments
 (0)