52 lines
2.7 KiB
TypeScript
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")
|
|
})
|
|
})
|