1import { RuntimeContext } from "alchemy";
2import * as Neon from "alchemy/Neon";
3import * as Test from "alchemy/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { BetterAuth, Database } from "@/index.ts";
7import { applyMigrations } from "@/Migrate.ts";
8import { Postgres } from "@/Postgres.ts";
9
10const { test } = Test.make({ providers: Neon.providers() });
11
12const baseOptions = {
13 baseURL: "http://localhost:3000",
14 emailAndPassword: { enabled: true },
15 secret: "test-secret-test-secret-test-secret",
16} as const;
17
18test.provider(
19 "Postgres layer: migrate + auth roundtrip against Neon",
20 (stack) =>
21 Effect.gen(function* () {
22 yield* stack.destroy();
23 const project = yield* stack.deploy(Neon.Project("BetterAuthPgProject"));
24
25 // resolved connection string — the same layer users feed an Output
26 const layer = Postgres(project.connectionUri);
27 const db = yield* Database.pipe(Effect.provide(layer));
28 expect(db.migrate).toBeDefined();
29
30 const first = yield* applyMigrations(db.migrate!, baseOptions);
31 expect(first.tablesCreated).toBeGreaterThan(0);
32 const second = yield* applyMigrations(db.migrate!, baseOptions);
33 expect(second.tablesCreated).toBe(0);
34 expect(second.tablesAltered).toBe(0);
35
36 // full auth flow against the real Neon Postgres, in-process (bun
37 // provides the execution scope the per-execution pool rides on)
38 yield* Effect.scoped(
39 Effect.gen(function* () {
40 const auth = yield* BetterAuth(baseOptions);
41 const signUp = yield* auth.api.signUpEmail({
42 body: {
43 email: "neon@example.com",
44 password: "password1234",
45 name: "Neon User",
46 },
47 });
48 expect(signUp.user.email).toBe("neon@example.com");
49 const signIn = yield* auth.api.signInEmail({
50 body: { email: "neon@example.com", password: "password1234" },
51 });
52 expect(signIn.token).toBeDefined();
53 }).pipe(Effect.provide(layer)),
54 );
55
56 yield* stack.destroy();
57 }).pipe(Effect.provide(RuntimeContext.phantom)),
58 {
59 tags: ["provider:neon", "provider:neon:project", "live"],
60 timeout: 120_000,
61 },
62);