From 30439948fd525329e53b6760e930dd38b04793bc Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Thu, 25 Jul 2024 14:28:17 -0700 Subject: [PATCH] test: restart basic-flow's orchestration flow --- .../orchestration/restart-contracts.test.ts | 98 +++++++++++++++++ .../scripts/testing/restart-basic-flows.js | 100 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 packages/builders/scripts/testing/restart-basic-flows.js diff --git a/packages/boot/test/orchestration/restart-contracts.test.ts b/packages/boot/test/orchestration/restart-contracts.test.ts index 4871efb7d6b..5e74eb59577 100644 --- a/packages/boot/test/orchestration/restart-contracts.test.ts +++ b/packages/boot/test/orchestration/restart-contracts.test.ts @@ -167,3 +167,101 @@ test('stakeAtom', async t => { t.is(await flushInboundQueue(), 1); t.true(hasResult(wd.getLatestUpdateRecord())); }); + +// Tests restart of an orchestration() flow while an IBC response is pending. +// TODO make this a macro so we test at arbitrary depth +test.serial('basicFlows', async t => { + const { + walletFactoryDriver, + buildProposal, + evalProposal, + bridgeUtils: { getInboundQueueLength, flushInboundQueue }, + } = t.context; + + t.log('start basicFlows'); + await evalProposal( + buildProposal('@agoric/builders/scripts/orchestration/init-basic-flows.js'), + ); + + t.log('making offer'); + const wallet = await walletFactoryDriver.provideSmartWallet('agoric1test'); + const id1 = 'make-orch-account'; + // send because it won't resolve + await wallet.sendOffer({ + id: id1, + invitationSpec: { + source: 'agoricContract', + instancePath: ['basicFlows'], + callPipe: [['makeOrchAccountInvitation']], + }, + proposal: {}, + offerArgs: { + chainName: 'cosmoshub', + }, + }); + // no errors and no result yet + t.like(wallet.getLatestUpdateRecord(), { + status: { + id: id1, + error: undefined, + numWantsSatisfied: 1, + payouts: {}, + result: undefined, // no property + }, + }); + t.is(getInboundQueueLength(), 1); + + const id2 = 'makePortfolio'; + await wallet.sendOffer({ + id: id2, + invitationSpec: { + source: 'agoricContract', + instancePath: ['basicFlows'], + callPipe: [['makePortfolioAccountInvitation']], + }, + proposal: {}, + offerArgs: { + chainNames: ['agoric', 'cosmoshub'], + }, + }); + // no errors and no result yet + t.like(wallet.getLatestUpdateRecord(), { + status: { + id: id2, + error: undefined, + numWantsSatisfied: 1, + payouts: {}, + result: undefined, // no property + }, + }); + t.is(getInboundQueueLength(), 2); + + t.log('restart basicFlows'); + await evalProposal( + buildProposal('@agoric/builders/scripts/testing/restart-basic-flows.js'), + ); + + t.log('flush and verify results'); + const beforeFlush = wallet.getLatestUpdateRecord(); + t.like(beforeFlush, { + status: { + result: undefined, + }, + }); + t.is(await flushInboundQueue(1), 1); + t.like(wallet.getLatestUpdateRecord(), { + status: { + id: id1, + error: undefined, + result: 'UNPUBLISHED', + }, + }); + t.is(await flushInboundQueue(1), 1); + t.like(wallet.getLatestUpdateRecord(), { + status: { + id: id2, + error: undefined, + result: 'UNPUBLISHED', + }, + }); +}); diff --git a/packages/builders/scripts/testing/restart-basic-flows.js b/packages/builders/scripts/testing/restart-basic-flows.js new file mode 100644 index 00000000000..ac4c7b3f02f --- /dev/null +++ b/packages/builders/scripts/testing/restart-basic-flows.js @@ -0,0 +1,100 @@ +/** + * @file This is for use in tests. + * Unlike most builder scripts, this one includes the proposal exports as well. + */ +import { + deeplyFulfilledObject, + makeTracer, + NonNullish, +} from '@agoric/internal'; +import { E } from '@endo/far'; + +/// + +const trace = makeTracer('RestartBasicFlows', true); + +/** + * @import {start as StartFn} from '@agoric/orchestration/src/examples/basic-flows.contract.js'; + */ + +/** + * @param {BootstrapPowers} powers + */ +export const restartBasicFlows = async ({ + consume: { + agoricNames, + board, + chainStorage, + chainTimerService, + cosmosInterchainService, + localchain, + + contractKits, + }, + instance: instances, +}) => { + trace(restartBasicFlows.name); + + // @ts-expect-error unknown instance + const instance = await instances.consume.basicFlows; + trace('instance', instance); + /** @type {StartedInstanceKit} */ + const kit = /** @type {any} */ (await E(contractKits).get(instance)); + + const marshaller = await E(board).getReadonlyMarshaller(); + + const privateArgs = await deeplyFulfilledObject( + harden({ + agoricNames, + localchain, + marshaller, + orchestrationService: cosmosInterchainService, + storageNode: E(NonNullish(await chainStorage)).makeChildNode( + 'basicFlows', + ), + timerService: chainTimerService, + }), + ); + + await E(kit.adminFacet).restartContract(privateArgs); + trace('done'); +}; +harden(restartBasicFlows); + +export const getManifest = () => { + return { + manifest: { + [restartBasicFlows.name]: { + consume: { + agoricNames: true, + board: true, + chainStorage: true, + chainTimerService: true, + cosmosInterchainService: true, + localchain: true, + + contractKits: true, + }, + instance: { + consume: { basicFlows: true }, + }, + }, + }, + }; +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async () => + harden({ + // Somewhat unorthodox, source the exports from this builder module + sourceSpec: '@agoric/builders/scripts/testing/restart-basic-flows.js', + getManifestCall: [getManifest.name], + }); + +export default async (homeP, endowments) => { + // import dynamically so the module can work in CoreEval environment + const dspModule = await import('@agoric/deploy-script-support'); + const { makeHelpers } = dspModule; + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval(restartBasicFlows.name, defaultProposalBuilder); +};