Files
Epicnabbo-Catalogus-Updated…/src/lib/services/catalog-repair-direct-live.test.ts
T
openhands 8218039c64 test(live): stop the live suites inheriting the production database
Seven suites read .env with a bare `process.env[key] = value`, which
overwrites whatever the shell already set. That made the DATABASE_URL from
the production .env authoritative, so a single environment variable was
enough to aim them at the live hotel database:

  RUN_CATALOG_AUDIT_LIVE=1 pnpm vitest run src/lib/services/catalog-audit-repair-live.test.ts

Three of those suites then repair the catalog in place: catalog-audit-repair-live
and catalog-repair-direct-live rewrite catalog_items and delete duplicate
classnames, and clone-bulk-import-live bulk-imports every cloneable item. None
of that is undoable, and nothing in their output said the target was
production rather than a sandbox.

Added src/test/live-env.ts with one shared loader, and pointed all seven suites
at it:

- Values already in the real environment win, so an explicit DATABASE_URL on
  the command line is always respected.
- DATABASE_URL defaults to the sandbox on port 3307 rather than inheriting the
  production one from .env.
- Anything that is not loopback is treated as production and redirected.
- Reaching production requires ALLOW_PRODUCTION_LIVE_DB=1 and logs a warning
  saying the suite repairs the catalog.

Tests in src/test/live-env.test.ts run the loader against a temporary .env so
the real project file is never read, and cover the redirect, the shell
override, non-loopback detection, the opt-in and quote stripping. A second
block asserts each of the seven suites no longer contains an inline
`process.env[...] =` assignment. Verified four of them fail against the old
loader.

This does not enable the suites; they stay gated behind their RUN_* flags.
It only removes the possibility of them silently hitting production.

Unit suite: 3330 passed, 12 skipped. Typecheck and lint clean.
2026-10-03 19:03:28 +02:00

54 lines
1.6 KiB
TypeScript

// @ts-nocheck
// Direct repair execution against the live DB. Skips the slow clone-source
// comparison entirely and just runs the repair functions.
import { appendFileSync } from "node:fs";
const LOG = "/tmp/catalog-repair.log";
const log = (msg: string) => {
appendFileSync(LOG, `${msg}\n`);
console.log(msg);
};
import { sql } from "drizzle-orm";
import { beforeAll, describe, expect, it } from "vitest";
import { loadEnvForLiveTests } from "@/test/live-env";
const runLive = process.env.RUN_CATALOG_AUDIT_LIVE === "1";
describe.skipIf(!runLive)("catalog repair direct (live)", () => {
let db: typeof import("@/lib/db").db;
let repair: typeof import("@/lib/services/catalog-repair");
beforeAll(async () => {
loadEnvForLiveTests();
const [dbMod, repairMod] = await Promise.all([
import("@/lib/db"),
import("@/lib/services/catalog-repair"),
]);
db = dbMod.db;
repair = repairMod;
});
it("repairs orphaned catalog + duplicate classnames", async () => {
expect(process.env.DATABASE_URL).toMatch(/@/);
log("=== ORPHANED REPAIR ===");
const orphan = await repair.repairOrphanedCatalog();
log(`orphaned result: ${JSON.stringify(orphan)}`);
log("=== DUPLICATE REPAIR ===");
const dup = await repair.repairDuplicateClassnames();
log(`duplicate result: ${JSON.stringify(dup)}`);
log("=== VERIFY DUPLICATES ===");
const [dupRows] = (await db.execute(sql`
SELECT item_name, COUNT(*) as cnt
FROM items_base
GROUP BY item_name
HAVING cnt > 1
`)) as unknown as [Array<{ item_name: string; cnt: number }>, unknown];
log(`remaining duplicate groups: ${JSON.stringify(dupRows)}`);
}, 300000);
});