auth-tenant-pool.integration.test.ts78 lines · main
1/**
2 * Customer auth (Better-Auth) against REAL DoltGres — sprint plan S2.1 + S2.3.
3 *
4 * The riskiest change in the plan: re-platforming the per-tenant Better-Auth
5 * instance off postgres.js + schema-per-project onto pg + database-per-project,
6 * with citext replaced by text + a lower(email) unique index. This test PROVES
7 * the whole customer-login loop actually works on DoltGres:
8 * provision project DB → create auth tables → sign up → sign in (DIFFERENT
9 * email case) → case-insensitive uniqueness is enforced.
10 *
11 * Skips when BRIVEN_DATA_PLANE_URL is unset.
12 */
13import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
14
15import {
16 closeProjectDbPools,
17 dropProjectDatabase,
18 provisionProjectDatabase,
19 runInProjectDatabase,
20} from '../db/data-plane.js';
21import { renderAuthProvisioningSql } from './auth-provisioning.js';
22import { clearAuthInstancePool, getAuthInstance } from './auth-tenant-pool.js';
23
24const PROJECT_ID = `p_authpool${Date.now().toString(36)}`;
25const PASSWORD = 'correct-horse-battery-staple';
26const HAS_DB = Boolean(process.env.BRIVEN_DATA_PLANE_URL);
27
28// Acceptance test for the customer-auth rebuild: S2.1a (pg + db-per-project),
29// S2.3 (citext→text + lower(email) unique), S2.1b (Better-Auth table schema).
30// Skips when no DoltGres is configured.
31describe.skipIf(!HAS_DB)('customer auth on real DoltGres (S2.1 + S2.3)', () => {
32 beforeAll(async () => {
33 await provisionProjectDatabase(PROJECT_ID);
34 // Create the auth tables exactly as the "enable auth" route does.
35 await runInProjectDatabase(PROJECT_ID, async (tx) => {
36 await tx.unsafe('SET dolt_transaction_commit = 1');
37 for (const stmt of renderAuthProvisioningSql()) {
38 await tx.unsafe(stmt);
39 }
40 });
41 });
42
43 afterAll(async () => {
44 await clearAuthInstancePool().catch(() => {});
45 await dropProjectDatabase(PROJECT_ID).catch(() => {});
46 await closeProjectDbPools().catch(() => {});
47 });
48
49 test('sign up, then sign in with a DIFFERENT email case', async () => {
50 const inst = await getAuthInstance(PROJECT_ID);
51
52 const signup = await inst.betterAuth.api.signUpEmail({
53 body: { email: 'Mixed@Case.com', password: PASSWORD, name: 'Test User' },
54 });
55 expect(signup).toBeTruthy();
56 expect(signup.user?.id).toBeTruthy();
57
58 // Sign in with a different-case email — must resolve to the same account.
59 const signin = await inst.betterAuth.api.signInEmail({
60 body: { email: 'mixed@case.com', password: PASSWORD },
61 });
62 expect(signin).toBeTruthy();
63 expect(signin.user?.id).toBe(signup.user.id);
64 });
65
66 test('case-variant email cannot create a duplicate account', async () => {
67 const inst = await getAuthInstance(PROJECT_ID);
68 let threw = false;
69 try {
70 await inst.betterAuth.api.signUpEmail({
71 body: { email: 'MIXED@CASE.COM', password: PASSWORD, name: 'Dup' },
72 });
73 } catch {
74 threw = true;
75 }
76 expect(threw).toBe(true);
77 });
78});