diff --git a/a3p-integration/proposals/z:acceptance/core-eval.test.js b/a3p-integration/proposals/z:acceptance/core-eval.test.js index 0fc33270508..7b92e35a6cb 100644 --- a/a3p-integration/proposals/z:acceptance/core-eval.test.js +++ b/a3p-integration/proposals/z:acceptance/core-eval.test.js @@ -1,7 +1,10 @@ +/* global setTimeout */ + import test from 'ava'; import { readFile, writeFile } from 'node:fs/promises'; -import { agd, evalBundles, waitForBlock } from '@agoric/synthetic-chain'; +import { agd, evalBundles } from '@agoric/synthetic-chain'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; const SUBMISSION_DIR = 'core-eval-test-submission'; @@ -45,7 +48,12 @@ test(`core eval works`, async t => { await evalBundles(SUBMISSION_DIR); - await waitForBlock(2); // enough time for core eval to execute ? + const actualValue = await retryUntilCondition( + async () => readPublished(nodePath), + value => value === nodeValue, + 'core eval not processed yet', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); - t.is(await readPublished(nodePath), nodeValue); + t.is(actualValue, nodeValue); }); diff --git a/a3p-integration/proposals/z:acceptance/exitOffer.js b/a3p-integration/proposals/z:acceptance/exitOffer.js deleted file mode 100644 index 959e9ed7c74..00000000000 --- a/a3p-integration/proposals/z:acceptance/exitOffer.js +++ /dev/null @@ -1,98 +0,0 @@ -// Note: limit imports to node modules for portability -/* eslint-env node */ -import { parseArgs, promisify } from 'node:util'; -import { execFile } from 'node:child_process'; -import { writeFile, mkdtemp, rm } from 'node:fs/promises'; -import { join } from 'node:path'; - -const options = /** @type {const} */ ({ - id: { type: 'string' }, - from: { type: 'string' }, - bin: { type: 'string', default: '/usr/src/agoric-sdk/node_modules/.bin' }, -}); - -const Usage = ` -Try to exit an offer, reclaiming any associated payments. - - node exitOffer.js --id ID --from FROM [--bin PATH] - -Options: - --id - --from
- - --bin default: ${options.bin.default} -`; - -const badUsage = () => { - const reason = new Error(Usage); - reason.name = 'USAGE'; - throw reason; -}; - -const { stringify: jq } = JSON; -// limited to JSON data: no remotables/promises; no undefined. -const toCapData = data => ({ body: `#${jq(data)}`, slots: [] }); - -const { entries } = Object; -/** - * @param {Record} obj - e.g. { color: 'blue' } - * @returns {string[]} - e.g. ['--color', 'blue'] - */ -const flags = obj => - entries(obj) - .map(([k, v]) => [`--${k}`, v]) - .flat(); - -const execP = promisify(execFile); - -const showAndRun = (file, args) => { - console.log('$', file, ...args); - return execP(file, args); -}; - -const withTempFile = async (tail, fn) => { - const tmpDir = await mkdtemp('offers-'); - const tmpFile = join(tmpDir, tail); - try { - const result = await fn(tmpFile); - return result; - } finally { - await rm(tmpDir, { recursive: true, force: true }).catch(err => - console.error(err), - ); - } -}; - -const doAction = async (action, from) => { - await withTempFile('offer.json', async tmpOffer => { - await writeFile(tmpOffer, jq(toCapData(action))); - - const out = await showAndRun('agoric', [ - 'wallet', - ...flags({ 'keyring-backend': 'test' }), - 'send', - ...flags({ offer: tmpOffer, from }), - ]); - return out.stdout; - }); -}; - -const main = async (argv, env) => { - const { values } = parseArgs({ args: argv.slice(2), options }); - const { id: offerId, from, bin } = values; - (offerId && from) || badUsage(); - - env.PATH = `${bin}:${env.PATH}`; - const action = { method: 'tryExitOffer', offerId }; - const out = await doAction(action, from); - console.log(out); -}; - -main(process.argv, process.env).catch(e => { - if (e.name === 'USAGE' || e.code === 'ERR_PARSE_ARGS_UNKNOWN_OPTION') { - console.error(e.message); - } else { - console.error(e); - } - process.exit(1); -}); diff --git a/a3p-integration/proposals/z:acceptance/localchain.test.js b/a3p-integration/proposals/z:acceptance/localchain.test.js index b7145c64d6e..3b8cd9c7e86 100644 --- a/a3p-integration/proposals/z:acceptance/localchain.test.js +++ b/a3p-integration/proposals/z:acceptance/localchain.test.js @@ -1,6 +1,8 @@ -import test from 'ava'; +/* global setTimeout */ -import { agd, evalBundles, waitForBlock } from '@agoric/synthetic-chain'; +import test from 'ava'; +import { agd, evalBundles } from '@agoric/synthetic-chain'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; const SUBMISSION_DIR = 'localchaintest-submission'; @@ -27,7 +29,12 @@ test(`localchain passes tests`, async t => { const nodePath = 'test.localchain'; const nodeValue = JSON.stringify({ success: true }); - await waitForBlock(2); // enough time for core eval to execute ? + const actualValue = await retryUntilCondition( + async () => readPublished(nodePath), + value => value === nodeValue, + 'core eval not processed yet', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); - t.is(await readPublished(nodePath), nodeValue); + t.is(actualValue, nodeValue); }); diff --git a/a3p-integration/proposals/z:acceptance/valueVow.test.js b/a3p-integration/proposals/z:acceptance/valueVow.test.js index 1193740b541..81cbae6de82 100644 --- a/a3p-integration/proposals/z:acceptance/valueVow.test.js +++ b/a3p-integration/proposals/z:acceptance/valueVow.test.js @@ -9,12 +9,12 @@ import '@endo/init/debug.js'; import { evalBundles, getIncarnation, - waitForBlock, GOV1ADDR as GETTER, // not particular to governance, just a handy wallet GOV2ADDR as SETTER, // not particular to governance, just a handy wallet } from '@agoric/synthetic-chain'; import { makeWalletUtils } from './test-lib/wallet.js'; import { networkConfig } from './test-lib/index.js'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; const START_VALUEVOW_DIR = 'start-valueVow'; const RESTART_VALUEVOW_DIR = 'restart-valueVow'; @@ -44,21 +44,21 @@ test('vow survives restart', async t => { }); t.log('confirm the value is not in offer results'); - await waitForBlock(2); - { - /** @type {any} */ - const getterStatus = await walletUtils.readLatestHead( - `published.wallet.${GETTER}`, - ); - console.log('current: ', inspect(getterStatus, { depth: 10 })); - t.like(getterStatus, { - status: { - id: 'get-value', - }, - updated: 'offerStatus', - }); - t.false('result' in getterStatus.status, 'no result yet'); - } + let getterStatus = await retryUntilCondition( + async () => walletUtils.readLatestHead(`published.wallet.${GETTER}`), + value => value.status.id === 'get-value' && value.updated === 'offerStatus', + 'Offer get-value not succeeded', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); + + console.log('current: ', inspect(getterStatus, { depth: 10 })); + t.like(getterStatus, { + status: { + id: 'get-value', + }, + updated: 'offerStatus', + }); + t.false('result' in getterStatus.status, 'no result yet'); t.log('restart valueVow'); await evalBundles(RESTART_VALUEVOW_DIR); @@ -82,11 +82,7 @@ test('vow survives restart', async t => { }); t.log('confirm the value is now in offer results'); - { - const getterStatus = await walletUtils.readLatestHead( - `published.wallet.${GETTER}`, - ); + getterStatus = await walletUtils.readLatestHead(`published.wallet.${GETTER}`); - t.like(getterStatus, { status: { result: offerArgs.value } }); - } + t.like(getterStatus, { status: { result: offerArgs.value } }); }); diff --git a/a3p-integration/proposals/z:acceptance/wallet.test.js b/a3p-integration/proposals/z:acceptance/wallet.test.js index 1c3e80395e8..8d7375f385a 100644 --- a/a3p-integration/proposals/z:acceptance/wallet.test.js +++ b/a3p-integration/proposals/z:acceptance/wallet.test.js @@ -1,3 +1,5 @@ +/* global fetch setTimeout */ + import test from 'ava'; import '@endo/init'; import { @@ -6,14 +8,21 @@ import { GOV1ADDR, GOV2ADDR, CHAINID, - waitForBlock, } from '@agoric/synthetic-chain'; -import { $ } from 'execa'; +import { execFileSync } from 'child_process'; import { agd, getBalances, replaceTemplateValuesInFile, } from './test-lib/utils.js'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; +import { makeWalletUtils } from './test-lib/wallet.js'; +import { networkConfig } from './test-lib/index.js'; + +const walletUtils = await makeWalletUtils( + { setTimeout, execFileSync, fetch }, + networkConfig, +); test.serial(`send invitation via namesByAddress`, async t => { const SUBMISSION_DIR = 'invitation-test-submission'; @@ -43,20 +52,23 @@ test.serial(`send invitation via namesByAddress`, async t => { }); test.serial('exitOffer tool reclaims stuck payment', async t => { - const offerId = 'bad-invitation-15'; // offer submitted on proposal upgrade-15 with an incorrect method name - const from = 'gov1'; - - const before = await getBalances([GOV1ADDR], 'uist'); - t.log('uist balance before:', before); + const istBalanceBefore = await getBalances([GOV1ADDR], 'uist'); - await $`node ./exitOffer.js --id ${offerId} --from ${from} `; - await waitForBlock(2); + const offerId = 'bad-invitation-15'; // offer submitted on proposal upgrade-15 with an incorrect method name + await walletUtils.broadcastBridgeAction(GOV1ADDR, { + method: 'tryExitOffer', + offerId, + }); - const after = await getBalances([GOV1ADDR], 'uist'); - t.log('uist balance after:', after); + const istBalanceAfter = await retryUntilCondition( + async () => getBalances([GOV1ADDR], 'uist'), + istBalance => istBalance > istBalanceBefore, + 'tryExitOffer failed to reclaim stuck payment ', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); t.true( - after > before, + istBalanceAfter > istBalanceBefore, 'The IST balance should increase after reclaiming the stuck payment', ); }); @@ -98,7 +110,6 @@ test.serial(`ante handler sends fee only to vbank/reserve`, async t => { { chainId: CHAINID, from: GOV1ADDR, yes: true }, ); - await waitForBlock(); t.like(result, { code: 0 }); const [feeCollectorEndBalances, vbankReserveEndBalances] = await getBalances([