diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 67efddfcc81..05a8e4eaabf 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -57,7 +57,7 @@ jobs: # Prerequisites - uses: ./.github/actions/restore-node with: - node-version: 16.x + node-version: 18.18 keep-endo: 'true' # Select a branch on dapp to test against by adding text to the body of the diff --git a/packages/agoric-cli/tools/getting-started.js b/packages/agoric-cli/tools/getting-started.js index 40ceae5f278..dae31eeb298 100644 --- a/packages/agoric-cli/tools/getting-started.js +++ b/packages/agoric-cli/tools/getting-started.js @@ -1,4 +1,4 @@ -/* global process setTimeout clearTimeout setInterval clearInterval */ +/* global process setTimeout setInterval clearInterval */ import fs from 'fs'; import path from 'path'; @@ -10,32 +10,21 @@ import { spawn } from 'child_process'; import { makePspawn } from '../src/helpers.js'; -const TIMEOUT_SECONDS = 20 * 60; +const TIMEOUT_SECONDS = 3 * 60; const dirname = new URL('./', import.meta.url).pathname; -// To keep in sync with https://agoric.com/documentation/getting-started/ +// To keep in sync with https://docs.agoric.com/guides/getting-started/ // Note that we currently only test: // agoric init dapp-foo -// agoric install -// agoric start --reset -// agoric deploy ./contract/deploy.js ./api/deploy.js -// (For simple-exchange and autoswap, the above also makes and accepts offers) -// cd ui && yarn install -// cd ui && yarn start +// yarn install +// yarn start:docker +// yarn start:contract +// yarn start:ui export const gettingStartedWorkflowTest = async (t, options = {}) => { - const { - init: initOptions = [], - install: installOptions = [], - start: startOptions = [], - testUnsafePlugins = false, - } = options; - // FIXME: Do a search for an unused port or allow specification. - const PORT = '7999'; - process.env.PORT = PORT; - + const { init: initOptions = [] } = options; const pspawn = makePspawn({ spawn }); // Kill an entire process group. @@ -61,7 +50,6 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { const { AGORIC_CMD = JSON.stringify(defaultAgoricCmd()) } = process.env; const agoricCmd = JSON.parse(AGORIC_CMD); function myMain(args, opts = {}) { - // console.error('running agoric-cli', ...extraArgs, ...args); return pspawnStdout(agoricCmd[0], [...agoricCmd.slice(1), ...args], { stdio: ['ignore', 'pipe', 'inherit'], env: { ...process.env, DEBUG: 'agoric:debug' }, @@ -70,6 +58,14 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { }); } + function yarn(args) { + return pspawnStdout('yarn', args, { + stdio: ['ignore', 'pipe', 'inherit'], + env: { ...process.env }, + detached: true, + }); + } + const olddir = process.cwd(); const { name } = tmp.dirSync({ unsafeCleanup: true, @@ -83,7 +79,7 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { try { f(); } catch (e) { - // console.log(e); + console.log(e); } } if (sig) { @@ -105,122 +101,32 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { initOptions.push(...opts); } t.is( - await myMain([ - 'init', - '--dapp-template', - 'dapp-fungible-faucet', - ...initOptions, - 'dapp-foo', - ]), + await myMain(['init', ...initOptions, 'dapp-foo']), 0, 'init dapp-foo works', ); process.chdir('dapp-foo'); // ============== - // agoric install - if (process.env.AGORIC_INSTALL_OPTIONS) { - const opts = JSON.parse(process.env.AGORIC_INSTALL_OPTIONS); - installOptions.push(...opts); - } - t.is(await myMain(['install', ...installOptions]), 0, 'install works'); + // yarn install + t.is(await yarn(['install']), 0, 'yarn install works'); // ============== - // agoric start --reset - const startResult = makePromiseKit(); - - if (process.env.AGORIC_START_OPTIONS) { - const opts = JSON.parse(process.env.AGORIC_START_OPTIONS); - startOptions.push(...opts); - } - - // TODO: Allow this to work even if the port is already used. - const startP = myMain(['start', '--reset', ...startOptions]); - finalizers.push(() => pkill(startP.childProcess, 'SIGINT')); - - let stdoutStr = ''; - if (startP.childProcess.stdout) { - startP.childProcess.stdout.on('data', chunk => { - // console.log('stdout:', chunk.toString()); - stdoutStr += chunk.toString(); - if (stdoutStr.match(/(^|:\s+)swingset running$/m)) { - startResult.resolve(true); - } - }); - } - startP.childProcess.on('close', code => - startResult.reject(Error(`early termination: ${code}`)), - ); + // yarn start:docker + t.is(await yarn(['start:docker']), 0, 'yarn start:docker works'); - const timeout = setTimeout( - startResult.reject, - TIMEOUT_SECONDS * 1000, - 'timeout', - ); - t.is(await startResult.promise, true, `swingset running before timeout`); - clearTimeout(timeout); - - const openP = myMain(['open', '--no-browser'], { - stdio: ['ignore', 'pipe', 'inherit'], - }); - t.is(await openP, 0, `open --no-browser exits successfully`); - - const testDeploy = async (deployCmd, opts = {}) => { - const deployResult = makePromiseKit(); - const deployP = myMain( - ['deploy', `--hostport=127.0.0.1:${PORT}`, ...deployCmd], - { - stdio: [opts.stdin ? 'pipe' : 'ignore', 'pipe', 'inherit'], - }, - ); - - if (opts.stdin) { - // Write the input to stdin. - deployP.childProcess.stdin.write(opts.stdin); - deployP.childProcess.stdin.end(); - } - - finalizers.push(() => pkill(deployP.childProcess, 'SIGINT')); - const to = setTimeout( - deployResult.resolve, - TIMEOUT_SECONDS * 1000, - 'timeout', - ); - const done = await Promise.race([deployResult.promise, deployP]); - t.is(done, 0, `deploy ${deployCmd.join(' ')} successful before timeout`); - clearTimeout(to); - }; + // XXX: use abci_info endpoint to get block height + // sleep to let contract start + await new Promise(resolve => setTimeout(resolve, TIMEOUT_SECONDS)); // ============== - // agoric deploy ./contract/deploy.js ./api/deploy.js - await testDeploy(['./contract/deploy.js', './api/deploy.js']); - - for (const [suffix, code] of [ - ['/notthere', 404], - ['', 200], - ]) { - let urlResolve; - const url = `http://127.0.0.1:${PORT}${suffix}`; - const urlP = new Promise(resolve => (urlResolve = resolve)); - const urlReq = request(url, res => urlResolve(res.statusCode)); - urlReq.setTimeout(2000); - urlReq.on('error', err => urlResolve(`Cannot connect to ${url}: ${err}`)); - urlReq.end(); - const urlTimeout = setTimeout(urlResolve, 3000, 'timeout'); - const urlDone = await urlP; - clearTimeout(urlTimeout); - t.is(urlDone, code, `${url} gave status ${code}`); - } + // yarn start:contract + t.is(await yarn(['start:contract']), 0, 'yarn start:contract works'); // ============== - // cd ui && yarn start - const uiStartP = pspawn(`yarn`, ['start'], { - stdio: ['ignore', 'inherit', 'inherit'], - cwd: 'ui', - env: { ...process.env, PORT: '3000' }, - detached: true, - }); - finalizers.push(() => pkill(uiStartP.childProcess, 'SIGINT')); + // yarn start:ui + const startUiP = yarn(['start:ui']); + finalizers.push(() => pkill(startUiP.childProcess, 'SIGINT')); const uiListening = makePromiseKit(); let retries = 0; const ival = setInterval(() => { @@ -236,7 +142,7 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { return; } - const req = request('http://localhost:3000/', _res => { + const req = request('http://localhost:5173/', _res => { resolve('listening'); }); req.setTimeout(2000); @@ -251,20 +157,11 @@ export const gettingStartedWorkflowTest = async (t, options = {}) => { } }, 3000); t.is( - await Promise.race([uiStartP, uiListening.promise]), + await Promise.race([startUiP, uiListening.promise]), 'listening', - `cd ui && yarn start succeeded`, + `yarn start:ui succeeded`, ); clearInterval(ival); - - // Test that the Node.js `-r esm`-dependent plugin works. - await (testUnsafePlugins && - testDeploy( - ['--allow-unsafe-plugins', `${dirname}/resm-plugin/deploy.js`], - { stdin: 'yes\n' }, - )); - - // TODO: When it exists, Test that the Node.js native ESM plugin works. } finally { runFinalizers(); process.off('SIGINT', runFinalizers);