85 lines
4.4 KiB
JavaScript
85 lines
4.4 KiB
JavaScript
import { readFile } from "node:fs/promises";
|
|
import { spawnSync } from "node:child_process";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const root = process.cwd();
|
|
const liveGatePath = join(root, "scripts/verify-rls-live.sh");
|
|
|
|
describe("verify:rls-live migration-chain mode", () => {
|
|
it("probes fresh schema and exact portal policies before any feature-branch overlay", async () => {
|
|
const liveGate = await readFile(liveGatePath, "utf8");
|
|
const schemaProbe = liveGate.indexOf("pnpm exec prisma migrate diff");
|
|
const policyProbe = liveGate.indexOf("pnpm exec tsx scripts/verify-portal-migration-chain-policy-state.ts");
|
|
const dbPush = liveGate.indexOf("pnpm exec prisma db push --accept-data-loss");
|
|
const actorOverlay = liveGate.indexOf("< prisma/rls/portal-actor-invariants.sql");
|
|
const tenantOverlay = liveGate.indexOf("< prisma/rls/tenant-policy-contract-isolation.sql");
|
|
const portalOverlay = liveGate.indexOf("< prisma/rls/portal-policy-contract-isolation.sql");
|
|
const domainOverlay = liveGate.indexOf("< prisma/rls/portal-domain-capabilities.sql");
|
|
|
|
expect(schemaProbe).toBeGreaterThan(-1);
|
|
expect(liveGate).toContain("--from-config-datasource");
|
|
expect(liveGate).toContain("--to-schema prisma/schema");
|
|
expect(liveGate).toContain("--exit-code");
|
|
expect(policyProbe).toBeGreaterThan(schemaProbe);
|
|
expect(dbPush).toBeGreaterThan(policyProbe);
|
|
expect(tenantOverlay).toBeGreaterThan(dbPush);
|
|
expect(actorOverlay).toBeGreaterThan(tenantOverlay);
|
|
expect(portalOverlay).toBeGreaterThan(actorOverlay);
|
|
expect(domainOverlay).toBeGreaterThan(portalOverlay);
|
|
expect(liveGate).toContain("packages/db/src/portal-p26b-rls-live.integration.test.ts");
|
|
expect(liveGate).toContain("packages/db/src/portal-domain-capabilities.rls.integration.test.ts");
|
|
});
|
|
|
|
it("fails strict mode closed and gates every mutation behind the feature-branch overlay flag", async () => {
|
|
const liveGate = await readFile(liveGatePath, "utf8");
|
|
|
|
expect(liveGate).toContain('elif [[ "$REQUIRE_MIGRATION_CHAIN_PARITY" == "1" ]]; then');
|
|
expect(liveGate).toContain("strict mode never runs db push or a policy overlay");
|
|
expect(liveGate.match(/if \[\[ "\$USE_FEATURE_BRANCH_OVERLAY" == "1" \]\]; then/g)).toHaveLength(2);
|
|
expect(liveGate).toContain(
|
|
"MODE migration-chain: fresh migrations provide the current schema and exact portal policy contract; db push and manual policy overlays are disabled"
|
|
);
|
|
expect(liveGate).toContain("MODE feature-branch-overlay: migration-chain parity is not available yet");
|
|
});
|
|
|
|
it("rejects invalid strict-mode values before starting Docker", () => {
|
|
const result = spawnSync("bash", [liveGatePath], {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
env: {
|
|
...process.env,
|
|
HIVEGRID_RLS_REQUIRE_MIGRATION_CHAIN_PARITY: "yes",
|
|
},
|
|
});
|
|
|
|
expect(result.status).toBe(1);
|
|
expect(result.stderr).toContain("HIVEGRID_RLS_REQUIRE_MIGRATION_CHAIN_PARITY must be exactly 0 or 1");
|
|
expect(result.stdout).not.toContain("PHASE 1/8");
|
|
});
|
|
|
|
it("keeps all CI and exact-revision deploy callers in strict mode", async () => {
|
|
const workflowPaths = [".gitea/workflows/ci.yml", ".gitea/workflows/deploy.yml"];
|
|
|
|
for (const workflowPath of workflowPaths) {
|
|
const workflow = await readFile(join(root, workflowPath), "utf8");
|
|
const liveStepStart = workflow.indexOf("run: pnpm run verify:rls-live");
|
|
const nextStepStart = workflow.indexOf("\n - name:", liveStepStart + 1);
|
|
const liveStep = workflow.slice(liveStepStart, nextStepStart === -1 ? workflow.length : nextStepStart);
|
|
expect(liveStepStart, workflowPath).toBeGreaterThan(-1);
|
|
expect(liveStep, workflowPath).toContain('HIVEGRID_RLS_REQUIRE_MIGRATION_CHAIN_PARITY: "1"');
|
|
}
|
|
});
|
|
|
|
it("uses the canonical exact portal runtime-readiness contract for policy parity", async () => {
|
|
const verifier = await readFile(join(root, "scripts/verify-portal-migration-chain-policy-state.ts"), "utf8");
|
|
|
|
expect(verifier).toContain('from "@hivegrid/db/portal-rls-runtime-readiness.server"');
|
|
expect(verifier).toContain("verifyPortalRlsRuntimeReadiness([client]");
|
|
expect(verifier).toContain('RLS_RUNTIME_MODE: "required"');
|
|
expect(verifier).toContain('"../prisma/rls/tenant-policy-contract-isolation.sql"');
|
|
expect(verifier).toContain("verifyPortalAppWorkerPolicyParity(");
|
|
expect(verifier).toContain("disconnectDbClients");
|
|
});
|
|
});
|