From 27d108a85285a9070ab8c511e02544def6eec902 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:06:05 +0400 Subject: [PATCH] test: simplify --- .github/workflows/lint-rs.yml | 19 +--- package.json | 3 +- scripts/openvpn-test-harness.mjs | 161 +++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 scripts/openvpn-test-harness.mjs diff --git a/.github/workflows/lint-rs.yml b/.github/workflows/lint-rs.yml index eea3ff7..11ed4b7 100644 --- a/.github/workflows/lint-rs.yml +++ b/.github/workflows/lint-rs.yml @@ -113,23 +113,8 @@ jobs: run: cargo clippy --all-targets --all-features -- -D warnings -D clippy::all working-directory: src-tauri - - name: Run Rust unit tests - run: cargo test --lib && cargo test --test donut_proxy_integration && cargo test --test vpn_integration - working-directory: src-tauri - - - name: Run OpenVPN e2e test (Ubuntu only) - if: matrix.os == 'ubuntu-22.04' - shell: bash - working-directory: src-tauri - env: - DONUTBROWSER_RUN_OPENVPN_E2E: "1" - run: | - CARGO_BIN="$(command -v cargo)" - sudo --preserve-env=CARGO_HOME,RUSTUP_HOME,DONUTBROWSER_RUN_OPENVPN_E2E \ - "${CARGO_BIN}" test --test vpn_integration test_openvpn_traffic_flows_through_donut_proxy -- --nocapture - - - name: Run Rust sync e2e tests - run: node scripts/sync-test-harness.mjs + - name: Run test suite + run: pnpm test - name: Run cargo audit security check run: cargo audit diff --git a/package.json b/package.json index ac9745f..ec2298b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "dev": "next dev --turbopack -p 12341", "build": "next build", "start": "next start", - "test": "pnpm test:rust:unit && pnpm test:sync-e2e", + "test": "pnpm test:rust:unit && pnpm test:openvpn-e2e && pnpm test:sync-e2e", + "test:openvpn-e2e": "node scripts/openvpn-test-harness.mjs", "test:rust": "cd src-tauri && cargo test", "test:rust:unit": "cd src-tauri && cargo test --lib && cargo test --test donut_proxy_integration && cargo test --test vpn_integration", "test:sync-e2e": "node scripts/sync-test-harness.mjs", diff --git a/scripts/openvpn-test-harness.mjs b/scripts/openvpn-test-harness.mjs new file mode 100644 index 0000000..45d8549 --- /dev/null +++ b/scripts/openvpn-test-harness.mjs @@ -0,0 +1,161 @@ +#!/usr/bin/env node +/** + * OpenVPN E2E Test Harness + * + * This script: + * 1. Skips unless explicitly enabled via DONUTBROWSER_RUN_OPENVPN_E2E=1 + * 2. Builds the Rust vpn_integration test binary without running it + * 3. Runs the OpenVPN e2e test binary under sudo + * + * Usage: DONUTBROWSER_RUN_OPENVPN_E2E=1 node scripts/openvpn-test-harness.mjs + */ + +import { spawn } from "child_process"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT_DIR = path.resolve(__dirname, ".."); +const SRC_TAURI_DIR = path.join(ROOT_DIR, "src-tauri"); +const TEST_NAME = "test_openvpn_traffic_flows_through_donut_proxy"; + +function log(message) { + console.log(`[openvpn-harness] ${message}`); +} + +function error(message) { + console.error(`[openvpn-harness] ERROR: ${message}`); +} + +function shouldRun() { + if (process.env.DONUTBROWSER_RUN_OPENVPN_E2E !== "1") { + log("Skipping OpenVPN e2e test because DONUTBROWSER_RUN_OPENVPN_E2E is not set"); + return false; + } + + if (process.platform !== "linux") { + log(`Skipping OpenVPN e2e test on unsupported platform: ${process.platform}`); + return false; + } + + return true; +} + +async function buildTestBinary() { + log("Building OpenVPN e2e test binary..."); + + return new Promise((resolve, reject) => { + let executablePath = ""; + let stdoutBuffer = ""; + + const proc = spawn( + "cargo", + [ + "test", + "--test", + "vpn_integration", + TEST_NAME, + "--no-run", + "--message-format=json", + ], + { + cwd: SRC_TAURI_DIR, + env: process.env, + stdio: ["ignore", "pipe", "pipe"], + } + ); + + const parseBuffer = (flush = false) => { + const lines = stdoutBuffer.split("\n"); + const completeLines = flush ? lines : lines.slice(0, -1); + stdoutBuffer = flush ? "" : lines.at(-1) ?? ""; + + for (const line of completeLines.filter(Boolean)) { + try { + const message = JSON.parse(line); + if (message.reason === "compiler-artifact" && message.executable) { + executablePath = message.executable; + } + } catch { + // Ignore non-JSON lines. + } + } + }; + + proc.stdout.on("data", (data) => { + stdoutBuffer += data.toString(); + parseBuffer(); + }); + + proc.stderr.on("data", (data) => { + process.stderr.write(data); + }); + + proc.on("error", (err) => { + reject(err); + }); + + proc.on("close", (code) => { + parseBuffer(true); + + if (code !== 0) { + reject(new Error(`cargo test --no-run exited with code ${code}`)); + return; + } + + if (!executablePath) { + reject(new Error("Could not determine the vpn_integration test binary path")); + return; + } + + resolve(path.isAbsolute(executablePath) ? executablePath : path.resolve(SRC_TAURI_DIR, executablePath)); + }); + }); +} + +async function runOpenVpnE2e(executablePath) { + log("Running OpenVPN e2e test under sudo..."); + + return new Promise((resolve, reject) => { + const proc = spawn( + "sudo", + [ + "--preserve-env=CI,GITHUB_ACTIONS,VPN_TEST_OVPN_HOST,VPN_TEST_OVPN_PORT,DONUTBROWSER_RUN_OPENVPN_E2E", + executablePath, + TEST_NAME, + "--exact", + "--nocapture", + ], + { + cwd: SRC_TAURI_DIR, + env: process.env, + stdio: "inherit", + } + ); + + proc.on("error", (err) => { + reject(err); + }); + + proc.on("close", (code) => { + resolve(code ?? 1); + }); + }); +} + +async function main() { + if (!shouldRun()) { + process.exit(0); + } + + try { + const executablePath = await buildTestBinary(); + const exitCode = await runOpenVpnE2e(executablePath); + process.exit(exitCode); + } catch (err) { + error(err instanceof Error ? err.message : String(err)); + process.exit(1); + } +} + +main();