1import * as Lambda from "@/AWS/Lambda";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Redacted from "effect/Redacted";
5import * as Stream from "effect/Stream";
6import { HttpServerRequest } from "effect/http/HttpServerRequest";
7import * as HttpServerResponse from "effect/http/HttpServerResponse";
8import path from "pathe";
9
10const main = path.resolve(import.meta.dirname, "bindings-handler.ts");
11const targetMain = path.resolve(import.meta.dirname, "timeout-handler.ts");
12
13export class LambdaBindingsTestFunction extends Lambda.Function<Lambda.Function>()(
14 "LambdaBindingsTestFunction",
15) {}
16
17export default LambdaBindingsTestFunction.make(
18 {
19 main,
20 functionUrl: true,
21 // Resolve distilled from `src/*.ts` (the `bun` export condition — same
22 // as vitest) so distilled source changes are test-visible in the
23 // deployed bundle without a `lib/` rebuild.
24 build: {
25 resolve: { conditionNames: ["bun", "import", "module", "default"] },
26 },
27 },
28 Effect.gen(function* () {
29 const target = yield* Lambda.Function("BindingsTarget", {
30 main: targetMain,
31 handler: "handler",
32 isExternal: true,
33 functionUrl: false,
34 });
35
36 const invokeFunction = yield* Lambda.InvokeFunction(target);
37 const invokeWithResponseStream =
38 yield* Lambda.InvokeWithResponseStream(target);
39 const getFunction = yield* Lambda.GetFunction(target);
40 const getAccountSettings = yield* Lambda.GetAccountSettings();
41 const listFunctions = yield* Lambda.ListFunctions();
42
43 return {
44 fetch: Effect.gen(function* () {
45 const request = yield* HttpServerRequest;
46 const url = new URL(request.originalUrl);
47 const pathname = url.pathname;
48
49 if (pathname === "/invoke") {
50 const response = yield* invokeFunction({});
51 return yield* HttpServerResponse.json({
52 statusCode: response.StatusCode,
53 executedVersion: response.ExecutedVersion,
54 });
55 }
56
57 if (pathname === "/invoke-stream") {
58 const response = yield* invokeWithResponseStream({});
59 const events = response.EventStream
60 ? Array.from(yield* Stream.runCollect(response.EventStream))
61 : [];
62 const decoder = new TextDecoder();
63 const decodeChunk = (chunk: unknown): string => {
64 const value = Redacted.isRedacted(chunk)
65 ? Redacted.value(chunk)
66 : chunk;
67 if (value instanceof Uint8Array) return decoder.decode(value);
68 if (typeof value === "string") return value;
69 return "";
70 };
71 const payload = events
72 .map((event) =>
73 "PayloadChunk" in event && event.PayloadChunk
74 ? decodeChunk(event.PayloadChunk.Payload)
75 : "",
76 )
77 .join("");
78 const complete = events.some((event) => "InvokeComplete" in event);
79 return yield* HttpServerResponse.json({
80 statusCode: response.StatusCode,
81 payload,
82 complete,
83 // debug: surface the raw event shapes so assertion failures are
84 // diagnosable from the test output
85 eventShapes: events.map((event) =>
86 Object.entries(event).map(
87 ([key, value]) =>
88 `${key}:${value === undefined ? "undefined" : `[${Object.keys(value ?? {}).join(",")}]`}`,
89 ),
90 ),
91 });
92 }
93
94 if (pathname === "/get-function") {
95 const response = yield* getFunction();
96 return yield* HttpServerResponse.json({
97 functionName: response.Configuration?.FunctionName,
98 state: response.Configuration?.State,
99 });
100 }
101
102 if (pathname === "/account-settings") {
103 const response = yield* getAccountSettings();
104 return yield* HttpServerResponse.json({
105 concurrentExecutions: response.AccountLimit?.ConcurrentExecutions,
106 });
107 }
108
109 if (pathname === "/list-functions") {
110 const response = yield* listFunctions({ MaxItems: 5 });
111 return yield* HttpServerResponse.json({
112 count: response.Functions?.length ?? 0,
113 });
114 }
115
116 return yield* HttpServerResponse.json(
117 { error: "Not found", pathname },
118 { status: 404 },
119 );
120 }).pipe(Effect.orDie),
121 };
122 }).pipe(
123 Effect.provide(
124 Layer.mergeAll(
125 Lambda.InvokeFunctionHttp,
126 Lambda.InvokeWithResponseStreamHttp,
127 Lambda.GetFunctionHttp,
128 Lambda.GetAccountSettingsHttp,
129 Lambda.ListFunctionsHttp,
130 ),
131 ),
132 ),
133);