1import * as AWS from "@/AWS";
2import * as Test from "@/Test/Alchemy";
3import * as Lambda from "@distilled.cloud/aws/lambda";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import * as HttpClient from "effect/http/HttpClient";
8import {
9 materializeIsolatedProject,
10 removeIsolatedProject,
11} from "../../IsolatedProject.ts";
12import IsolatedProjectFunctionLive, {
13 BODY,
14 IsolatedProjectFunction,
15 project,
16} from "./fixtures/isolated-project-handler.ts";
17
18const { test } = Test.make({ providers: AWS.providers() });
19
20// Live proof that the Lambda (zip) bootstrap loads when the function's
21// `main` lives in an isolated project (see test/IsolatedProject.ts) — the
22// bundle `cwd` resolves none of alchemy's dependencies, so the bootstrap's
23// `@distilled.cloud/aws/*` / `@effect/platform-node` imports must be bundled
24// by the virtual-entry plugin rather than found from the project root. With
25// them left external the runtime fails at init (`Cannot find package`) and
26// the function URL answers 5xx.
27test.provider(
28 "function bundled from an isolated project serves its URL",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* materializeIsolatedProject(project);
32 yield* stack.destroy();
33
34 try {
35 const { functionName, functionUrl } = yield* stack.deploy(
36 IsolatedProjectFunction.pipe(
37 Effect.provide(IsolatedProjectFunctionLive),
38 ),
39 );
40 expect(functionUrl).toBeTruthy();
41
42 const response = yield* HttpClient.get(functionUrl!).pipe(
43 Effect.flatMap((res) =>
44 res.status === 200
45 ? Effect.succeed(res)
46 : Effect.fail(new Error(`Function URL returned ${res.status}`)),
47 ),
48 // Bounded: a bootstrap that fails at init answers 5xx forever.
49 Effect.retry({ schedule: Schedule.spaced("3 seconds"), times: 40 }),
50 );
51 expect(yield* response.text).toBe(BODY);
52
53 yield* stack.destroy();
54 // Out-of-band proof the destroy removed the function (bounded retry
55 // to ride out delete propagation).
56 yield* Lambda.getFunction({ FunctionName: functionName }).pipe(
57 Effect.flatMap(() =>
58 Effect.fail(new Error(`Function ${functionName} still exists`)),
59 ),
60 Effect.catchTag("ResourceNotFoundException", () => Effect.void),
61 Effect.retry({
62 schedule: Schedule.max([
63 Schedule.exponential(500),
64 Schedule.recurs(8),
65 ]),
66 }),
67 );
68 } finally {
69 yield* removeIsolatedProject(project);
70 }
71 }),
72 { tags: ["provider:aws", "provider:aws:lambda", "live"], timeout: 300_000 },
73);