feat: establish core identity foundation
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]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
df0f20d8af
commit
679a5d225c
@@ -0,0 +1,67 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
const poolConstructor = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("pg", () => ({ Pool: poolConstructor }));
|
||||
|
||||
import { createIdentityDrizzleDatabase, IdentityDatabaseError } from "../src/identity/database.js";
|
||||
|
||||
describe("Core identity Drizzle database composition", () => {
|
||||
it("does not construct a pool merely because the module is imported", () => {
|
||||
expect(poolConstructor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
"",
|
||||
" ",
|
||||
"not-a-database-url",
|
||||
"https://admin:[email protected]/database",
|
||||
])("rejects an invalid connection string without exposing it (%s)", (connectionString) => {
|
||||
let captured: unknown;
|
||||
try {
|
||||
createIdentityDrizzleDatabase(connectionString);
|
||||
} catch (error) {
|
||||
captured = error;
|
||||
}
|
||||
|
||||
expect(captured).toBeInstanceOf(IdentityDatabaseError);
|
||||
expect(captured).toMatchObject({ code: "IDENTITY_DATABASE_CONFIGURATION_INVALID" });
|
||||
const publicText = `${String(captured)}\n${
|
||||
captured instanceof Error ? (captured.stack ?? "") : ""
|
||||
}`;
|
||||
if (connectionString) {
|
||||
expect(publicText).not.toContain(connectionString);
|
||||
}
|
||||
expect(poolConstructor).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("explicitly constructs the pool only when called, with the identity schema attached", () => {
|
||||
poolConstructor.mockImplementation(function fakePool() {
|
||||
return { end: async () => undefined };
|
||||
});
|
||||
|
||||
const handle = createIdentityDrizzleDatabase(
|
||||
"postgresql://operator:[email protected]/ekklesia",
|
||||
);
|
||||
|
||||
expect(poolConstructor).toHaveBeenCalledTimes(1);
|
||||
expect(handle.db).toBeDefined();
|
||||
expect(typeof handle.close).toBe("function");
|
||||
expect(Object.isFrozen(handle)).toBe(true);
|
||||
});
|
||||
|
||||
it("passes the connection timeout through to the pool configuration", () => {
|
||||
poolConstructor.mockImplementation(function fakePool() {
|
||||
return { end: async () => undefined };
|
||||
});
|
||||
poolConstructor.mockClear();
|
||||
|
||||
createIdentityDrizzleDatabase("postgresql://example.test/ekklesia", {
|
||||
connectionTimeoutMs: 5_000,
|
||||
});
|
||||
|
||||
expect(poolConstructor).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ connectionTimeoutMillis: 5_000 }),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user