From 92092e9844e1cace4a9af832021a2eeaf50dcfcf Mon Sep 17 00:00:00 2001 From: NimBold Date: Sat, 1 Aug 2026 19:55:42 +0330 Subject: [PATCH] test(torrents): harden native smoke validation --- .github/workflows/ci.yml | 6 +++++ scripts/smoke-torrent.js | 48 +++++++++++++++++++++++++++++++-------- src-tauri/tests/README.md | 5 ++++ 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa0a6da..812c11b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,10 @@ jobs: run: | cargo test --tests --target ${{ matrix.target }} cargo test --lib --no-run --target ${{ matrix.target }} + - name: Test Torrent RPC harness on Windows + if: runner.os == 'Windows' + working-directory: src-tauri + run: cargo test torrent_probe::tests::http_rpc_harness --target ${{ matrix.target }} -- --nocapture - name: Provision locked engines if: runner.os != 'macOS' run: node scripts/provision-engines.js --target ${{ matrix.target }} @@ -78,3 +82,5 @@ jobs: run: | node scripts/stage-engines.js --target ${{ matrix.target }} node scripts/verify-binaries.js --staged --target ${{ matrix.target }} + - name: Run Torrent process smoke + run: node scripts/smoke-torrent.js --binary src-tauri/engine-dist/${{ matrix.target }}/aria2c-${{ matrix.target }}${{ runner.os == 'Windows' && '.exe' || '' }} --failure-paths diff --git a/scripts/smoke-torrent.js b/scripts/smoke-torrent.js index c2696cc..f5fa262 100644 --- a/scripts/smoke-torrent.js +++ b/scripts/smoke-torrent.js @@ -6,7 +6,7 @@ import http from 'node:http'; import net from 'node:net'; import os from 'node:os'; import path from 'node:path'; -import { spawn } from 'node:child_process'; +import { execFileSync, spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -359,12 +359,30 @@ async function stopDaemon(daemon) { // The process may already have exited during cleanup. } } - if (!await waitForChildExit(child, 3000)) { - child.kill('SIGTERM'); - if (!await waitForChildExit(child, 3000) && process.platform !== 'win32') { - child.kill('SIGKILL'); - await waitForChildExit(child, 1000); + let exited = await waitForChildExit(child, 3000); + if (!exited) { + if (process.platform === 'win32') { + try { + execFileSync('taskkill', ['/pid', String(child.pid), '/t', '/f'], { + stdio: 'ignore', + timeout: 3000, + }); + } catch { + // The process may have exited between the wait and taskkill. + } + } else { + child.kill('SIGTERM'); } + exited = await waitForChildExit(child, 3000); + } + if (!exited) { + if (process.platform !== 'win32') { + child.kill('SIGKILL'); + exited = await waitForChildExit(child, 3000); + } + } + if (!exited) { + throw new Error(`Aria2 daemon process ${child.pid} did not exit after forced cleanup`); } } finally { activeDaemons.delete(daemon); @@ -379,11 +397,13 @@ async function closeServer(server) { async function cleanupRuntime() { if (cleanupPromise) return cleanupPromise; cleanupPromise = (async () => { + let cleanupError; for (const daemon of [...activeDaemons].reverse()) { try { await stopDaemon(daemon); } catch (error) { - console.error(`[WARN] failed to stop Aria2 daemon: ${error.message}`); + cleanupError ??= error; + console.error(`[FAIL] failed to stop Aria2 daemon: ${error.message}`); } } if (runtimeTracker) { @@ -394,9 +414,19 @@ async function cleanupRuntime() { if (runtimeTempRoot) { const tempRoot = runtimeTempRoot; runtimeTempRoot = undefined; - if (keepTemp) console.log(`[INFO] retained smoke-test directory: ${tempRoot}`); - else fs.rmSync(tempRoot, { recursive: true, force: true }); + if (keepTemp || cleanupError) { + console.log(`[INFO] retained smoke-test directory: ${tempRoot}`); + } else { + try { + fs.rmSync(tempRoot, { recursive: true, force: true }); + } catch (error) { + cleanupError ??= error; + console.error(`[FAIL] failed to remove smoke-test directory: ${error.message}`); + console.log(`[INFO] retained smoke-test directory: ${tempRoot}`); + } + } } + if (cleanupError) throw cleanupError; })(); return cleanupPromise; } diff --git a/src-tauri/tests/README.md b/src-tauri/tests/README.md index aadfdf4..153cbce 100644 --- a/src-tauri/tests/README.md +++ b/src-tauri/tests/README.md @@ -49,3 +49,8 @@ Run the deterministic unavailable-tracker and Aria2-daemon-exit checks with: ```sh npm run smoke:torrent:failure-paths ``` + +Native CI runs this failure-path smoke after staging the target-specific +bundled engines on macOS, Windows, and Linux. Windows also runs the filtered +HTTP-boundary RPC harness explicitly because its general Rust job compiles the +library tests without executing them.