Skip to content

Commit dd02fbc

Browse files
committed
feat: add tests for RemoteHandle.ts
1 parent 5377739 commit dd02fbc

7 files changed

Lines changed: 326 additions & 123 deletions

File tree

packages/ocap-kernel/src/Kernel.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ export class Kernel {
343343
#establishRemote(peerId: string): RemoteHandle {
344344
const remoteComms = this.#getRemoteComms();
345345
const remoteId = this.#kernelStore.getNextRemoteId();
346-
this.#kernelStore.initEndpoint(remoteId);
347346
const remote = RemoteHandle.make({
348347
remoteId,
349348
peerId,

packages/ocap-kernel/src/RemoteHandle.test.ts

Lines changed: 155 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { KernelQueue } from './KernelQueue.ts';
66
import { RemoteHandle } from './RemoteHandle.ts';
77
import { makeKernelStore } from './store/index.ts';
88
import type { KernelStore } from './store/index.ts';
9+
import { parseRef } from './store/utils/parse-ref.ts';
910
import type { Message, RemoteComms, RRef } from './types.ts';
1011
import { makeMapKernelDatabase } from '../test/storage.ts';
1112

@@ -15,6 +16,8 @@ let mockKernelQueue: KernelQueue;
1516
const mockRemoteId = 'r0';
1617
const mockRemotePeerId = 'remotePeerId';
1718

19+
/* eslint-disable vitest/no-conditional-expect */
20+
1821
/**
1922
* Fabricate a mock remote for testing purposes
2023
*
@@ -68,7 +71,7 @@ describe('RemoteHandle', () => {
6871
params: ['message', target, message],
6972
}),
7073
);
71-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
74+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
7275
});
7376

7477
it('deliverNotify calls sendRemoteMessage with correct delivery message', async () => {
@@ -85,7 +88,7 @@ describe('RemoteHandle', () => {
8588
params: ['notify', resolutions],
8689
}),
8790
);
88-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
91+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
8992
});
9093

9194
it('deliverDropExports calls sendRemoteMessage with correct delivery message', async () => {
@@ -100,7 +103,7 @@ describe('RemoteHandle', () => {
100103
params: ['dropExports', rrefs],
101104
}),
102105
);
103-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
106+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
104107
});
105108

106109
it('deliverRetireExports calls sendRemoteMessage with correct delivery message', async () => {
@@ -115,7 +118,7 @@ describe('RemoteHandle', () => {
115118
params: ['retireExports', rrefs],
116119
}),
117120
);
118-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
121+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
119122
});
120123

121124
it('deliverRetireImports calls sendRemoteMessage with correct delivery message', async () => {
@@ -130,15 +133,15 @@ describe('RemoteHandle', () => {
130133
params: ['retireImports', rrefs],
131134
}),
132135
);
133-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
136+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
134137
});
135138

136139
it('deliverBringOutYourDead does not call sendRemoteMessage', async () => {
137140
const remote = makeRemote();
138141

139142
const crankResult = await remote.deliverBringOutYourDead();
140143
expect(mockRemoteComms.sendRemoteMessage).not.toHaveBeenCalled();
141-
expect(crankResult).toStrictEqual({ didDelivery: mockRemoteId });
144+
expect(crankResult).toStrictEqual({ didDelivery: remote.remoteId });
142145
});
143146

144147
it('redeemOcapURL calls sendRemoteMessage correctly and handles expected reply (success)', async () => {
@@ -164,7 +167,7 @@ describe('RemoteHandle', () => {
164167
const kref = await urlPromise;
165168
expect(kref).toBe(mockURLResolutionKRef);
166169
expect(
167-
mockKernelStore.translateRefEtoK(mockRemoteId, mockURLResolutionRRef),
170+
mockKernelStore.translateRefEtoK(remote.remoteId, mockURLResolutionRRef),
168171
).toBe(mockURLResolutionKRef);
169172
});
170173

@@ -211,10 +214,10 @@ describe('RemoteHandle', () => {
211214
methargs: message.methargs,
212215
result: resultKRef,
213216
});
214-
expect(mockKernelStore.translateRefEtoK(mockRemoteId, targetRRef)).toBe(
217+
expect(mockKernelStore.translateRefEtoK(remote.remoteId, targetRRef)).toBe(
215218
targetKRef,
216219
);
217-
expect(mockKernelStore.translateRefEtoK(mockRemoteId, resultRRef)).toBe(
220+
expect(mockKernelStore.translateRefEtoK(remote.remoteId, resultRRef)).toBe(
218221
resultKRef,
219222
);
220223
});
@@ -232,9 +235,148 @@ describe('RemoteHandle', () => {
232235
});
233236
const reply = await remote.handleRemoteMessage(notify);
234237
expect(reply).toBe('');
235-
expect(mockKernelQueue.resolvePromises).toHaveBeenCalledWith(mockRemoteId, [
236-
[promiseKRef, false, { body: '"resolved value"', slots: [] }],
237-
]);
238+
expect(mockKernelQueue.resolvePromises).toHaveBeenCalledWith(
239+
remote.remoteId,
240+
[[promiseKRef, false, { body: '"resolved value"', slots: [] }]],
241+
);
242+
});
243+
244+
it('handleRemoteMessage handles deliver dropExports', async () => {
245+
const remote = makeRemote();
246+
247+
// Note that vat v1 does not exist; we're just pretending the test object
248+
// came from there (because it had to come from *somewhere*).
249+
const koref = mockKernelStore.initKernelObject('v1');
250+
const [kpref] = mockKernelStore.initKernelPromise();
251+
252+
// Pretend these refs had earlier been imported into the test remote from
253+
// our kernel (as if they had, say, appeared in message slots) and thence were
254+
// exported at the remote end. This way they'll be here to be dropped when
255+
// a request to do so is "received".
256+
const roref = mockKernelStore.translateRefKtoE(
257+
remote.remoteId,
258+
koref,
259+
true,
260+
);
261+
const rpref = mockKernelStore.translateRefKtoE(
262+
remote.remoteId,
263+
kpref,
264+
true,
265+
);
266+
267+
const drops = [
268+
mockKernelStore.invertRRef(roref),
269+
mockKernelStore.invertRRef(rpref),
270+
];
271+
272+
const krefs = drops.map((rref) => {
273+
const result = mockKernelStore.translateRefEtoK(remote.remoteId, rref);
274+
return result;
275+
});
276+
for (const kref of krefs) {
277+
const { isPromise } = parseRef(kref);
278+
if (isPromise) {
279+
expect(mockKernelStore.getRefCount(kref)).toBe(1);
280+
} else {
281+
expect(mockKernelStore.getObjectRefCount(kref)).toStrictEqual({
282+
reachable: 1,
283+
recognizable: 1,
284+
});
285+
}
286+
}
287+
288+
// Now have the "other end" drop them.
289+
const dropExports = JSON.stringify({
290+
method: 'deliver',
291+
params: ['dropExports', drops],
292+
});
293+
const reply = await remote.handleRemoteMessage(dropExports);
294+
295+
expect(reply).toBe('');
296+
for (const kref of krefs) {
297+
const { isPromise } = parseRef(kref);
298+
if (isPromise) {
299+
expect(mockKernelStore.getRefCount(kref)).toBe(1);
300+
} else {
301+
expect(mockKernelStore.getObjectRefCount(kref)).toStrictEqual({
302+
reachable: 0,
303+
recognizable: 1,
304+
});
305+
}
306+
}
307+
});
308+
309+
it('handleRemoteMessage handles deliver retireExports', async () => {
310+
const remote = makeRemote();
311+
312+
// Note that vat v1 does not exist; we're just pretending the test object
313+
// came from there (because it had to come from *somewhere*).
314+
const koref = mockKernelStore.initKernelObject('v1');
315+
316+
// Pretend this ref had earlier been imported into the test remote from our
317+
// kernel (as if it had, say, appeared in message slots) and thence wwas
318+
// exported at the remote end. This way it'll be here to be retired when a
319+
// request to do so is "received".
320+
const roref = mockKernelStore.translateRefKtoE(
321+
remote.remoteId,
322+
koref,
323+
true,
324+
);
325+
326+
const toRetireRRef = mockKernelStore.invertRRef(roref);
327+
328+
const kref = mockKernelStore.translateRefEtoK(
329+
remote.remoteId,
330+
toRetireRRef,
331+
);
332+
expect(mockKernelStore.getObjectRefCount(kref)).toStrictEqual({
333+
reachable: 1,
334+
recognizable: 1,
335+
});
336+
337+
// Before we can retire, we have to drop, so pretend that happened too
338+
mockKernelStore.clearReachableFlag(remote.remoteId, kref);
339+
340+
// Now have the "other end" retire them.
341+
const retireExports = JSON.stringify({
342+
method: 'deliver',
343+
params: ['retireExports', [toRetireRRef]],
344+
});
345+
const reply = await remote.handleRemoteMessage(retireExports);
346+
347+
expect(reply).toBe('');
348+
expect(mockKernelStore.getObjectRefCount(kref)).toStrictEqual({
349+
reachable: 0,
350+
recognizable: 0,
351+
});
352+
});
353+
354+
it('handleRemoteMessage handles deliver retireImports', async () => {
355+
const remote = makeRemote();
356+
357+
// An object, as if it had been imported from the other end (and thus exported here)
358+
const roref = 'ro+1';
359+
const koref = mockKernelStore.translateRefEtoK(remote.remoteId, roref);
360+
361+
// As if we're no longer using it (which, in fact, we weren't), which is a
362+
// prequisite for a valid 'retireImports' delivery
363+
mockKernelStore.decrementRefCount(koref, 'test');
364+
mockKernelStore.clearReachableFlag(remote.remoteId, koref);
365+
366+
// Now have the "other end" retire the import.
367+
const retireImports = JSON.stringify({
368+
method: 'deliver',
369+
params: ['retireImports', [roref]],
370+
});
371+
const reply = await remote.handleRemoteMessage(retireImports);
372+
373+
expect(reply).toBe('');
374+
375+
// Object should have disappeared from the clists
376+
expect(() =>
377+
mockKernelStore.translateRefKtoE(remote.remoteId, koref, false),
378+
).toThrow(`unmapped kref "${koref}" endpoint="${remote.remoteId}"`);
379+
expect(mockKernelStore.erefToKref(remote.remoteId, roref)).toBeUndefined();
238380
});
239381

240382
it('handleRemoteMessage handles bogus deliver', async () => {
@@ -270,7 +412,7 @@ describe('RemoteHandle', () => {
270412
}),
271413
);
272414
expect(
273-
mockKernelStore.translateRefKtoE(mockRemoteId, replyKRef, false),
415+
mockKernelStore.translateRefKtoE(remote.remoteId, replyKRef, false),
274416
).toBe(replyRRef);
275417
});
276418

0 commit comments

Comments
 (0)