1import * as Lambda from "@/AWS/Lambda";
2import * as RDS from "@/AWS/RDS";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7import path from "pathe";
8
9const main = path.resolve(import.meta.dirname, "handler.ts");
10
11// Well-formed-but-nonexistent identifiers — the probe routes drive the
12// bindings against them so the fixture exercises the IAM grants and the
13// typed error decode at zero cost (no database/snapshot is ever created).
14const NONEXISTENT_CLUSTER_ID = "alchemy-nonexistent-rds-cluster-probe";
15const NONEXISTENT_INSTANCE_ID = "alchemy-nonexistent-rds-instance-probe";
16const NONEXISTENT_CLUSTER_SNAPSHOT_ID =
17 "alchemy-nonexistent-rds-cluster-snapshot-probe";
18const NONEXISTENT_SNAPSHOT_ID = "alchemy-nonexistent-rds-snapshot-probe";
19
20export class RdsBindingsTestFunction extends Lambda.Function<Lambda.Function>()(
21 "RdsBindingsTestFunction",
22) {}
23
24/**
25 * Account-level binding fixture: no RDS database is ever created. List
26 * routes prove each grant and response decode against real (possibly
27 * empty) account data; probe routes drive identifier-addressed operations
28 * against nonexistent identifiers and must surface the service's typed
29 * not-found tag — an IAM gap would surface AccessDeniedException and fail
30 * the route with an opaque 500.
31 *
32 * The cluster/instance-scoped bindings (`CreateDBClusterSnapshot`,
33 * `CreateDBSnapshot`, `FailoverDBCluster`, `Start/StopDBCluster`,
34 * `Start/Stop/RebootDBInstance`) resolve a bound resource at deploy time,
35 * so they need a live database (~10 min, billed hourly) and are exercised
36 * only under RDS_TEST_LIFECYCLE-gated runs.
37 */
38export default RdsBindingsTestFunction.make(
39 {
40 main,
41 functionUrl: true,
42 },
43 Effect.gen(function* () {
44 const describeDBClusters = yield* RDS.DescribeDBClusters();
45 const describeDBInstances = yield* RDS.DescribeDBInstances();
46 const describeDBClusterEndpoints = yield* RDS.DescribeDBClusterEndpoints();
47 const describeEvents = yield* RDS.DescribeEvents();
48 const describeDBClusterSnapshots = yield* RDS.DescribeDBClusterSnapshots();
49 const describeDBSnapshots = yield* RDS.DescribeDBSnapshots();
50 const deleteDBClusterSnapshot = yield* RDS.DeleteDBClusterSnapshot();
51 const copyDBClusterSnapshot = yield* RDS.CopyDBClusterSnapshot();
52 const deleteDBSnapshot = yield* RDS.DeleteDBSnapshot();
53 const copyDBSnapshot = yield* RDS.CopyDBSnapshot();
54 const describePendingMaintenanceActions =
55 yield* RDS.DescribePendingMaintenanceActions();
56 const applyPendingMaintenanceAction =
57 yield* RDS.ApplyPendingMaintenanceAction();
58
59 const bound = {
60 describeDBClusters,
61 describeDBInstances,
62 describeDBClusterEndpoints,
63 describeEvents,
64 describeDBClusterSnapshots,
65 describeDBSnapshots,
66 deleteDBClusterSnapshot,
67 copyDBClusterSnapshot,
68 deleteDBSnapshot,
69 copyDBSnapshot,
70 describePendingMaintenanceActions,
71 applyPendingMaintenanceAction,
72 };
73
74 return {
75 fetch: Effect.gen(function* () {
76 const request = yield* HttpServerRequest;
77 const url = new URL(request.originalUrl);
78 const pathname = url.pathname;
79
80 if (request.method === "GET" && pathname === "/bindings") {
81 return yield* HttpServerResponse.json({
82 bound: Object.keys(bound),
83 });
84 }
85
86 if (request.method === "GET" && pathname === "/clusters") {
87 // A nonexistent cluster identifier must surface the service's
88 // typed not-found tag.
89 const tag = yield* describeDBClusters({
90 DBClusterIdentifier: NONEXISTENT_CLUSTER_ID,
91 }).pipe(
92 Effect.map(() => "Found"),
93 Effect.catchTag("DBClusterNotFoundFault", (e) =>
94 Effect.succeed(e._tag),
95 ),
96 );
97 return yield* HttpServerResponse.json({ tag });
98 }
99
100 if (request.method === "GET" && pathname === "/instances") {
101 // Nonexistent instance identifier → typed not-found tag.
102 const tag = yield* describeDBInstances({
103 DBInstanceIdentifier: NONEXISTENT_INSTANCE_ID,
104 }).pipe(
105 Effect.map(() => "Found"),
106 Effect.catchTag("DBInstanceNotFoundFault", (e) =>
107 Effect.succeed(e._tag),
108 ),
109 );
110 return yield* HttpServerResponse.json({ tag });
111 }
112
113 if (request.method === "GET" && pathname === "/endpoints") {
114 // Listing the account's cluster endpoints succeeds (possibly
115 // empty) — proves the grant and the schema decode.
116 const result = yield* describeDBClusterEndpoints();
117 return yield* HttpServerResponse.json({
118 count: (result.DBClusterEndpoints ?? []).length,
119 });
120 }
121
122 if (request.method === "GET" && pathname === "/events") {
123 // Listing the account's recent RDS events succeeds (possibly
124 // empty) — proves the grant and the schema decode.
125 const result = yield* describeEvents();
126 return yield* HttpServerResponse.json({
127 count: (result.Events ?? []).length,
128 });
129 }
130
131 if (request.method === "GET" && pathname === "/cluster-snapshots") {
132 // Listing the account's cluster snapshots succeeds (possibly
133 // empty).
134 const result = yield* describeDBClusterSnapshots();
135 return yield* HttpServerResponse.json({
136 count: (result.DBClusterSnapshots ?? []).length,
137 });
138 }
139
140 if (request.method === "GET" && pathname === "/instance-snapshots") {
141 // Listing the account's instance snapshots succeeds (possibly
142 // empty).
143 const result = yield* describeDBSnapshots();
144 return yield* HttpServerResponse.json({
145 count: (result.DBSnapshots ?? []).length,
146 });
147 }
148
149 if (
150 request.method === "GET" &&
151 pathname === "/delete-cluster-snapshot-probe"
152 ) {
153 // Deleting a nonexistent cluster snapshot must surface the typed
154 // not-found tag — proves the write-side grant without ever
155 // creating a snapshot.
156 const tag = yield* deleteDBClusterSnapshot({
157 DBClusterSnapshotIdentifier: NONEXISTENT_CLUSTER_SNAPSHOT_ID,
158 }).pipe(
159 Effect.map(() => "Deleted"),
160 Effect.catchTag("DBClusterSnapshotNotFoundFault", (e) =>
161 Effect.succeed(e._tag),
162 ),
163 );
164 return yield* HttpServerResponse.json({ tag });
165 }
166
167 if (
168 request.method === "GET" &&
169 pathname === "/copy-cluster-snapshot-probe"
170 ) {
171 // Copying from a nonexistent source snapshot must surface the
172 // typed not-found tag — proves the copy grant at zero cost.
173 const tag = yield* copyDBClusterSnapshot({
174 SourceDBClusterSnapshotIdentifier: NONEXISTENT_CLUSTER_SNAPSHOT_ID,
175 TargetDBClusterSnapshotIdentifier: `${NONEXISTENT_CLUSTER_SNAPSHOT_ID}-copy`,
176 }).pipe(
177 Effect.map(() => "Copied"),
178 Effect.catchTag("DBClusterSnapshotNotFoundFault", (e) =>
179 Effect.succeed(e._tag),
180 ),
181 );
182 return yield* HttpServerResponse.json({ tag });
183 }
184
185 if (
186 request.method === "GET" &&
187 pathname === "/delete-instance-snapshot-probe"
188 ) {
189 // Deleting a nonexistent instance snapshot must surface the
190 // typed not-found tag.
191 const tag = yield* deleteDBSnapshot({
192 DBSnapshotIdentifier: NONEXISTENT_SNAPSHOT_ID,
193 }).pipe(
194 Effect.map(() => "Deleted"),
195 Effect.catchTag("DBSnapshotNotFoundFault", (e) =>
196 Effect.succeed(e._tag),
197 ),
198 );
199 return yield* HttpServerResponse.json({ tag });
200 }
201
202 if (
203 request.method === "GET" &&
204 pathname === "/copy-instance-snapshot-probe"
205 ) {
206 // Copying from a nonexistent source snapshot must surface the
207 // typed not-found tag.
208 const tag = yield* copyDBSnapshot({
209 SourceDBSnapshotIdentifier: NONEXISTENT_SNAPSHOT_ID,
210 TargetDBSnapshotIdentifier: `${NONEXISTENT_SNAPSHOT_ID}-copy`,
211 }).pipe(
212 Effect.map(() => "Copied"),
213 Effect.catchTag("DBSnapshotNotFoundFault", (e) =>
214 Effect.succeed(e._tag),
215 ),
216 );
217 return yield* HttpServerResponse.json({ tag });
218 }
219
220 if (request.method === "GET" && pathname === "/pending-maintenance") {
221 // Listing pending maintenance actions succeeds (possibly empty).
222 const result = yield* describePendingMaintenanceActions();
223 return yield* HttpServerResponse.json({
224 count: (result.PendingMaintenanceActions ?? []).length,
225 });
226 }
227
228 if (request.method === "GET" && pathname === "/apply-probe") {
229 // Applying maintenance to a nonexistent resource ARN must surface
230 // the typed ResourceNotFoundFault — proves the apply grant.
231 const arn = url.searchParams.get("arn") ?? "";
232 const tag = yield* applyPendingMaintenanceAction({
233 ResourceIdentifier: arn,
234 ApplyAction: "system-update",
235 OptInType: "next-maintenance",
236 }).pipe(
237 Effect.map(() => "Applied"),
238 Effect.catchTag("ResourceNotFoundFault", (e) =>
239 Effect.succeed(e._tag),
240 ),
241 );
242 return yield* HttpServerResponse.json({ tag });
243 }
244
245 return yield* HttpServerResponse.json(
246 { error: "Not found", method: request.method, pathname },
247 { status: 404 },
248 );
249 }).pipe(Effect.orDie),
250 };
251 }).pipe(
252 Effect.provide(
253 Layer.mergeAll(
254 RDS.DescribeDBClustersHttp,
255 RDS.DescribeDBInstancesHttp,
256 RDS.DescribeDBClusterEndpointsHttp,
257 RDS.DescribeEventsHttp,
258 RDS.DescribeDBClusterSnapshotsHttp,
259 RDS.DescribeDBSnapshotsHttp,
260 RDS.DeleteDBClusterSnapshotHttp,
261 RDS.CopyDBClusterSnapshotHttp,
262 RDS.DeleteDBSnapshotHttp,
263 RDS.CopyDBSnapshotHttp,
264 RDS.DescribePendingMaintenanceActionsHttp,
265 RDS.ApplyPendingMaintenanceActionHttp,
266 ),
267 ),
268 ),
269);