Files
fusion/scripts/__tests__/check-no-nohup.test.mjs
gsxdsm c25f8b796d Harden PostgreSQL migration foundation (#2088)
## Summary

- make SQLite-to-PostgreSQL cutover retryable, fail-closed, versioned,
and transactionally serialized
- isolate migration sessions from runtime traffic and apply schema
upgrades through `0002`
- enforce tenant ownership across automations, analytics, activity,
usage, agent runs, evals, and todos
- replace expired SQLite-only coverage with PostgreSQL parity and
concurrency coverage

This is PR 1 of 2. The stacked follow-up restores PostgreSQL parity for
CLI, engine, dashboard, and bundled integrations.

## Verification

- `pnpm check:changesets --strict`
- `pnpm --filter @fusion/core typecheck`
- migration schema, connection, and SQLite cutover suite: 57 tests
passed
- `pnpm test:gate`: 463 tests passed

## Post-Deploy Monitoring & Validation

- take a restorable PostgreSQL backup before deploy
- confirm `fusion_schema_migrations` contains `0002`
- confirm each expected project has a complete
`fusion_sqlite_migrations` row
- verify no null or empty tenant ownership in automations, activity
logs, agent runs, and usage events
- monitor for ownership inference failures, cutover verification
failures, and migration session errors
- restore the backup for data rollback; do not downgrade the
tenant-isolation schema in place

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* PostgreSQL-backed analytics and live dashboard metrics are now
project-scoped (activity, tools, monitor, signals, and live snapshots).
* Evaluation runs and scheduled eval batches received lifecycle
improvements (ordering, updates, and execution flow).
* Todo list changes now emit events; WhatsApp persistence and
project-scoped roadmap data are supported.

* **Bug Fixes**
* SQLite-to-PostgreSQL cutovers now fail safely with stronger
verification, serialized cutover handling, and safer project ownership.
* PostgreSQL backend writes and reads are now strictly project-isolated
and fail closed when project context is missing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 08:16:42 -07:00

46 lines
1.7 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
const checkerModule = await import(["..", "/check-no-no", "hup", ".mjs"].join(""));
const { formatFailureMessage, scanFileContent, scanTrackedFiles } = checkerModule;
const bannedToken = ["no", "hup"].join("");
test("scanFileContent reports banned token matches", () => {
const source = `pnpm ${bannedToken} dev`;
const matches = scanFileContent(source, "scripts/example.mjs");
assert.equal(matches.length, 1);
assert.equal(matches[0].lineNumber, 1);
assert.match(matches[0].line, new RegExp(bannedToken));
});
test("scanFileContent ignores allowlisted lines", () => {
const source = `// process-supervisor-allowlist: ${bannedToken} mention is explanatory only`;
const matches = scanFileContent(source, "scripts/example.mjs");
assert.equal(matches.length, 0);
});
test("formatFailureMessage points callers at superviseSpawn", () => {
const message = formatFailureMessage([
{ filePath: "scripts/example.mjs", lineNumber: 3, line: `pnpm ${bannedToken} dev` },
]);
assert.match(message, /superviseSpawn/);
assert.match(message, /scripts\/example\.mjs:3/);
});
test("scanTrackedFiles skips only tracked files missing from the working tree", () => {
const missing = Object.assign(new Error("missing"), { code: "ENOENT" });
const readFile = () => { throw missing; };
assert.deepEqual(scanTrackedFiles(["scripts/deleted.mjs"], readFile), []);
});
test("scanTrackedFiles rethrows tracked-file read failures other than ENOENT", () => {
const denied = Object.assign(new Error("denied"), { code: "EACCES" });
const readFile = () => { throw denied; };
assert.throws(
() => scanTrackedFiles(["scripts/unreadable.mjs"], readFile),
(error) => error === denied,
);
});