Adds Core-owned identity contracts, a Core-owned Better Auth database schema (ekklesia_core_user/session/account/verification), a first-class Core-owned migration source that coexists with module-owned migrations under the exact existing migration system, and an explicit Better Auth construction boundary. No authentication endpoint or sign-in UI is exposed yet (ADR 0010). Co-Authored-By: Claude Sonnet 5 <[email protected]>
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
import { getTableName } from "drizzle-orm";
|
|
import { getTableConfig } from "drizzle-orm/pg-core";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
ekklesiaCoreAccount,
|
|
ekklesiaCoreSession,
|
|
ekklesiaCoreUser,
|
|
ekklesiaCoreVerification,
|
|
identityAdapterSchema,
|
|
} from "../src/identity/schema.js";
|
|
|
|
describe("Core identity Drizzle schema", () => {
|
|
it("uses explicit, unambiguous ekklesia_core_* physical table names", () => {
|
|
expect(getTableName(ekklesiaCoreUser)).toBe("ekklesia_core_user");
|
|
expect(getTableName(ekklesiaCoreSession)).toBe("ekklesia_core_session");
|
|
expect(getTableName(ekklesiaCoreAccount)).toBe("ekklesia_core_account");
|
|
expect(getTableName(ekklesiaCoreVerification)).toBe("ekklesia_core_verification");
|
|
});
|
|
|
|
it("explicitly maps every Better Auth logical model to its physical table object", () => {
|
|
expect(identityAdapterSchema).toEqual({
|
|
account: ekklesiaCoreAccount,
|
|
session: ekklesiaCoreSession,
|
|
user: ekklesiaCoreUser,
|
|
verification: ekklesiaCoreVerification,
|
|
});
|
|
});
|
|
|
|
it("gives the user table a unique email and a default-false verification flag", () => {
|
|
const config = getTableConfig(ekklesiaCoreUser);
|
|
const columns = Object.fromEntries(config.columns.map((column) => [column.name, column]));
|
|
|
|
expect(columns.id?.primary).toBe(true);
|
|
expect(columns.email?.isUnique).toBe(true);
|
|
expect(columns.email?.notNull).toBe(true);
|
|
expect(columns.email_verified?.hasDefault).toBe(true);
|
|
expect(columns.email_verified?.notNull).toBe(true);
|
|
});
|
|
|
|
it("scopes session and account rows to a user with cascading delete", () => {
|
|
for (const table of [ekklesiaCoreSession, ekklesiaCoreAccount]) {
|
|
const config = getTableConfig(table);
|
|
expect(config.foreignKeys).toHaveLength(1);
|
|
const foreignKey = config.foreignKeys[0];
|
|
if (!foreignKey) {
|
|
throw new Error("Expected a foreign key.");
|
|
}
|
|
const reference = foreignKey.reference();
|
|
expect(reference.columns.map((column) => column.name)).toEqual(["user_id"]);
|
|
expect(reference.foreignColumns.map((column) => column.name)).toEqual(["id"]);
|
|
expect(getTableName(reference.foreignTable)).toBe("ekklesia_core_user");
|
|
expect(foreignKey.onDelete).toBe("cascade");
|
|
}
|
|
});
|
|
|
|
it("indexes session.user_id, account.user_id, and verification.identifier for lookup", () => {
|
|
expect(
|
|
getTableConfig(ekklesiaCoreSession).indexes.map((index) =>
|
|
index.config.columns.map((column) => ("name" in column ? column.name : undefined)),
|
|
),
|
|
).toEqual([["user_id"]]);
|
|
expect(
|
|
getTableConfig(ekklesiaCoreAccount).indexes.map((index) =>
|
|
index.config.columns.map((column) => ("name" in column ? column.name : undefined)),
|
|
),
|
|
).toEqual([["user_id"]]);
|
|
expect(
|
|
getTableConfig(ekklesiaCoreVerification).indexes.map((index) =>
|
|
index.config.columns.map((column) => ("name" in column ? column.name : undefined)),
|
|
),
|
|
).toEqual([["identifier"]]);
|
|
});
|
|
|
|
it("requires a unique session token", () => {
|
|
const config = getTableConfig(ekklesiaCoreSession);
|
|
const token = config.columns.find((column) => column.name === "token");
|
|
expect(token?.isUnique).toBe(true);
|
|
expect(token?.notNull).toBe(true);
|
|
});
|
|
|
|
it("keeps every table in the default public PostgreSQL schema", () => {
|
|
for (const table of [
|
|
ekklesiaCoreUser,
|
|
ekklesiaCoreSession,
|
|
ekklesiaCoreAccount,
|
|
ekklesiaCoreVerification,
|
|
]) {
|
|
expect(getTableConfig(table).schema).toBeUndefined();
|
|
}
|
|
});
|
|
});
|