Skip to content

Commit e263d77

Browse files
committed
Add virtual object (exo) tests
1 parent b061659 commit e263d77

File tree

4 files changed

+582
-0
lines changed

4 files changed

+582
-0
lines changed

packages/kernel-test/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@
4747
"test:watch": "vitest --config vitest.config.ts"
4848
},
4949
"devDependencies": {
50+
"@agoric/store": "0.9.3-u19.0",
5051
"@arethetypeswrong/cli": "^0.17.4",
52+
"@endo/exo": "^1.5.9",
53+
"@endo/patterns": "^1.5.0",
5154
"@metamask/auto-changelog": "^5.0.1",
5255
"@metamask/eslint-config": "^14.0.0",
5356
"@metamask/eslint-config-nodejs": "^14.0.0",
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import '@ocap/shims/endoify';
2+
import { kunser } from '@ocap/kernel';
3+
import { makeSQLKernelDatabase } from '@ocap/store/sqlite/nodejs';
4+
import { waitUntilQuiescent } from '@ocap/utils';
5+
import { describe, expect, it } from 'vitest';
6+
7+
import {
8+
extractVatLogs,
9+
getBundleSpec,
10+
makeKernel,
11+
runTestVats,
12+
} from './utils.ts';
13+
14+
const origStdoutWrite = process.stdout.write.bind(process.stdout);
15+
let buffered: string = '';
16+
// @ts-expect-error Some type def used by lint is just wrong (compiler likes it ok, but lint whines)
17+
process.stdout.write = (buffer: string, encoding, callback): void => {
18+
buffered += buffer;
19+
origStdoutWrite(buffer, encoding, callback);
20+
};
21+
22+
const testSubcluster = {
23+
bootstrap: 'exoTest',
24+
forceReset: true,
25+
vats: {
26+
exoTest: {
27+
bundleSpec: getBundleSpec('exo-vat'),
28+
parameters: {
29+
name: 'ExoTest',
30+
},
31+
},
32+
},
33+
};
34+
35+
describe('virtual objects functionality', async () => {
36+
it('successfully creates and uses exo objects and scalar stores', async () => {
37+
const kernelDatabase = await makeSQLKernelDatabase({
38+
dbFilename: ':memory:',
39+
});
40+
const kernel = await makeKernel(kernelDatabase, true);
41+
buffered = '';
42+
43+
const bootstrapResult = await runTestVats(kernel, testSubcluster);
44+
expect(bootstrapResult).toBe('exo-test-complete');
45+
await waitUntilQuiescent();
46+
const vatLogs = extractVatLogs(buffered);
47+
48+
// Verify exo objects were created and used
49+
expect(vatLogs).toContain('ExoTest: initializing state');
50+
expect(vatLogs).toContain('ExoTest: counter value from baggage: 0');
51+
expect(vatLogs).toContain(
52+
'::> ExoTest: Created counter with initial value: 10',
53+
);
54+
expect(vatLogs).toContain('::> ExoTest: Incremented counter by 5 to: 15');
55+
// Verify scalar map store functionality
56+
expect(vatLogs).toContain('::> ExoTest: Added 2 entries to map store');
57+
expect(vatLogs).toContain('::> ExoTest: Retrieved Alice from map store');
58+
// Verify scalar set store functionality
59+
expect(vatLogs).toContain('::> ExoTest: Added 2 entries to set store');
60+
// Verify exo validation works
61+
expect(vatLogs).toContain(
62+
'::> ExoTest: Successfully caught error on negative increment',
63+
);
64+
// Verify exoClassKit temperature converter
65+
expect(vatLogs).toContain('::> ExoTest: Temperature at 25°C =');
66+
expect(vatLogs).toContain('::> ExoTest: After setting to 68°F, celsius is');
67+
// Verify makeExo direct instance
68+
expect(vatLogs).toContain('::> ExoTest: SimpleCounter initial value:');
69+
expect(vatLogs).toContain('::> ExoTest: SimpleCounter after +7:');
70+
}, 30000);
71+
72+
it('preserves state across vat restarts', async () => {
73+
const kernelDatabase = await makeSQLKernelDatabase({
74+
dbFilename: ':memory:',
75+
});
76+
const kernel = await makeKernel(kernelDatabase, true);
77+
buffered = '';
78+
79+
// Run the test vat first time
80+
await runTestVats(kernel, testSubcluster);
81+
await waitUntilQuiescent();
82+
// Restart the vat
83+
await kernel.restartVat('v1');
84+
buffered = '';
85+
86+
// Create and send a message to the root
87+
await kernel.queueMessageFromKernel('ko1', 'resume', []);
88+
await waitUntilQuiescent();
89+
90+
// Extract logs to verify operations worked correctly
91+
const vatLogs = extractVatLogs(buffered);
92+
// Verify state was preserved
93+
expect(vatLogs).toContain('ExoTest: state already initialized');
94+
expect(vatLogs).toContain('ExoTest: Counter value after restart: 7');
95+
// Verify stores persistence
96+
expect(vatLogs).toContain('::> ExoTest: Map store size after restart: 2');
97+
expect(vatLogs).toContain('::> ExoTest: Set store size after restart: 2');
98+
}, 30000);
99+
100+
it('tests scalar store functionality', async () => {
101+
const kernelDatabase = await makeSQLKernelDatabase({
102+
dbFilename: ':memory:',
103+
});
104+
const kernel = await makeKernel(kernelDatabase, true);
105+
106+
// Run the test vat
107+
await runTestVats(kernel, testSubcluster);
108+
await waitUntilQuiescent();
109+
buffered = '';
110+
111+
const storeResult = await kernel.queueMessageFromKernel(
112+
'ko1',
113+
'testScalarStore',
114+
[],
115+
);
116+
await waitUntilQuiescent();
117+
const vatLogs = extractVatLogs(buffered);
118+
119+
// Verify test result
120+
expect(kunser(storeResult)).toBe('scalar-store-tests-complete');
121+
// Verify map store operations
122+
expect(vatLogs).toContain('::> ExoTest: Map store size:');
123+
expect(vatLogs).toContain('::> ExoTest: Map store keys:');
124+
expect(vatLogs).toContain("::> ExoTest: Map has 'charlie': true");
125+
// Verify set store operations
126+
expect(vatLogs).toContain('::> ExoTest: Set store size:');
127+
expect(vatLogs).toContain('::> ExoTest: Set has Charlie: true');
128+
}, 30000);
129+
130+
it('can create and use objects through messaging', async () => {
131+
const kernelDatabase = await makeSQLKernelDatabase({
132+
dbFilename: ':memory:',
133+
});
134+
const kernel = await makeKernel(kernelDatabase, true);
135+
136+
// Run the test vat
137+
await runTestVats(kernel, testSubcluster);
138+
await waitUntilQuiescent();
139+
buffered = '';
140+
141+
// Create a counter through messaging
142+
const counterResult = await kernel.queueMessageFromKernel(
143+
'ko1',
144+
'createCounter',
145+
[42],
146+
);
147+
await waitUntilQuiescent();
148+
149+
// Use the returned counter object
150+
const counterRef = JSON.parse(counterResult.body).slots[0];
151+
const incrementResult = await kernel.queueMessageFromKernel(
152+
counterRef,
153+
'increment',
154+
[5],
155+
);
156+
await waitUntilQuiescent();
157+
158+
// Add object to map store
159+
const personResult = await kernel.queueMessageFromKernel(
160+
'ko1',
161+
'createPerson',
162+
['Dave', 35],
163+
);
164+
await waitUntilQuiescent();
165+
166+
const personRef = JSON.parse(personResult.body).slots[0];
167+
await kernel.queueMessageFromKernel('ko1', 'addToMap', ['dave', personRef]);
168+
await waitUntilQuiescent();
169+
170+
// Get object from map store
171+
const retrievedPerson = await kernel.queueMessageFromKernel(
172+
'ko1',
173+
'getFromMap',
174+
['dave'],
175+
);
176+
await waitUntilQuiescent();
177+
const vatLogs = extractVatLogs(buffered);
178+
179+
// Verify counter was created and used
180+
expect(vatLogs).toContain(
181+
'::> ExoTest: Created new counter with value: 42',
182+
);
183+
expect(JSON.parse(incrementResult.body).body).toBe(47);
184+
// Verify map store operations through messaging
185+
expect(vatLogs).toContain('::> ExoTest: Created person Dave, age 35');
186+
expect(vatLogs).toContain('::> ExoTest: Added dave to map');
187+
expect(vatLogs).toContain('::> ExoTest: Found dave in map');
188+
// Verify the retrieved person object
189+
const personSlot = JSON.parse(retrievedPerson.body).slots[0];
190+
expect(personSlot).toBeDefined();
191+
}, 30000);
192+
});

0 commit comments

Comments
 (0)