Skip to content

Commit 371010e

Browse files
committed
test(zone): Add coverage for stateShape expansion
1 parent 4565665 commit 371010e

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

packages/zone/test/exos.test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { M } from '@endo/patterns';
12
import {
23
annihilate,
34
getBaggage,
@@ -123,3 +124,80 @@ test.serial('vatData migrate to durableZone', t => {
123124
const baggage2 = getBaggage();
124125
testSecondZoneIncarnation(t, makeDurableZone(baggage2));
125126
});
127+
128+
test.serial('exoClass stateShape expansion', t => {
129+
annihilate();
130+
131+
// See ../../swingset-liveslots/test/virtual-objects/state-shape.test.js
132+
const stateShapeMismatch = { message: /stateShape mismatch/ };
133+
const HolderI = M.interface('Holder', {
134+
get: M.call().rest(M.arrayOf(M.string())).returns(M.record()),
135+
set: M.call(M.record()).returns(),
136+
});
137+
const initHolder = fields => ({ ...fields });
138+
const holderMethods = {
139+
get(...fields) {
140+
const { state } = this;
141+
// We require fields to be explicit because they are currently defined on
142+
// the state *prototype*.
143+
return Object.fromEntries(
144+
fields.flatMap(key => (key in state ? [[key, state[key]]] : [])),
145+
);
146+
},
147+
set(fields) {
148+
Object.assign(this.state, fields);
149+
},
150+
};
151+
const prepareHolder = (zone, stateShape) =>
152+
zone.exoClass('Holder', HolderI, initHolder, holderMethods, { stateShape });
153+
154+
const fields = ['foo', 'bar', 'baz']; // but "baz" is not initially present
155+
const baggage1 = getBaggage();
156+
const zone1 = makeDurableZone(baggage1);
157+
const makeHolder1 = prepareHolder(zone1, {
158+
foo: M.number(),
159+
bar: M.number(),
160+
});
161+
const holder1 = makeHolder1({ foo: 0, bar: 1 });
162+
t.deepEqual(holder1.get(...fields), { foo: 0, bar: 1 });
163+
holder1.set({ foo: 2, bar: 2 });
164+
t.deepEqual(holder1.get(...fields), { foo: 2, bar: 2 });
165+
t.throws(() => makeHolder1({ foo: 0, bar: 1, baz: 2 }));
166+
t.throws(() => makeHolder1({ foo: 0, bar: 'string' }));
167+
168+
nextLife();
169+
t.throws(
170+
() =>
171+
prepareHolder(makeDurableZone(getBaggage()), {
172+
foo: M.string(),
173+
bar: M.number(),
174+
}),
175+
stateShapeMismatch,
176+
'backwards-incompatible stateShape change',
177+
);
178+
179+
nextLife();
180+
t.throws(
181+
() =>
182+
prepareHolder(makeDurableZone(getBaggage()), {
183+
foo: M.or(M.number(), M.string()),
184+
bar: M.number(),
185+
baz: M.or(undefined, M.number()),
186+
}),
187+
stateShapeMismatch,
188+
'stateShape field value expansion (needs #7407)',
189+
);
190+
191+
nextLife();
192+
const baggage2 = getBaggage();
193+
const zone2 = makeDurableZone(baggage2);
194+
const makeHolder2 = prepareHolder(zone2, {
195+
foo: M.number(),
196+
bar: M.number(),
197+
baz: M.or(undefined, M.number()),
198+
});
199+
const holder2 = makeHolder2({ foo: 0, bar: 1, baz: 2 });
200+
t.deepEqual(holder2.get(...fields), { foo: 0, bar: 1, baz: 2 });
201+
holder2.set({ foo: 2, bar: 2, baz: undefined });
202+
t.deepEqual(holder2.get(...fields), { foo: 2, bar: 2, baz: undefined });
203+
});

0 commit comments

Comments
 (0)