| 1 | import { RuntimeContext } from "alchemy"; |
| 2 | import * as Neon from "alchemy/Neon"; |
| 3 | import * as Test from "alchemy/Test/Alchemy"; |
| 4 | import { expect } from "alchemy-test"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import { BetterAuth, Database } from "@/index.ts"; |
| 7 | import { applyMigrations } from "@/Migrate.ts"; |
| 8 | import { Postgres } from "@/Postgres.ts"; |
| 9 | |
| 10 | const { test } = Test.make({ providers: Neon.providers() }); |
| 11 | |
| 12 | const baseOptions = { |
| 13 | baseURL: "http://localhost:3000", |
| 14 | emailAndPassword: { enabled: true }, |
| 15 | secret: "test-secret-test-secret-test-secret", |
| 16 | } as const; |
| 17 | |
| 18 | test.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 | ); |