1import * as AWS from "alchemy/AWS";
2import * as Core from "alchemy/Test/Core";
3import * as Test from "alchemy/Test/Alchemy";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import AuroraAuthFunctionLive, {
8 AuroraAuthFunction,
9} from "./fixtures/aurora-handler.ts";
10import { AuthHttpError, getJson, postJson, toCookieHeader } from "../http.ts";
11
12/**
13 * Aurora Serverless v2 provisioning takes ~10 minutes, so this suite is
14 * skipped under `--fast` (the established FAST=1 convention for
15 * long-provisioning resources). The Data API dialect itself is pinned
16 * ungated by AuroraDataApi.test.ts.
17 */
18const enabled = !process.env.FAST;
19
20const testOptions = { providers: AWS.providers() };
21const { test, beforeAll, afterAll } = Test.make(testOptions);
22const sharedStack = Core.scratchStack(
23 testOptions,
24 "BetterAuthAurora",
25 "test/AWS/Aurora.test.ts",
26);
27
28let baseUrl: string;
29
30const readinessRetry = Effect.retry({
31 schedule: Schedule.exponential("3 seconds", 1.5),
32 times: 10,
33});
34
35beforeAll(
36 Effect.gen(function* () {
37 if (!enabled) {
38 return;
39 }
40 if (!process.env.NO_DESTROY) {
41 yield* sharedStack.destroy();
42 }
43 const { functionUrl } = yield* sharedStack.deploy(
44 Effect.gen(function* () {
45 return yield* AuroraAuthFunction;
46 }).pipe(Effect.provide(AuroraAuthFunctionLive)),
47 );
48 expect(functionUrl).toBeTruthy();
49 baseUrl = functionUrl!.replace(/\/+$/, "");
50 yield* getJson<{ email: string | null }>(`${baseUrl}/me`).pipe(
51 Effect.tapError((error) =>
52 Effect.logWarning(`Aurora Lambda not ready yet: ${error.message}`),
53 ),
54 readinessRetry,
55 );
56 }),
57 { timeout: 1_500_000 },
58);
59
60afterAll.skipIf(!!process.env.NO_DESTROY)(sharedStack.destroy(), {
61 timeout: 1_500_000,
62});
63
64test.skipIf(!enabled)(
65 "Lambda -> Aurora over the Data API: sign-up, sign-in, session",
66 Effect.gen(function* () {
67 const email = "aurora-user@example.com";
68 const password = "password1234";
69
70 yield* postJson(`${baseUrl}/auth/sign-up/email`, {
71 email,
72 password,
73 name: "Aurora User",
74 }).pipe(
75 Effect.filterOrFail(
76 (response) =>
77 response.status === 200 ||
78 response.body.includes("USER_ALREADY_EXISTS"),
79 (response) => new AuthHttpError({ url: baseUrl, ...response }),
80 ),
81 );
82
83 const signIn = yield* postJson(`${baseUrl}/auth/sign-in/email`, {
84 email,
85 password,
86 }).pipe(
87 Effect.filterOrFail(
88 (response) => response.status === 200,
89 (response) => new AuthHttpError({ url: baseUrl, ...response }),
90 ),
91 );
92 expect(signIn.setCookies.length).toBeGreaterThan(0);
93
94 const me = yield* getJson<{ email: string | null }>(`${baseUrl}/me`, {
95 cookie: toCookieHeader(signIn.setCookies),
96 });
97 expect(me.email).toBe(email);
98 }),
99 {
100 tags: [
101 "provider:aws",
102 "provider:aws:ec2",
103 "provider:aws:lambda",
104 "provider:aws:rds",
105 "live",
106 ],
107 timeout: 120_000,
108 },
109);