diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b5f2f2dc..36aabff68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@zombienet/orchestrator': specifier: ^0.0.52 version: 0.0.52(@polkadot/util@12.4.2)(@types/node@20.5.6) - execa: - specifier: ^8.0.1 - version: 8.0.1 inquirer: specifier: ^9.2.11 version: 9.2.11 @@ -1520,12 +1517,12 @@ packages: '@polkadot/types-support': 10.9.1 '@polkadot/util': 12.5.1 '@polkadot/util-crypto': 12.5.1(@polkadot/util@12.5.1) - '@polkadot/x-fetch': 12.4.1 + '@polkadot/x-fetch': 12.5.1 '@polkadot/x-global': 12.5.1 '@polkadot/x-ws': 12.5.1 eventemitter3: 5.0.1 - mock-socket: 9.2.1 - nock: 13.3.3 + mock-socket: 9.3.1 + nock: 13.3.4 tslib: 2.6.2 optionalDependencies: '@substrate/connect': 0.7.26 @@ -1950,14 +1947,6 @@ packages: '@polkadot/x-global': 12.5.1 tslib: 2.6.2 - /@polkadot/x-fetch@12.4.1: - resolution: {integrity: sha512-9EuUiXFwLAuyK8B/9i413vIGAKtD5LDcBJWSlsCknXMCn81Qy8Td7kdPohPVtXJkydcW+oW8Qoa2IYviCLjdpg==} - engines: {node: '>=16'} - dependencies: - '@polkadot/x-global': 12.4.1 - node-fetch: 3.3.2 - tslib: 2.6.2 - /@polkadot/x-fetch@12.5.1: resolution: {integrity: sha512-Bc019lOKCoQJrthiS+H3LwCahGtl5tNnb2HK7xe3DBQIUx9r2HsF/uEngNfMRUFkUYg5TPCLFbEWU8NIREBS1A==} engines: {node: '>=16'} @@ -1966,12 +1955,6 @@ packages: node-fetch: 3.3.2 tslib: 2.6.2 - /@polkadot/x-global@12.4.1: - resolution: {integrity: sha512-r83Bd/VE6Gq5aXhIX0DUQWn3XF1c9ZH5AxqD1wwUiU3DQ5sKcO9DXRm5+sJ9ZTZrAl0efkix97TAygH+GXWD7Q==} - engines: {node: '>=16'} - dependencies: - tslib: 2.6.2 - /@polkadot/x-global@12.4.2: resolution: {integrity: sha512-CwbjSt1Grmn56xAj+hGC8ZB0uZxMl92K+VkBH0KxjgcbAX/D24ZD/0ds8pAnUYrO4aYHYq2j2MAGVSMdHcMBAQ==} engines: {node: '>=16'} @@ -3815,21 +3798,6 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.1.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - dev: false - /external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -4067,11 +4035,6 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: false - /get-tsconfig@4.7.0: resolution: {integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==} dependencies: @@ -4315,11 +4278,6 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: false - /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} requiresBuild: true @@ -4588,11 +4546,6 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: false - /is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -4755,7 +4708,6 @@ packages: chalk: 2.4.2 diff-match-patch: 1.0.5 dev: true - bundledDependencies: [] /jsondiffpatch@0.5.0: resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} @@ -5160,11 +5112,6 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: false - /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} dev: true @@ -5311,10 +5258,6 @@ packages: yargs-parser: 20.2.4 yargs-unparser: 2.0.0 - /mock-socket@9.2.1: - resolution: {integrity: sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==} - engines: {node: '>= 8'} - /mock-socket@9.3.1: resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==} engines: {node: '>= 8'} @@ -5417,17 +5360,6 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true - /nock@13.3.3: - resolution: {integrity: sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw==} - engines: {node: '>= 10.13'} - dependencies: - debug: 4.3.4(supports-color@8.1.1) - json-stringify-safe: 5.0.1 - lodash: 4.17.21 - propagate: 2.0.1 - transitivePeerDependencies: - - supports-color - /nock@13.3.4: resolution: {integrity: sha512-DDpmn5oLEdCTclEqweOT4U7bEpuoifBMFUXem9sA4turDAZ5tlbrEoWqCorwXey8CaAw44mst5JOQeVNiwtkhw==} engines: {node: '>= 10.13'} @@ -5511,13 +5443,6 @@ packages: dependencies: path-key: 3.1.1 - /npm-run-path@5.1.0: - resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - path-key: 4.0.0 - dev: false - /npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} dependencies: @@ -5603,13 +5528,6 @@ packages: dependencies: mimic-fn: 2.1.0 - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - dependencies: - mimic-fn: 4.0.0 - dev: false - /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -5726,11 +5644,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: false - /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -6209,11 +6122,6 @@ packages: /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: false - /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -6410,11 +6318,6 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: false - /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} diff --git a/test/package.json b/test/package.json index 20d7c9777..1363261c2 100644 --- a/test/package.json +++ b/test/package.json @@ -60,7 +60,6 @@ }, "dependencies": { "@zombienet/orchestrator": "^0.0.52", - "execa": "^8.0.1", "inquirer": "^9.2.11", "ps-node": "^0.1.6" } diff --git a/test/scripts/zombienetRestart.ts b/test/scripts/zombienetRestart.ts index e871ac57e..460cfaa0a 100644 --- a/test/scripts/zombienetRestart.ts +++ b/test/scripts/zombienetRestart.ts @@ -3,8 +3,6 @@ import { exec, spawn, execSync } from "child_process"; import { readFileSync, writeFileSync, readlinkSync, unlinkSync } from "fs"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import os from "os"; -import path from "path"; import inquirer from "inquirer"; const getEnvVariables = (pid: number) => { @@ -112,7 +110,7 @@ yargs(hideBin(process.argv)) let { command, arguments: args } = processInfo; if (argv["edit-cmd"]) { - const tempFilePath = path.join(os.tmpdir(), `zombienet-restart-cmd-${Date.now()}.txt`); + const tempFilePath = execSync("mktemp /tmp/zombienet-restart-cmd-XXXXXX").toString().trim(); writeFileSync(tempFilePath, `${command} ${args.join(" ")}`); const editor = process.env.EDITOR || "vim"; // Default to 'vim' if EDITOR is not set @@ -152,9 +150,14 @@ yargs(hideBin(process.argv)) env: Object.fromEntries(envVariables.map((e) => e.split("=", 2))), }); - process.on("SIGINT", () => { - console.log('zombienetRestart: got SIGINT'); - child.kill("SIGINT"); + ["SIGINT", "SIGTERM"].forEach((signal) => { + process.on(signal, () => { + console.log("zombienetRestart: got ", signal); + if (child) { + child.kill(signal); + } + process.exit(); + }); }); }, argv["wait-ms"]); }); diff --git a/test/suites/keep-db/test_restart_keep_db.ts b/test/suites/keep-db/test_restart_keep_db.ts index 81508e657..01411c2bf 100644 --- a/test/suites/keep-db/test_restart_keep_db.ts +++ b/test/suites/keep-db/test_restart_keep_db.ts @@ -1,12 +1,12 @@ import { afterAll, beforeAll, describeSuite, expect } from "@moonwall/cli"; import { ApiPromise, Keyring } from "@polkadot/api"; -import { getAuthorFromDigest } from "../../util/author"; +import { getAuthorFromDigest, getAuthorFromDigestRange } from "../../util/author"; import { signAndSendAndInclude, waitSessions } from "../../util/block"; import { getKeyringNimbusIdHex } from "../../util/keys"; import { getHeaderFromRelay } from "../../util/relayInterface"; -import { exec } from "child_process"; -import { ExecaChildProcess, execa } from "execa"; +import { exec, spawn } from "child_process"; import fs from "fs/promises"; +import { createWriteStream } from "fs"; describeSuite({ id: "ZK01", @@ -16,7 +16,9 @@ describeSuite({ let paraApi: ApiPromise; let relayApi: ApiPromise; let container2000Api: ApiPromise; - const restartedHandles: Array> = []; + let blockNumberOfRestart; + let authoritiesAtRestart; + const restartedHandles = []; beforeAll(async () => { paraApi = context.polkadotJs("Tanssi"); @@ -43,29 +45,50 @@ describeSuite({ }, 120000); afterAll(async () => { - // TODO: this doesn't seem to run after the tests fail? - // Or maybe, this is only able to kill the zombienetRestart.ts process, not the tanssi-node - // once it has been started? + // Kill restared processes for (const h of restartedHandles) { - console.log('afterAll: killing ', h.pid, ' (exit code? ', h.exitCode, ')'); - h.kill('SIGINT'); - await sleep(1000); - console.log('afterAll: killed ', h.pid, ' (exit code? ', h.exitCode, ')'); + h.kill(); } }); - const runZombienetRestart = async (pid: number): Promise => { + const runZombienetRestart = async (pid: number, collatorLogFile: string): Promise => { // Wait 10 seconds to have enough time to check if db exists // Need to use `pnpm tsx` instead of `pnpm run` to ensure that the process gets killed properly - const handle = execa( - "pnpm", - ["tsx", "scripts/zombienetRestart.ts", "restart", "--wait-ms", "10000", "--pid", pid.toString()], - { - stdio: "inherit", + const command = "pnpm"; + const args = [ + "tsx", + "scripts/zombienetRestart.ts", + "restart", + "--wait-ms", + "10000", + "--pid", + pid.toString(), + ]; + + const child = spawn(command, args, { + stdio: ["inherit", "pipe", "pipe"], + }); + + // Pipe both stdout and stderr to the log file + const log = createWriteStream(collatorLogFile, { flags: "a" }); + child.stdout.pipe(log); + child.stderr.pipe(log); + + // Handle errors and exit events if needed + child.on("error", (error) => { + console.error(`spawn error: ${error}`); + }); + + child.on("exit", (code, signal) => { + if (code) { + console.error(`Child process exited with code ${code}`); } - ); + if (signal) { + console.error(`Child process was killed with signal ${signal}`); + } + }); - restartedHandles.push(handle); + restartedHandles.push(child); }; it({ @@ -171,25 +194,31 @@ describeSuite({ id: "T11", title: "Test restarting both container chain collators", test: async function () { + // Fetch block number before restarting because the RPC may no longer work after the restart + blockNumberOfRestart = (await container2000Api.rpc.chain.getBlock()).block.header.number.toNumber(); + // Fetch authorities for a later test + const currentSession = (await paraApi.query.session.currentIndex()).toNumber(); + authoritiesAtRestart = ( + await paraApi.query.authorityAssignment.collatorContainerChain(currentSession) + ).toJSON(); + const pidCollator200001 = await findCollatorProcessPid("Collator2000-01"); const pidCollator200002 = await findCollatorProcessPid("Collator2000-02"); - await runZombienetRestart(pidCollator200001); - await runZombienetRestart(pidCollator200002); + await runZombienetRestart(pidCollator200001, getTmpZombiePath() + `/Collator2000-01.log`); + await runZombienetRestart(pidCollator200002, getTmpZombiePath() + `/Collator2000-02.log`); await sleep(5000); // Check db has not been deleted const dbPath01 = - getTmpZombiePath() + - `/Collator2000-01/data/containers/chains/simple_container_2000/db/full-container-2000`; + getTmpZombiePath() + + `/Collator2000-01/data/containers/chains/simple_container_2000/db/full-container-2000`; const dbPath02 = - getTmpZombiePath() + - `/Collator2000-02/data/containers/chains/simple_container_2000/db/full-container-2000`; + getTmpZombiePath() + + `/Collator2000-02/data/containers/chains/simple_container_2000/db/full-container-2000`; expect(await directoryExists(dbPath01)).to.be.true; expect(await directoryExists(dbPath02)).to.be.true; - - // TODO: Check both collators are still producing blocks }, }); @@ -218,17 +247,38 @@ describeSuite({ // TODO: fix once we have types expect(registered.toJSON().includes(2000)).to.be.false; - // Check Collator2000-01 db path exists, and Collator2000-02 has deleted it + // Collator2000-01 db path exists because it was started with `--keep-db`, Collator2000-02 has deleted it const dbPath01 = - getTmpZombiePath() + - `/Collator2000-01/data/containers/chains/simple_container_2000/db/full-container-2000`; + getTmpZombiePath() + + `/Collator2000-01/data/containers/chains/simple_container_2000/db/full-container-2000`; const dbPath02 = - getTmpZombiePath() + - `/Collator2000-02/data/containers/chains/simple_container_2000/db/full-container-2000`; + getTmpZombiePath() + + `/Collator2000-02/data/containers/chains/simple_container_2000/db/full-container-2000`; expect(await directoryExists(dbPath01)).to.be.true; expect(await directoryExists(dbPath02)).to.be.false; + }, + }); + it({ + id: "T13", + title: "Both container chain collators keep producing blocks after restart", + test: async function () { + const currentBlock = (await container2000Api.rpc.chain.getBlock()).block.header.number.toNumber(); + console.log( + `Checking block authors for container chain 2000 in range ${blockNumberOfRestart} - ${currentBlock}` + ); + expect( + currentBlock, + "container chain 2000 should have produced more than 5 blocks already" + ).toBeGreaterThan(blockNumberOfRestart + 5); + await countUniqueBlockAuthorsExact( + container2000Api, + blockNumberOfRestart, + currentBlock, + 2, + authoritiesAtRestart + ); }, }); }, @@ -298,4 +348,31 @@ function getTmpZombiePath() { // Return null if the environment variable is not set return null; -} \ No newline at end of file +} + +/// Verify that the next `numBlocks` have exactly `numAuthors` different authors +async function countUniqueBlockAuthorsExact(paraApi, blockStart, blockEnd, numAuthors, authorities) { + const actualAuthors = []; + const blockNumbers = []; + + const authors = await getAuthorFromDigestRange(paraApi, blockStart, blockEnd); + for (let i = 0; i < authors.length; i++) { + const [blockNum, author] = authors[i]; + blockNumbers.push(blockNum); + actualAuthors.push(author); + } + + const uniq = [...new Set(actualAuthors)]; + + if (uniq.length != numAuthors) { + console.error( + "Mismatch between authorities and actual block authors: authorities: ", + authorities, + ", actual authors: ", + actualAuthors, + ", block numbers: ", + blockNumbers + ); + expect(false).to.be.true; + } +} diff --git a/test/suites/para/test_tanssi_containers.ts b/test/suites/para/test_tanssi_containers.ts index 386dea72e..86508659f 100644 --- a/test/suites/para/test_tanssi_containers.ts +++ b/test/suites/para/test_tanssi_containers.ts @@ -280,19 +280,9 @@ describeSuite({ title: "Blocks are being produced on container 2002", timeout: 90000, test: async function () { - let blockNum = (await container2002Api.rpc.chain.getBlock()).block.header.number.toNumber(); - // Wait 3 blocks because the next test needs to get a non empty value from // container2002Api.query.authoritiesNoting() - while (blockNum < 3) { - // Wait a bit - // Cannot use context.waitBlock because the container2002Api is not part of moonwall - const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); - await sleep(1_000); - - blockNum = (await container2002Api.rpc.chain.getBlock()).block.header.number.toNumber(); - } - expect(blockNum).to.be.greaterThan(0); + await context.waitBlock(3, "Container2002"); }, });