Files
OmniRoute/tests/unit/electron-smoke-script.test.ts
Webman 9862f129e2 fix(electron): make the packaged Windows build pass the #7592 cold-restart smoke (#11443)
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip.
- Focused test: electron-smoke-script.test.ts — part of batch's 165/165 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:docs-counts-sync — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Real hardware validation on a fresh packaged Windows build (Hard Rule #18) surfacing and fixing 5 genuine defects (GNU-tar drive-letter parsing, missing asar file, electron-builder's node_modules extraResources drop, USERPROFILE-derived userData path, unreachable driver-log assertion) is exactly the kind of investigation this repo needs more of. Thank you.
2026-08-25 18:54:20 -03:00

153 lines
5.9 KiB
TypeScript

import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import {
assertNativeDriverSelected,
buildSmokeEnv,
ensureSmokeEnvDirs,
FATAL_LOG_PATTERNS,
LINUX_EXECUTABLE_NAMES,
stopApp,
} from "../../scripts/dev/smoke-electron-packaged.mjs";
import { tarPack } from "../../scripts/build/optionalPackStaging.mjs";
test("electron smoke discovers the default Linux executable name", () => {
assert.ok(LINUX_EXECUTABLE_NAMES.includes("omniroute-desktop"));
});
test("electron smoke env allowlists runtime variables and drops secrets", () => {
const dataDir = path.join("/tmp", "omniroute-electron-smoke-test");
const env = buildSmokeEnv({
currentPlatform: "linux",
dataDir,
parentEnv: {
DISPLAY: ":99",
GITHUB_TOKEN: "should-not-leak",
PATH: "/usr/bin",
SNYK_TOKEN: "should-not-leak",
},
});
// Expected values are built with path.join so the assertions hold on every
// host platform: buildSmokeEnv() composes its redirected paths with join(),
// which yields backslashes on Windows even when currentPlatform is "linux".
assert.equal(env.DATA_DIR, dataDir);
assert.equal(env.DISPLAY, ":99");
assert.equal(env.PATH, "/usr/bin");
assert.equal(env.HOME, path.join(dataDir, "home"));
assert.equal(env.XDG_CONFIG_HOME, path.join(dataDir, "config"));
assert.equal(env.ELECTRON_ENABLE_LOGGING, "1");
assert.equal(env.ELECTRON_ENABLE_STACK_DUMPING, "1");
assert.equal(env.GITHUB_TOKEN, undefined);
assert.equal(env.SNYK_TOKEN, undefined);
});
test("electron smoke pre-creates the USERPROFILE-derived Roaming userData tree on Windows", async () => {
// #7592: Electron resolves userData from %USERPROFILE%\AppData\Roaming\<name>
// (USERPROFILE takes precedence over the APPDATA env var) and the path
// service throws — rather than creates — when the directory is missing, so
// requestSingleInstanceLock() returns false and the app exits(0) silently.
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-smoke-env-test-"));
try {
const smokeEnv = buildSmokeEnv({ currentPlatform: "win32", dataDir });
// Inject the smoke TARGET platform: ensureSmokeEnvDirs must key its win32
// userData-tree branches off the target, not the host OS — otherwise this
// guard can never pass on a Linux CI host (the 8/9 red the reviewer hit).
await ensureSmokeEnvDirs(smokeEnv, dataDir, { currentPlatform: "win32" });
for (const appName of ["omniroute-desktop", "OmniRoute", "omniroute"]) {
const derived = path.join(smokeEnv.USERPROFILE, "AppData", "Roaming", appName);
assert.ok(fs.existsSync(derived), `expected pre-created derived userData dir: ${derived}`);
const viaAppData = path.join(smokeEnv.APPDATA, appName);
assert.ok(fs.existsSync(viaAppData), `expected pre-created APPDATA dir: ${viaAppData}`);
}
} finally {
fs.rmSync(dataDir, { recursive: true, force: true });
}
});
test("electron smoke tarPack handles absolute Windows-style tarball paths", () => {
// GNU tar treats `C:\...` in `-f` as a remote rsh target ("Cannot connect to
// C:"), which broke optional-pack staging on Windows. tarPack() must pass a
// bare filename with cwd at the tarball directory instead.
const staging = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-tar-pack-test-"));
try {
const nodeModules = path.join(staging, "pack", "node_modules");
fs.mkdirSync(path.join(nodeModules, "fixture-pkg"), { recursive: true });
fs.writeFileSync(path.join(nodeModules, "fixture-pkg", "index.js"), "module.exports = 1;");
const tarballPath = path.join(staging, "optional-pack-fixture.tar.gz");
tarPack(path.join(staging, "pack"), tarballPath);
assert.ok(fs.existsSync(tarballPath), "tarball should exist after tarPack");
assert.ok(fs.statSync(tarballPath).size > 0, "tarball should not be empty");
} finally {
fs.rmSync(staging, { recursive: true, force: true });
}
});
test("electron smoke treats Electron process errors as fatal startup logs", () => {
const logs = [
"[Electron] Unhandled Rejection: Error: startup failed",
"[Electron] Uncaught Exception: Error: startup failed",
];
for (const log of logs) {
assert.ok(
FATAL_LOG_PATTERNS.some((pattern) => pattern.test(log)),
`${log} should match a fatal log pattern`
);
}
});
test("electron smoke force-terminates the Windows process tree before the parent can exit", async () => {
const signals: string[] = [];
const waits: number[] = [];
const child = {
pid: 4242,
exitCode: 0,
signalCode: null,
};
await stopApp(child, {
currentPlatform: "win32",
signalProcessTreeFn: async (_child, signal) => {
signals.push(signal);
},
waitForProcessTreeExitFn: async (_child, timeoutMs) => {
waits.push(timeoutMs);
},
});
assert.deepEqual(signals, ["SIGKILL"]);
assert.deepEqual(waits, [2_000]);
});
// #7592: on a cold restart against an already-persisted DATA_DIR, a stale-ABI
// better-sqlite3 binary used to fail to load and silently fall through to the
// sql.js (WASM) driver. These are the regression guards for that assertion.
test("electron smoke accepts every native SQLite driver on the startup log", () => {
for (const driver of ["bun:sqlite", "better-sqlite3", "node:sqlite"]) {
assert.doesNotThrow(() =>
assertNativeDriverSelected(`[electron] [DB] Driver: ${driver} | file: /data/storage.sqlite`)
);
}
});
test("electron smoke flags a cold-restart fallback to the sql.js WASM driver", () => {
assert.throws(
() => assertNativeDriverSelected("[electron] [DB] Driver: sql.js | file: /data/storage.sqlite"),
/fell back to the sql\.js \(WASM\) driver/
);
});
test("electron smoke flags startup logs missing any driver selection line", () => {
assert.throws(
() => assertNativeDriverSelected("[electron] [server] listening on 20128"),
/no '\[DB\] Driver: \.\.\.' line/
);
});