schema/ledger-dimension-fk-indexes.integration.test.mjs
Rama main · réplica de lectura del repositorio original.
/// <reference types="node" />
/**
* Behavioral coverage for 0261_ledger_dimension_fk_indexes — the dimension-FK
* backing indexes on the three big ledger tables (journal_lines,
* document_lines, time_entries).
*
* Deleting a department (or any sandbox refresh / org purge touching a
* referenced row) fires one FK check per referencing row; with no index each
* check scans all of the org's lines. 0261 builds the missing composite
* (org_id, fk) indexes CONCURRENTLY (partial where the pointer is sparse),
* with an INVALID-index cleanup that makes a retry safe: a failed
* CONCURRENTLY build leaves an INVALID index under the target name, and a
* bare IF NOT EXISTS would skip that name forever.
*
* What these tests assert, through the live database rather than the
* migration's text:
* - no INVALID index residue exists on the three tables (the ongoing
* property the cleanup exists to protect — any future failed CONCURRENTLY
* build on these tables turns this red);
* - the detector fires on the real hazard, by planting a genuinely failed
* CONCURRENTLY build and showing the same query names it.
*
* Deliberately not asserted here: the 28 exact index names and predicates
* (a hand list of one migration's spelling), the no-transaction directive
* and lock_timeout absence (owned by the check:migration-headers gate and
* the no-published-survivors sanitizer test, which cover 0261 with the whole
* corpus), the INVALID-cleanup statement list (the retry-heal pattern is
* proven through the real attempt executor by the bootstrap pool's
* CONCURRENTLY test), and the Drizzle mirror parity (mirror index lists
* have no runtime effect and no conformance backstop; the database property
* is what matters and is covered above).
*
* Runs without a self-skip: the integration partition always provides a
* database, so a missing one must fail loudly, never pass silently.
*/
import assert from "node:assert/strict";
import test from "node:test";
import { sql } from "drizzle-orm";
import { db } from "@openbooks/engine/platform/db";
const LEDGER_TABLES = ["journal_lines", "document_lines", "time_entries"];
/** INVALID indexes on the given tables — residue of a failed CONCURRENTLY build. */
async function invalidIndexes(database, tables) {
const rows = await database.execute(sql`
select c.relname as name
from pg_index i
join pg_class c on c.oid = i.indexrelid
join pg_class t on t.oid = i.indrelid
join pg_namespace n on n.oid = t.relnamespace
where n.nspname = 'public'
and t.relname in (${sql.join(
tables.map((table) => sql`${table}`),
sql`, `,
)})
and not i.indisvalid
order by 1`);
return rows.rows.map((row) => row.name);
}
test("the ledger tables carry no INVALID index residue", async () => {
const residue = await invalidIndexes(db, LEDGER_TABLES);
assert.deepEqual(
residue,
[],
`a failed CONCURRENTLY build left INVALID indexes behind: ${residue.join(", ")}`,
);
});
test("the residue detector fires on a real failed CONCURRENTLY build", async () => {
const table = "probe_0261_invalid_table";
const probe = "probe_0261_invalid_residue";
await db.execute(sql.raw(`DROP TABLE IF EXISTS ${table}`));
try {
await db.execute(
sql.raw(`CREATE TABLE ${table} (id uuid primary key, org_id uuid, item_id uuid)`),
);
await db.execute(
sql.raw(
`INSERT INTO ${table} (id, org_id, item_id) VALUES (gen_random_uuid(), gen_random_uuid(), gen_random_uuid())`,
),
);
// uuid text never parses as int, so the build fails on the row above and
// leaves the name INVALID — the exact hazard 0261's cleanup exists for.
const failed = await db
.execute(sql.raw(`CREATE INDEX CONCURRENTLY ${probe} ON ${table} ((item_id::text::int))`))
.then(
() => null,
(error) => error,
);
assert.ok(failed, "the poisoned build must fail");
const residue = await invalidIndexes(db, [...LEDGER_TABLES, table]);
assert.ok(
residue.includes(probe),
`the detector must name the failed build, got: ${residue.join(", ")}`,
);
} finally {
await db.execute(sql.raw(`DROP TABLE IF EXISTS ${table}`));
}
const healed = await invalidIndexes(db, LEDGER_TABLES);
assert.deepEqual(healed, [], "the probe must leave no residue behind");
});