From eb941d442717e9ed3b8c7641b69d8c1c74945480 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 9 Oct 2024 10:32:54 -0700 Subject: [PATCH] test: update new acceptance tests for results of priceFeed update --- .../z:acceptance/test-lib/sync-tools.js | 70 +++++++++++++++++++ .../proposals/z:acceptance/vaults.test.js | 40 +++++++++++ 2 files changed, 110 insertions(+) create mode 100644 a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js diff --git a/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js b/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js new file mode 100644 index 00000000000..70ffbee37db --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js @@ -0,0 +1,70 @@ +/** + * @file These tools mostly duplicate code that will be added in other PRs + * and eventually migrated to synthetic-chain. Sorry for the duplication. + */ + +/** + * @typedef {object} RetryOptions + * @property {number} [maxRetries] + * @property {number} [retryIntervalMs] + * @property {(...arg0: string[]) => void} [log] + * @property {(object) => void} [setTimeout] + * @property {string} [errorMessage=Error] + */ + +const ambientSetTimeout = globalThis.setTimeout; + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L10 + * + * @param {number} ms + * @param {*} sleepOptions + */ +const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => + new Promise(resolve => { + log(`Sleeping for ${ms}ms...`); + setTimeout(resolve, ms); + }); + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L24 + * + * @param {() => Promise} operation + * @param {(result: any) => boolean} condition + * @param {string} message + * @param {RetryOptions} options + */ +export const retryUntilCondition = async ( + operation, + condition, + message, + { maxRetries = 6, retryIntervalMs = 3500, log, setTimeout }, +) => { + console.log({ maxRetries, retryIntervalMs, message }); + let retries = 0; + + await null; + while (retries < maxRetries) { + try { + const result = await operation(); + log('RESULT', result); + if (condition(result)) { + return result; + } + } catch (error) { + if (error instanceof Error) { + log(`Error: ${error.message}`); + } else { + log(`Unknown error: ${String(error)}`); + } + } + + retries += 1; + console.log( + `Retry ${retries}/${maxRetries} - Waiting for ${retryIntervalMs}ms for ${message}...`, + ); + await sleep(retryIntervalMs, { log, setTimeout }); + } + + throw Error(`${message} condition failed after ${maxRetries} retries.`); +}; diff --git a/a3p-integration/proposals/z:acceptance/vaults.test.js b/a3p-integration/proposals/z:acceptance/vaults.test.js index eea9b5899e9..a33ac7319b8 100644 --- a/a3p-integration/proposals/z:acceptance/vaults.test.js +++ b/a3p-integration/proposals/z:acceptance/vaults.test.js @@ -7,12 +7,52 @@ import { adjustVault, closeVault, getISTBalance, + getPriceQuote, + pushPrices, getContractInfo, ATOM_DENOM, USER1ADDR, waitForBlock, + registerOraclesForBrand, + generateOracleMap, } from '@agoric/synthetic-chain'; import { getBalances, agopsVaults } from './test-lib/utils.js'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; + +export const scale6 = x => BigInt(x * 1_000_000); + +// There may be a new vaultFactory that doesn't have prices yet, so we publish +// prices now +test.before(async t => { + const pushPriceRetryOpts = { + maxRetries: 5, // arbitrary + retryIntervalMs: 5000, // in ms + }; + t.context = { + roundId: 1, + retryOpts: { + pushPriceRetryOpts, + }, + }; + const oraclesByBrand = generateOracleMap('z-acc', ['ATOM']); + await registerOraclesForBrand('ATOM', oraclesByBrand); + + const price = 15.2; + // @ts-expect-error t.context is fine + await pushPrices(price, 'ATOM', oraclesByBrand, t.context.roundId); + + await retryUntilCondition( + () => getPriceQuote('ATOM'), + res => res === `+${scale6(price).toString()}`, + 'price not pushed yet', + { + log: t.log, + setTimeout: globalThis.setTimeout, + // @ts-expect-error t.context is fine + ...t.context.pushPriceRetryOpts, + }, + ); +}); test.serial('attempt to open vaults under the minimum amount', async t => { const activeVaultsBefore = await agopsVaults(USER1ADDR);