From 3f3cb0392ab66fcce2652086ccda936066bdbef3 Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 29 Aug 2026 09:32:57 -0300 Subject: [PATCH] fix(release): make the packaged-app smoke open the database before asserting the SQLite driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The packaged app opens SQLite lazily and the smoke's readiness URL (/login) never touches it, so the logs carried no "[DB] Driver: ..." line and assertNativeDriverSelected failed on the v3.8.50 re-attach (run 33251755872) with every leg green up to that point — the assertion could not distinguish a native driver from no database at all. After readiness the smoke now requests /api/monitoring/health (DB-backed) and waits up to 15 s for the driver line; the cold-restart assertion is unchanged. Unit tests cover the wait and the timeout. --- scripts/dev/smoke-electron-packaged.mjs | 59 +++++++++++++++++++++++- tests/unit/electron-smoke-script.test.ts | 23 +++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/scripts/dev/smoke-electron-packaged.mjs b/scripts/dev/smoke-electron-packaged.mjs index 72afc2f4a7..e134f5a5f0 100644 --- a/scripts/dev/smoke-electron-packaged.mjs +++ b/scripts/dev/smoke-electron-packaged.mjs @@ -123,6 +123,46 @@ function discoverPackagedExecutable() { throw new Error(`Packaged Electron smoke check does not support ${platform()}.`); } +/** + * The packaged app opens SQLite lazily: `/login` (the readiness URL) never touches the + * database, so a smoke that only waits for readiness sees no "[DB] Driver: ..." line at + * all and `assertNativeDriverSelected` cannot tell a native driver from nothing (v3.8.50 + * re-attach, run 33251755872: every leg green up to the smoke, then this). After readiness + * the smoke now requests a DB-backed endpoint and waits for the driver line to appear. + */ +export const DB_TOUCH_PATH = "/api/monitoring/health"; +const DB_DRIVER_LINE_PATTERN = /\[DB\] Driver: /; + +export async function waitForDriverLine(getLogs, { timeoutMs = 15_000, pollMs = 250 } = {}) { + const startedAt = Date.now(); + while (Date.now() - startedAt < timeoutMs) { + const logs = getLogs(); + assertNoFatalLogs(logs); + if (DB_DRIVER_LINE_PATTERN.test(logs)) return logs; + await sleep(pollMs); + } + throw new Error( + `Packaged Electron app logged no "[DB] Driver: ..." line within ${timeoutMs}ms of touching ` + + `${DB_TOUCH_PATH} — the database never opened, so the SQLite driver cannot be verified.` + ); +} + +async function openDatabaseForSmoke({ logs, smokeUrl }) { + const touchUrl = new URL(DB_TOUCH_PATH, smokeUrl).toString(); + try { + const response = await fetchWithTimeout(touchUrl, 5_000); + console.log( + `[electron-smoke] touched ${touchUrl} (HTTP ${response.status}) to open the database` + ); + } catch (error) { + console.log( + `[electron-smoke] touching ${touchUrl} failed (${error instanceof Error ? error.message : String(error)}) — waiting for the driver line anyway` + ); + } + await waitForDriverLine(() => logs.value); + console.log("[electron-smoke] database opened — driver line captured"); +} + async function fetchWithTimeout(url, timeoutMs) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); @@ -477,6 +517,7 @@ async function waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState }) if (response.status === 200) { assertNoFatalLogs(logs.value); console.log(`[electron-smoke] ready: ${smokeUrl} returned HTTP 200`); + await openDatabaseForSmoke({ logs, smokeUrl }); await settleAfterReady({ getExitState: () => ({ exitCode: exitState.exitCode, signalCode: exitState.signalCode }), logs, @@ -506,7 +547,14 @@ async function waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState }) * by the single-launch path and the cold-restart (two-launch) path so both * exercise identical spawn/readiness/shutdown behavior. */ -async function launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs }) { +async function launchAndCollectLogs({ + appExecutable, + smokeUrl, + dataDir, + timeoutMs, + settleMs, + streamLogs, +}) { const smokeEnv = buildSmokeEnv({ dataDir }); await assertPortIsFree(smokeUrl); await ensureSmokeEnvDirs(smokeEnv, dataDir); @@ -568,7 +616,14 @@ async function main() { !process.env.ELECTRON_SMOKE_DATA_DIR && process.env.ELECTRON_SMOKE_KEEP_DATA !== "1"; try { - await launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs }); + await launchAndCollectLogs({ + appExecutable, + smokeUrl, + dataDir, + timeoutMs, + settleMs, + streamLogs, + }); if (!coldRestart) return; diff --git a/tests/unit/electron-smoke-script.test.ts b/tests/unit/electron-smoke-script.test.ts index f7167859e1..57abbd9305 100644 --- a/tests/unit/electron-smoke-script.test.ts +++ b/tests/unit/electron-smoke-script.test.ts @@ -7,6 +7,8 @@ import { FATAL_LOG_PATTERNS, LINUX_EXECUTABLE_NAMES, stopApp, + waitForDriverLine, + DB_TOUCH_PATH, } from "../../scripts/dev/smoke-electron-packaged.mjs"; test("electron smoke discovers the default Linux executable name", () => { @@ -97,3 +99,24 @@ test("electron smoke flags startup logs missing any driver selection line", () = /no '\[DB\] Driver: \.\.\.' line/ ); }); + +test("electron smoke waits for the [DB] Driver line after touching a DB-backed endpoint", async () => { + assert.equal(DB_TOUCH_PATH, "/api/monitoring/health"); + let logs = "[electron] [Server] [STARTUP] ready\n"; + setTimeout(() => { + logs += "[electron] [Server] [DB] Driver: better-sqlite3 | file: /tmp/x/storage.sqlite\n"; + }, 60); + const seen = await waitForDriverLine(() => logs, { timeoutMs: 2_000, pollMs: 20 }); + assert.match(seen, /\[DB\] Driver: better-sqlite3/); +}); + +test("electron smoke fails clearly when the database never opens", async () => { + await assert.rejects( + () => + waitForDriverLine(() => "[electron] [Server] [STARTUP] ready\n", { + timeoutMs: 120, + pollMs: 20, + }), + /logged no "\[DB\] Driver: \.\.\." line within 120ms/ + ); +});