Skip to content

Commit

Permalink
test(vow): add test of more vow upgrade scenarios
Browse files Browse the repository at this point in the history
  • Loading branch information
mhofman authored and michaelfig committed Jun 12, 2024
1 parent 26b0505 commit bcecf52
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 13 deletions.
65 changes: 54 additions & 11 deletions packages/boot/test/upgrading/upgrade-vats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ test('upgrade vat-priceAuthority', async t => {
matchRef(t, reincarnatedRegistry.adminFacet, registry.adminFacet);
});

const dataOnly = obj => JSON.parse(JSON.stringify(obj));

test('upgrade vat-vow', async t => {
const bundles = {
vow: {
Expand All @@ -464,15 +466,37 @@ test('upgrade vat-vow', async t => {
t.log('test incarnation 0');
/** @type {Record<string, [settlementValue?: unknown, isRejection?: boolean]>} */
const localPromises = {
forever: [],
fulfilled: ['hello'],
rejected: ['goodbye', true],
promiseForever: [],
promiseFulfilled: ['hello'],
promiseRejected: ['goodbye', true],
};
const localVows = {
vowForever: [],
vowFulfilled: ['hello'],
vowRejected: ['goodbye', true],
vowPostUpgrade: [],
vowPromiseForever: [undefined, false, true],
};
await EV(vowRoot).makeLocalPromiseWatchers(localPromises);
t.deepEqual(await EV(vowRoot).getWatcherResults(), {
fulfilled: { status: 'fulfilled', value: 'hello' },
forever: { status: 'unsettled' },
rejected: { status: 'rejected', reason: 'goodbye' },
await EV(vowRoot).makeLocalVowWatchers(localVows);
t.deepEqual(dataOnly(await EV(vowRoot).getWatcherResults()), {
promiseForever: { status: 'unsettled' },
promiseFulfilled: { status: 'fulfilled', value: 'hello' },
promiseRejected: { status: 'rejected', reason: 'goodbye' },
vowForever: {
status: 'unsettled',
resolver: {},
},
vowFulfilled: { status: 'fulfilled', value: 'hello' },
vowRejected: { status: 'rejected', reason: 'goodbye' },
vowPostUpgrade: {
status: 'unsettled',
resolver: {},
},
vowPromiseForever: {
status: 'unsettled',
resolver: {},
},
});

t.log('restart');
Expand All @@ -481,16 +505,35 @@ test('upgrade vat-vow', async t => {
t.is(incarnationNumber, 1, 'vat must be reincarnated');

t.log('test incarnation 1');
t.deepEqual(await EV(vowRoot).getWatcherResults(), {
fulfilled: { status: 'fulfilled', value: 'hello' },
forever: {
const localVowsUpdates = {
vowPostUpgrade: ['bonjour'],
};
await EV(vowRoot).resolveVowWatchers(localVowsUpdates);
t.deepEqual(dataOnly(await EV(vowRoot).getWatcherResults()), {
promiseForever: {
status: 'rejected',
reason: {
name: 'vatUpgraded',
upgradeMessage: 'vat upgraded',
incarnationNumber: 0,
},
},
promiseFulfilled: { status: 'fulfilled', value: 'hello' },
promiseRejected: { status: 'rejected', reason: 'goodbye' },
vowForever: {
status: 'unsettled',
resolver: {},
},
vowFulfilled: { status: 'fulfilled', value: 'hello' },
vowRejected: { status: 'rejected', reason: 'goodbye' },
vowPostUpgrade: { status: 'fulfilled', value: 'bonjour' },
vowPromiseForever: {
status: 'rejected',
reason: {
name: 'vatUpgraded',
upgradeMessage: 'vat upgraded',
incarnationNumber: 0,
},
},
rejected: { status: 'rejected', reason: 'goodbye' },
});
});
49 changes: 47 additions & 2 deletions packages/boot/test/upgrading/vat-vow.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { Far } from '@endo/far';

export const buildRootObject = (_vatPowers, _args, baggage) => {
const zone = makeDurableZone(baggage);
const { watch } = prepareVowTools(zone.subZone('VowTools'));
const { watch, makeVowKit } = prepareVowTools(zone.subZone('VowTools'));

/** @type {MapStore<string, { status: 'unsettled' } | PromiseSettledResult<any>>} */
/** @typedef {{ status: 'unsettled', resolver?: import('@agoric/vow').VowResolver } | PromiseSettledResult<any>} WatcherResult */

/** @type {MapStore<string, WatcherResult>} */
const nameToResult = zone.mapStore('nameToResult');

const makeWatcher = zone.exoClass('Watcher', undefined, name => ({ name }), {
Expand Down Expand Up @@ -43,5 +45,48 @@ export const buildRootObject = (_vatPowers, _args, baggage) => {
watch(p, makeWatcher(name));
}
},
/** @param {Record<string, [settlementValue?: unknown, isRejection?: boolean, wrapInPromise?: boolean]>} localVows */
async makeLocalVowWatchers(localVows) {
for (const [name, settlement] of Object.entries(localVows)) {
const { vow, resolver } = makeVowKit();
nameToResult.init(name, harden({ status: 'unsettled', resolver }));
if (settlement.length) {
let [settlementValue, isRejection] = settlement;
const wrapInPromise = settlement[2];
if (wrapInPromise) {
if (isRejection) {
settlementValue = Promise.reject(settlementValue);
isRejection = false;
} else if (settlementValue === undefined) {
// Consider an undefined value as no settlement
settlementValue = new Promise(() => {});
} else {
settlementValue = Promise.resolve(settlementValue);
}
}
if (isRejection) {
resolver.reject(settlementValue);
} else {
resolver.resolve(settlementValue);
}
}
watch(vow, makeWatcher(name));
}
},
/** @param {Record<string, [settlementValue: unknown, isRejection?: boolean]>} localVows */
async resolveVowWatchers(localVows) {
for (const [name, settlement] of Object.entries(localVows)) {
const { status, resolver } = nameToResult.get(name);
if (status !== 'unsettled' || !resolver) {
throw Error(`Invalid pending vow for ${name}`);
}
const [settlementValue, isRejection] = settlement;
if (isRejection) {
resolver.reject(settlementValue);
} else {
resolver.resolve(settlementValue);
}
}
},
});
};

0 comments on commit bcecf52

Please sign in to comment.