Files
template-app/test/kernel-migration.test.ts
T

52 lines
2.7 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The committed kernel schema, and the rule that keeps it committable.
//
// `migrations/postgres/0001_kernel.sql` is `schemaStatements()` over the kernel's own entities,
// written once. It is a snapshot of the *kernel's* tables alone, not of the whole registry, so
// adding a module does not change this file -- a module ships its own numbered migration, which
// is what a forward-only runner needs (it refuses a migration whose text changed after it ran).
// A kit upgrade that changes a kernel table does change it, and that is exactly the moment a
// customer has to see a new migration in review.
import { authModule } from "@conusai-kit/auth"
import { buildRegistry } from "@conusai-kit/core"
import { entitiesOf } from "@conusai-kit/data"
import { schemaStatements } from "@conusai-kit/data-kysely"
import { KIT_JOBS_ENTITIES } from "@conusai-kit/jobs-pgboss"
import { appUserIdentities, tenancyModule } from "@conusai-kit/tenancy"
import { describe, expect, it } from "vitest"
import { kernelSchema } from "../apps/worker/src/migrations.js"
import { SPDX_LINE } from "../scripts/licence.mjs"
// Tenancy and sign-in alone: later kit tables have their own forward migrations, because 0001 is
// never edited once it has run. app_user_identity belongs to 0006, not this historical snapshot.
const kernelEntities = [
...entitiesOf(buildRegistry([tenancyModule, authModule]), "postgres"),
...KIT_JOBS_ENTITIES,
].filter((entity) => entity.table !== appUserIdentities.table)
describe("the kernel migration", () => {
it("matches the committed 0001_kernel.sql", async () => {
// The file leads with the template's SPDX line (AT5.1); the runner reads it as the comment it is.
await expect(`-- ${SPDX_LINE}\n${kernelSchema(kernelEntities)}`).toMatchFileSnapshot(
"../migrations/postgres/0001_kernel.sql",
)
})
it("puts forced row-level security on every tenant table", () => {
const sql = schemaStatements(kernelEntities).join(";\n")
for (const entity of kernelEntities) {
if (entity.tenancy === "global") continue
expect(sql, `${entity.table} is not forced`).toContain(`alter table "${entity.table}" force row level security`)
expect(sql).toContain(`create policy "${entity.table}_tenant" on "${entity.table}"`)
}
})
it("compares a tenant row against the transaction's organization and nothing else", () => {
const sql = schemaStatements(kernelEntities).join(";\n")
// The policy reads NULLIF(current_setting(...), '')::uuid: a pooled connection whose previous
// transaction set app.org_id sees no rows rather than an error (kit-data-kysely's decision 2).
expect(sql).toContain("nullif(current_setting('app.org_id', true), '')::uuid")
})
})