fix(release): the packaged-app smoke verifies the database opened, not a driver line the primary path never prints

The packaged app does open SQLite during the smoke (migrations run, [DB] Added …
column, cache_size lines) but its primary open path prints no "[DB] Driver: …"
line — only the recovery path and the sql.js fallback do — so the #7592 assertion
demanded a line that never exists and every Linux release leg failed on it. The
guard now: rejects the sql.js fallback line (which the fallback always prints),
accepts a native driver line, and otherwise accepts demonstrable database activity;
after readiness the smoke requests /api/monitoring/health and waits for that
activity, outside the readiness retry loop so a missing database is a verdict, not
a retry. The previous commit on this branch had the wait inside the loop and made
ci.yml's Electron Package Smoke fail; electron-smoke-script suite 10/10.
This commit is contained in:
diegosouzapw 2026-08-29 13:47:03 -03:00
parent 3f3cb0392a
commit 0c26216b85
2 changed files with 49 additions and 22 deletions

View file

@ -125,25 +125,27 @@ function discoverPackagedExecutable() {
/**
* 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.
* database, so a smoke that only waits for readiness sees no `[DB]` line at all. After
* readiness the smoke requests a DB-backed endpoint and waits for evidence that the
* database opened. The primary open path does NOT print "[DB] Driver: ..." (only the
* recovery path and the sql.js fallback do), so the evidence is any `[DB]`/`[Migration]`
* startup line — and the #7592 guard below rejects the fallback's own line explicitly.
*/
export const DB_TOUCH_PATH = "/api/monitoring/health";
const DB_DRIVER_LINE_PATTERN = /\[DB\] Driver: /;
export const DB_OPEN_EVIDENCE_PATTERN =
/\[DB\] (Driver: |SQLite database ready|Added [^\n]* column|Changing cache_size|cache_size changed)|\[Migration\] (Applied|Pre-migration backup)/;
export async function waitForDriverLine(getLogs, { timeoutMs = 15_000, pollMs = 250 } = {}) {
export async function waitForDatabaseOpen(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;
if (DB_OPEN_EVIDENCE_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.`
`Packaged Electron app logged no [DB]/[Migration] startup line within ${timeoutMs}ms of ` +
`touching ${DB_TOUCH_PATH} — the database never opened, so the SQLite driver cannot be verified.`
);
}
@ -156,11 +158,11 @@ async function openDatabaseForSmoke({ logs, smokeUrl }) {
);
} catch (error) {
console.log(
`[electron-smoke] touching ${touchUrl} failed (${error instanceof Error ? error.message : String(error)}) — waiting for the driver line anyway`
`[electron-smoke] touching ${touchUrl} failed (${error instanceof Error ? error.message : String(error)}) — waiting for the database anyway`
);
}
await waitForDriverLine(() => logs.value);
console.log("[electron-smoke] database opened — driver line captured");
await waitForDatabaseOpen(() => logs.value);
console.log("[electron-smoke] database opened");
}
async function fetchWithTimeout(url, timeoutMs) {
@ -490,8 +492,13 @@ export function assertNativeDriverSelected(logs) {
);
}
// The primary open path prints no "[DB] Driver: ..." line at all (only the recovery path and
// the sql.js fallback do), so a database that demonstrably opened WITHOUT the fallback's own
// line is the native driver — that is exactly what #7592 guards.
if (DB_OPEN_EVIDENCE_PATTERN.test(logs)) return;
throw new Error(
"Packaged Electron app logs contain no '[DB] Driver: ...' line — cannot confirm which SQLite " +
"Packaged Electron app logs show no database activity at all — cannot confirm which SQLite " +
"driver loaded."
);
}
@ -517,7 +524,6 @@ 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,
@ -586,6 +592,8 @@ async function launchAndCollectLogs({
try {
await waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState });
// Outside waitForReady on purpose: a missing database is a verdict, not a readiness retry.
await openDatabaseForSmoke({ logs, smokeUrl });
return logs.value;
} catch (error) {
if (!streamLogs) {

View file

@ -7,7 +7,7 @@ import {
FATAL_LOG_PATTERNS,
LINUX_EXECUTABLE_NAMES,
stopApp,
waitForDriverLine,
waitForDatabaseOpen,
DB_TOUCH_PATH,
} from "../../scripts/dev/smoke-electron-packaged.mjs";
@ -96,27 +96,46 @@ test("electron smoke flags a cold-restart fallback 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/
/no database activity/
);
});
test("electron smoke waits for the [DB] Driver line after touching a DB-backed endpoint", async () => {
test("electron smoke waits for database-open evidence 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";
logs += "[electron] [Server] [DB] Added usage_history.combo_strategy column\n";
}, 60);
const seen = await waitForDriverLine(() => logs, { timeoutMs: 2_000, pollMs: 20 });
assert.match(seen, /\[DB\] Driver: better-sqlite3/);
const seen = await waitForDatabaseOpen(() => logs, { timeoutMs: 2_000, pollMs: 20 });
assert.match(seen, /\[DB\] Added/);
});
test("electron smoke fails clearly when the database never opens", async () => {
await assert.rejects(
() =>
waitForDriverLine(() => "[electron] [Server] [STARTUP] ready\n", {
waitForDatabaseOpen(() => "[electron] [Server] [STARTUP] ready\n", {
timeoutMs: 120,
pollMs: 20,
}),
/logged no "\[DB\] Driver: \.\.\." line within 120ms/
/logged no \[DB\]\/\[Migration\] startup line within 120ms/
);
});
test("electron smoke driver guard: native line, DB evidence and sql.js fallback", () => {
assert.doesNotThrow(() =>
assertNativeDriverSelected("[DB] Driver: better-sqlite3 | file: /tmp/x/storage.sqlite\n")
);
assert.doesNotThrow(() =>
assertNativeDriverSelected(
"[electron] [Server] [DB] Added call_logs.session_tag column\n[electron] [Server] [Migration] Applied: 046_database_settings\n"
)
);
assert.throws(
() => assertNativeDriverSelected("[DB] Driver: sql.js | file: /tmp/x/storage.sqlite\n"),
/sql\.js \(WASM\) driver/
);
assert.throws(
() => assertNativeDriverSelected("[STARTUP] nothing here\n"),
/no database activity/
);
});