///
/**
* Live storage invariants of the bootstrapped schema. Each test drives the
* real interface, inserting a violating row against the installed database,
* with an independently derived expectation, or reads the live catalog for a
* shape the product depends on. Where an engine or integration suite already
* proves a behaviour, no test is added here.
*
* 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 { randomUUID } from "node:crypto";
import test from "node:test";
import { pool } from "@openbooks/engine/platform/db";
import {
createScratchOrg,
createScratchUser,
dropScratchOrg,
} from "@openbooks/engine/testing/fixtures";
function isConstraintViolation(error: unknown, pattern: RegExp): boolean {
if (!(error instanceof Error)) return false;
return pattern.test(error.message);
}
async function insertPayComponent(orgId: string): Promise {
const id = randomUUID();
await pool.query(
`insert into pay_components (id, org_id, code, name, kind)
values ($1, $2, 'T8BASE', 'T8 base component', 'earning')`,
[id, orgId],
);
return id;
}
async function insertPaySchedule(orgId: string): Promise {
const id = randomUUID();
await pool.query(
`insert into pay_schedules (id, org_id, name, frequency, periods_per_year, anchor_period_end)
values ($1, $2, 'T8 schedule', 'biweekly', 26, '2026-01-02')`,
[id, orgId],
);
return id;
}
test("overlapping active pay-derived windows for one rule identity are refused", async () => {
// Concurrent effective windows for the same (org, code) must not
// both be active — payroll would price the same earnings twice.
const org = await createScratchOrg();
try {
const componentId = await insertPayComponent(org.orgId);
const first = randomUUID();
await pool.query(
`insert into pay_derived_rules
(id, org_id, code, name, component_id, trigger, rate_value, effective_from, effective_to, is_active)
values ($1, $2, 'T8RULE', 'T8 rule', $3, 'time_entry', 10, '2026-01-01', '2026-12-31', true)`,
[first, org.orgId, componentId],
);
const overlapping = randomUUID();
const refused = await pool
.query(
`insert into pay_derived_rules
(id, org_id, code, name, component_id, trigger, rate_value, effective_from, effective_to, is_active)
values ($1, $2, 'T8RULE', 'T8 rule', $3, 'time_entry', 10, '2026-06-01', null, true)`,
[overlapping, org.orgId, componentId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(refused, /pay_derived_rules_no_active_overlap|exclusion/i),
`expected the overlap exclusion to refuse, got: ${String(refused)}`,
);
// An inactive twin is not a second live rule and must commit.
const inactive = randomUUID();
await pool.query(
`insert into pay_derived_rules
(id, org_id, code, name, component_id, trigger, rate_value, effective_from, effective_to, is_active)
values ($1, $2, 'T8RULE', 'T8 rule', $3, 'time_entry', 10, '2026-06-01', null, false)`,
[inactive, org.orgId, componentId],
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("tax rates refuse negative percentages and setup codes refuse duplicates", async () => {
// A negative rate would post inverted tax; a duplicate setup code
// would make pack resolution ambiguous.
const org = await createScratchOrg();
try {
const codeId = randomUUID();
await pool.query(
`insert into tax_codes (id, org_id, code, name) values ($1, $2, 'T8GST', 'T8 tax')`,
[codeId, org.orgId],
);
const negative = await pool
.query(
`insert into tax_rates (id, org_id, tax_code_id, rate_percent, effective_from)
values ($1, $2, $3, -5, '2026-01-01')`,
[randomUUID(), org.orgId, codeId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(negative, /tax_rates_rate_percent_domain|check/i),
`expected the rate domain check to refuse, got: ${String(negative)}`,
);
const duplicate = await pool
.query(`insert into tax_codes (id, org_id, code, name) values ($1, $2, 'T8GST', 'T8 copy')`, [
randomUUID(),
org.orgId,
])
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(duplicate, /tax_codes_org_code_unique|unique/i),
`expected the setup-code unique to refuse, got: ${String(duplicate)}`,
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("subscription plans refuse negative amounts", async () => {
// A negative plan amount would bill customers to receive money.
const org = await createScratchOrg();
try {
const refused = await pool
.query(
`insert into subscription_plans (id, org_id, name, amount) values ($1, $2, 'T8 plan', -10)`,
[randomUUID(), org.orgId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(refused, /subscription_plans_amount_nonnegative|check/i),
`expected the amount check to refuse, got: ${String(refused)}`,
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("forecast snapshots take at most one target and commit org-wide", async () => {
// A snapshot forecasting for both an owner and a team double-counts
// the pipeline; an org-wide snapshot is the honest default.
const org = await createScratchOrg();
const userId = randomUUID();
const teamId = randomUUID();
try {
// An active user must carry a role assignment; createScratchUser
// inserts user and assignment atomically.
await createScratchUser(org.orgId, "T8 owner", "t8_owner", userId);
await pool.query(
`insert into crm_sales_teams (id, org_id, key, name) values ($1, $2, 'T8', 'T8 team')`,
[teamId, org.orgId],
);
const snapshot = (id: string, owner: string | null, team: string | null) =>
pool.query(
`insert into crm_forecast_snapshots
(id, org_id, owner_user_id, sales_team_id, period_start, period_end,
snapshot_kind, currency, pipeline_amount, weighted_amount, worst_case_amount,
most_likely_amount, upside_amount, closed_amount)
values ($1, $2, $3, $4, '2026-01-01', '2026-03-31', 'commit', 'CAD', 0, 0, 0, 0, 0, 0)`,
[id, org.orgId, owner, team],
);
const both = await snapshot(randomUUID(), userId, teamId).then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(both, /crm_forecast_snapshot_target|check/i),
`expected the single-target check to refuse, got: ${String(both)}`,
);
await snapshot(randomUUID(), null, null);
await snapshot(randomUUID(), userId, null);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("payroll profiles refuse an omitted country instead of defaulting one", async () => {
// No country built-in exists, so an unset country must fail NOT NULL
// rather than silently becoming Canada.
const org = await createScratchOrg();
try {
const scheduleId = await insertPaySchedule(org.orgId);
const refused = await pool
.query(
`insert into employee_payroll_profiles (id, org_id, employee_party_id, pay_schedule_id, province)
values ($1, $2, $3, $4, 'Ontario')`,
[randomUUID(), org.orgId, org.customerId, scheduleId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(refused, /null value in column "country"|not-null/i),
`expected NOT NULL on country to refuse, got: ${String(refused)}`,
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("payroll pack facts refuse out-of-range values and accept unknown", async () => {
// Pack-declared facts are nullable (null = unknown, never guessed)
// but a present value outside the pack's bounds is refused.
const org = await createScratchOrg();
try {
const scheduleId = await insertPaySchedule(org.orgId);
const base = (id: string, country: string) =>
pool.query(
`insert into employee_payroll_profiles
(id, org_id, employee_party_id, pay_schedule_id, province, country)
values ($1, $2, $3, $4, 'Ontario', $5)`,
[id, org.orgId, org.customerId, scheduleId, country],
);
const profileId = randomUUID();
await base(profileId, "ES");
const outOfRange = await pool
.query(`update employee_payroll_profiles set es_ano_nacimiento = 1800 where id = $1`, [
profileId,
])
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(outOfRange, /employee_payroll_profiles_es_ano|check/i),
`expected the birth-year check to refuse, got: ${String(outOfRange)}`,
);
await pool.query(
`update employee_payroll_profiles set es_ano_nacimiento = null where id = $1`,
[profileId],
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("sync runs detach from a deleted connection instead of vanishing", async () => {
// Run history is evidence; deleting a connection must null the link
// (ON DELETE SET NULL over a nullable column), not cascade the runs.
const org = await createScratchOrg();
try {
const connectionId = randomUUID();
await pool.query(
`insert into connections (id, org_id, source, display_name) values ($1, $2, 't8', 'T8 connection')`,
[connectionId, org.orgId],
);
const runId = randomUUID();
await pool.query(
`insert into sync_runs (id, org_id, source, connection_id) values ($1, $2, 't8', $3)`,
[runId, org.orgId, connectionId],
);
await pool.query(`delete from connections where id = $1`, [connectionId]);
const stored = await pool.query(`select connection_id, status from sync_runs where id = $1`, [
runId,
]);
assert.equal(stored.rows[0]?.connection_id, null);
assert.equal(stored.rows[0]?.status, "running");
} finally {
await dropScratchOrg(org.orgId);
}
});
test("sftp prefixes refuse escape shapes and accept tenant folders", async () => {
// A tenant names folders, never physical locations — dot segments,
// absolute paths, and backslashes must not reach storage.
const org = await createScratchOrg();
try {
// Usernames are globally unique: scope the probe name to this run.
const username = `t8user-${randomUUID().slice(0, 8)}`;
const attempt = (prefix: string) =>
pool.query(
`insert into sftp_servers (id, org_id, name, username, root_prefix)
values ($1, $2, 'T8 sftp', $4, $3)`,
[randomUUID(), org.orgId, prefix, username],
);
const escape = await attempt("../escape").then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(escape, /sftp_servers_root_prefix_safe|check/i),
`expected the prefix guard to refuse, got: ${String(escape)}`,
);
await attempt("t8-ok/sub");
} finally {
await dropScratchOrg(org.orgId);
}
});
test("recognition rules refuse a second live version of one code", async () => {
// Editing a rule in use must create a successor, never mutate the
// version obligations already pinned — so only one live row per code.
const org = await createScratchOrg();
try {
await pool.query(
`insert into recognition_rules (id, org_id, code, name, method)
values ($1, $2, 'T8RULE', 'T8 rule', 'straight_line_even')`,
[randomUUID(), org.orgId],
);
const duplicate = await pool
.query(
`insert into recognition_rules (id, org_id, code, name, method)
values ($1, $2, 'T8RULE', 'T8 twin', 'straight_line_even')`,
[randomUUID(), org.orgId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(duplicate, /recognition_rules_org_code|unique/i),
`expected the live-version unique to refuse, got: ${String(duplicate)}`,
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("delivery keys refuse malformed values and double-claimed identities", async () => {
// The key is a fixed engine-minted shape, and one logical
// delivery owns exactly one canonical row.
const org = await createScratchOrg();
try {
const malformed = await pool
.query(
`insert into email_log (id, org_id, subject, delivery_key) values ($1, $2, 'T8', 'not-a-key')`,
[randomUUID(), org.orgId],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(malformed, /email_log_delivery_key_format|check/i),
`expected the key format check to refuse, got: ${String(malformed)}`,
);
const key = `obem_${"a".repeat(40)}`;
await pool.query(
`insert into email_log (id, org_id, subject, delivery_key) values ($1, $2, 'T8', $3)`,
[randomUUID(), org.orgId, key],
);
const twin = await pool
.query(
`insert into email_log (id, org_id, subject, delivery_key) values ($1, $2, 'T8 twin', $3)`,
[randomUUID(), org.orgId, key],
)
.then(
() => null,
(error: unknown) => error,
);
assert.ok(
isConstraintViolation(twin, /email_log_delivery_key|unique/i),
`expected the delivery identity unique to refuse, got: ${String(twin)}`,
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("document revisions advance on every committed update", async () => {
// Two committed revisions must never share a token. The
// collapse shape — an update storing updated_at byte-identical to the
// row — is rewritten strictly forward by documents_revision_monotonic,
// and openbooks_bump_revision_seq counts every UPDATE independently of
// the editable display timestamp. (The web optimistic-concurrency test
// proving the same lives in a skip-guarded plain .test.ts, so it never
// runs; this is the live cover.)
const org = await createScratchOrg();
try {
const id = randomUUID();
// Microsecond-exact display timestamps: node-pg truncates to
// milliseconds, which would never reproduce the collapse shape.
const stamp = "2026-08-24T12:00:00.400001Z";
await pool.query(
`insert into documents (id, org_id, kind, document_number, subsidiary_id, document_date, currency, status,
created_at, updated_at)
values ($1, $2, 'transfer', $3, $4, '2026-01-02', 'CAD', 'draft', $5::timestamptz, $5::timestamptz)`,
[id, org.orgId, `T8REV-${id.slice(0, 8)}`, org.subsidiaryId, stamp],
);
const read = async (): Promise<{ updated_at: Date; revision_seq: string }> =>
(await pool.query(`select updated_at, revision_seq from documents where id = $1`, [id]))
.rows[0] as {
updated_at: Date;
revision_seq: string;
};
const first = await read();
// Collapse-shaped write: store updated_at byte-identical to the row.
await pool.query(
`update documents set memo = 'T8 collapse', updated_at = $2::timestamptz where id = $1`,
[id, stamp],
);
const second = await read();
assert.ok(
second.updated_at > first.updated_at,
`a collapse write must move updated_at strictly forward, got ${String(second.updated_at)}`,
);
assert.equal(
BigInt(second.revision_seq),
BigInt(first.revision_seq) + 1n,
"every committed UPDATE must bump the revision counter exactly once",
);
await pool.query(`update documents set memo = 'T8 second' where id = $1`, [id]);
const third = await read();
assert.ok(
third.updated_at > second.updated_at,
`a second update must advance updated_at again, got ${String(third.updated_at)}`,
);
assert.equal(
BigInt(third.revision_seq),
BigInt(second.revision_seq) + 1n,
"the revision counter must keep counting past the first bump",
);
} finally {
await dropScratchOrg(org.orgId);
}
});
test("bank-feed bookkeeping separates the attempt cursor from the success cursor", async () => {
// Last_attempt_at records every finished attempt; last_sync_at is
// the success-only pull cursor the runner reads. The two must move
// independently — recording an attempt (even a failed one) must not
// advance the success cursor. (Runner-side attempt/success sequencing
// lives in engine banking tests; this is the storage half.)
const org = await createScratchOrg();
try {
const id = randomUUID();
await pool.query(
`insert into bank_feed_connections (id, org_id, name, provider, account_id)
values ($1, $2, 'T8 feed', 'manual', $3)`,
[id, org.orgId, randomUUID()],
);
await pool.query(
`update bank_feed_connections set last_attempt_at = now(), last_error = 'T8 probe failure', status = 'error'
where id = $1`,
[id],
);
const attempted = (
await pool.query(
`select last_attempt_at, last_sync_at from bank_feed_connections where id = $1`,
[id],
)
).rows[0] as { last_attempt_at: Date | null; last_sync_at: Date | null };
assert.ok(
attempted.last_attempt_at !== null,
"a finished attempt must stamp the attempt cursor",
);
assert.equal(attempted.last_sync_at, null, "a failed attempt must not move the success cursor");
await pool.query(
`update bank_feed_connections set last_attempt_at = now(), last_sync_at = now(), last_error = null,
status = 'connected' where id = $1`,
[id],
);
const succeeded = (
await pool.query(
`select last_attempt_at, last_sync_at from bank_feed_connections where id = $1`,
[id],
)
).rows[0] as { last_attempt_at: Date | null; last_sync_at: Date | null };
assert.ok(succeeded.last_sync_at !== null, "a success must stamp the success cursor");
assert.ok(succeeded.last_attempt_at !== null, "a success is also a finished attempt");
} finally {
await dropScratchOrg(org.orgId);
}
});
test("the installed catalog carries the canonical schema shape", async () => {
// Table/function/trigger/index presence, retired-model absence,
// adjustment RLS wiring and source-identity indexes, read from the live
// catalog: no scratch org needed, nothing written.
const tables = await pool.query(
`select tablename from pg_tables where schemaname = 'public' and tablename = any ($1)`,
[
[
"lease_agreements",
"lease_agreement_schedule_lines",
"inventory_writedowns",
"pay_schedules",
"pay_components",
"pay_run_adjustments",
"employee_payroll_profiles",
"employee_pay_components",
"pay_runs",
"pay_stubs",
"pay_stub_lines",
"payroll_opening_balances",
"union_agreements",
"union_classifications",
"union_fringes",
"auth_sessions",
"auth_login_events",
"auth_mfa_factors",
"auth_oidc_identities",
],
],
);
assert.equal(tables.rows.length, 19, "every canonical table must exist in the installed catalog");
const routines = await pool.query(
`select proname from pg_proc where pronamespace = 'public'::regnamespace and proname = any ($1)`,
[["posted_document_financial_guard"]],
);
assert.equal(routines.rows.length, 1, "the posted-document financial guard must exist");
const triggers = await pool.query(
`select tgname from pg_trigger where tgname = any ($1) and not tgisinternal`,
[["documents_posted_financial_guard"]],
);
assert.equal(triggers.rows.length, 1, "the posted-document guard trigger must exist");
const indexes = await pool.query(
`select indexname from pg_indexes where schemaname = 'public' and indexname = any ($1)`,
[
[
"backup_runs_one_inflight_per_org",
"parties_org_source_identity",
"projects_org_source_identity",
],
],
);
assert.equal(
indexes.rows.length,
3,
"one-inflight backup, tenant source-identity indexes must exist",
);
const retired = await pool.query(
`select tablename from pg_tables where schemaname = 'public' and tablename = any ($1)`,
[
[
"orphaned_tax_component_evidence",
"_migration_control_exceptions",
"_migration_schema_convergence",
"selection_source",
"legacy_json_migration",
"validation_replay",
],
],
);
assert.deepEqual(
retired.rows.map((row) => row.tablename),
[],
"no upgrade-only evidence model may survive in the catalog",
);
const policies = await pool.query(
`select policyname, permissive, roles::text, cmd, qual is not null as scoped, with_check is not null as checked
from pg_policies where schemaname = 'public' and tablename = 'pay_run_adjustments' and policyname = 'org_isolation'`,
);
assert.equal(policies.rows.length, 1, "pay_run_adjustments must carry the org_isolation policy");
assert.equal(policies.rows[0]?.scoped, true, "org_isolation must filter reads");
assert.equal(policies.rows[0]?.checked, true, "org_isolation must filter writes");
const relkind = await pool.query(
`select relforcerowsecurity, relrowsecurity from pg_class where relname = 'pay_run_adjustments'`,
);
assert.equal(
relkind.rows[0]?.relrowsecurity,
true,
"pay_run_adjustments must enforce row security",
);
assert.equal(
relkind.rows[0]?.relforcerowsecurity,
true,
"pay_run_adjustments must force row security",
);
});
test("the payroll opening-balances view exposes the statutory columns", async () => {
// The statutory columns of the governed payroll opening-balances view are
// its interface to payroll reporting.
const columns = await pool.query(
`select column_name from information_schema.columns
where table_schema = 'openbooks_query' and table_name = 'payroll_opening_balances'`,
);
const names = new Set(columns.rows.map((row) => row.column_name));
for (const column of ["cpp2_bonus_ytd", "qc_csb_ytd", "fica_withheld_ytd"]) {
assert.ok(names.has(column), `the view must expose ${column}`);
}
});