1import * as AuditManager from "@/AWS/AuditManager";
2import * as Lambda from "@/AWS/Lambda";
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// A well-formed-but-nonexistent control domain id — drives the typed error
12// path for ListControlInsightsByControlDomain.
13const NONEXISTENT_CONTROL_DOMAIN = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
14
15export class AuditManagerTestFunction extends Lambda.Function<Lambda.Function>()(
16 "AuditManagerTestFunction",
17) {}
18
19/**
20 * Account-level Audit Manager bindings fixture.
21 *
22 * The testing account is NOT registered with Audit Manager (the service
23 * entered maintenance mode on 2026-04-30, so it can never be registered).
24 * `GetAccountStatus` still succeeds on unregistered accounts — proving a
25 * real end-to-end success through the binding + attached IAM policy — while
26 * every other operation answers the typed `AccessDeniedException`, proving
27 * the wiring and typed error union for each capability.
28 */
29export default AuditManagerTestFunction.make(
30 {
31 main,
32 functionUrl: true,
33 },
34 Effect.gen(function* () {
35 const getAccountStatus = yield* AuditManager.GetAccountStatus();
36 const getServicesInScope = yield* AuditManager.GetServicesInScope();
37 const getInsights = yield* AuditManager.GetInsights();
38 const listControlDomainInsights =
39 yield* AuditManager.ListControlDomainInsights();
40 const listControlInsightsByControlDomain =
41 yield* AuditManager.ListControlInsightsByControlDomain();
42 const getDelegations = yield* AuditManager.GetDelegations();
43 const getEvidenceFileUploadUrl =
44 yield* AuditManager.GetEvidenceFileUploadUrl();
45 const listAssessmentReports = yield* AuditManager.ListAssessmentReports();
46 const validateAssessmentReportIntegrity =
47 yield* AuditManager.ValidateAssessmentReportIntegrity();
48 const listKeywordsForDataSource =
49 yield* AuditManager.ListKeywordsForDataSource();
50 const listNotifications = yield* AuditManager.ListNotifications();
51
52 const bound = {
53 getAccountStatus,
54 getServicesInScope,
55 getInsights,
56 listControlDomainInsights,
57 listControlInsightsByControlDomain,
58 getDelegations,
59 getEvidenceFileUploadUrl,
60 listAssessmentReports,
61 validateAssessmentReportIntegrity,
62 listKeywordsForDataSource,
63 listNotifications,
64 };
65
66 return {
67 fetch: Effect.gen(function* () {
68 const request = yield* HttpServerRequest;
69 const url = new URL(request.originalUrl);
70 const pathname = url.pathname;
71
72 if (request.method === "GET" && pathname === "/bindings") {
73 return yield* HttpServerResponse.json({
74 bound: Object.keys(bound),
75 });
76 }
77
78 if (request.method === "GET" && pathname === "/account-status") {
79 // Succeeds even on unregistered accounts — a REAL success through
80 // the binding's IAM grant.
81 const result = yield* getAccountStatus();
82 return yield* HttpServerResponse.json({
83 ok: true,
84 status: result.status ?? null,
85 });
86 }
87
88 if (request.method === "GET" && pathname === "/services-in-scope") {
89 const result = yield* getServicesInScope().pipe(
90 Effect.map((r) => ({
91 ok: true as const,
92 count: (r.serviceMetadata ?? []).length,
93 })),
94 Effect.catchTag(
95 ["AccessDeniedException", "ValidationException"],
96 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
97 ),
98 );
99 return yield* HttpServerResponse.json(result);
100 }
101
102 if (request.method === "GET" && pathname === "/insights") {
103 const result = yield* getInsights().pipe(
104 Effect.map((r) => ({
105 ok: true as const,
106 activeAssessmentsCount: r.insights?.activeAssessmentsCount ?? 0,
107 })),
108 Effect.catchTag("AccessDeniedException", (e) =>
109 Effect.succeed({ ok: false as const, tag: e._tag }),
110 ),
111 );
112 return yield* HttpServerResponse.json(result);
113 }
114
115 if (
116 request.method === "GET" &&
117 pathname === "/control-domain-insights"
118 ) {
119 const result = yield* listControlDomainInsights({
120 maxResults: 20,
121 }).pipe(
122 Effect.map((r) => ({
123 ok: true as const,
124 count: (r.controlDomainInsights ?? []).length,
125 })),
126 Effect.catchTag(
127 [
128 "AccessDeniedException",
129 "ResourceNotFoundException",
130 "ValidationException",
131 ],
132 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
133 ),
134 );
135 return yield* HttpServerResponse.json(result);
136 }
137
138 if (request.method === "GET" && pathname === "/control-insights") {
139 const result = yield* listControlInsightsByControlDomain({
140 controlDomainId: NONEXISTENT_CONTROL_DOMAIN,
141 }).pipe(
142 Effect.map((r) => ({
143 ok: true as const,
144 count: (r.controlInsightsMetadata ?? []).length,
145 })),
146 Effect.catchTag(
147 [
148 "AccessDeniedException",
149 "ResourceNotFoundException",
150 "ValidationException",
151 ],
152 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
153 ),
154 );
155 return yield* HttpServerResponse.json(result);
156 }
157
158 if (request.method === "GET" && pathname === "/delegations") {
159 const result = yield* getDelegations({ maxResults: 20 }).pipe(
160 Effect.map((r) => ({
161 ok: true as const,
162 count: (r.delegations ?? []).length,
163 })),
164 Effect.catchTag(
165 ["AccessDeniedException", "ValidationException"],
166 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
167 ),
168 );
169 return yield* HttpServerResponse.json(result);
170 }
171
172 if (request.method === "GET" && pathname === "/upload-url") {
173 const result = yield* getEvidenceFileUploadUrl({
174 fileName: "alchemy-evidence.txt",
175 }).pipe(
176 Effect.map((r) => ({
177 ok: true as const,
178 hasUrl: typeof r.uploadUrl === "string",
179 })),
180 Effect.catchTag(
181 [
182 "AccessDeniedException",
183 "ThrottlingException",
184 "ValidationException",
185 ],
186 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
187 ),
188 );
189 return yield* HttpServerResponse.json(result);
190 }
191
192 if (request.method === "GET" && pathname === "/reports") {
193 const result = yield* listAssessmentReports({ maxResults: 20 }).pipe(
194 Effect.map((r) => ({
195 ok: true as const,
196 count: (r.assessmentReports ?? []).length,
197 })),
198 Effect.catchTag(
199 ["AccessDeniedException", "ValidationException"],
200 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
201 ),
202 );
203 return yield* HttpServerResponse.json(result);
204 }
205
206 if (request.method === "GET" && pathname === "/validate-report") {
207 const result = yield* validateAssessmentReportIntegrity({
208 s3RelativePath: "s3://alchemy-nonexistent-bucket/report.zip",
209 }).pipe(
210 Effect.map((r) => ({
211 ok: true as const,
212 valid: r.signatureValid ?? false,
213 })),
214 Effect.catchTag(
215 [
216 "AccessDeniedException",
217 "ResourceNotFoundException",
218 "ValidationException",
219 ],
220 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
221 ),
222 );
223 return yield* HttpServerResponse.json(result);
224 }
225
226 if (request.method === "GET" && pathname === "/keywords") {
227 const result = yield* listKeywordsForDataSource({
228 source: "AWS_Cloudtrail",
229 }).pipe(
230 Effect.map((r) => ({
231 ok: true as const,
232 count: (r.keywords ?? []).length,
233 })),
234 Effect.catchTag(
235 ["AccessDeniedException", "ValidationException"],
236 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
237 ),
238 );
239 return yield* HttpServerResponse.json(result);
240 }
241
242 if (request.method === "GET" && pathname === "/notifications") {
243 const result = yield* listNotifications({ maxResults: 20 }).pipe(
244 Effect.map((r) => ({
245 ok: true as const,
246 count: (r.notifications ?? []).length,
247 })),
248 Effect.catchTag(
249 ["AccessDeniedException", "ValidationException"],
250 (e) => Effect.succeed({ ok: false as const, tag: e._tag }),
251 ),
252 );
253 return yield* HttpServerResponse.json(result);
254 }
255
256 return yield* HttpServerResponse.json(
257 { error: "Not found", method: request.method, pathname },
258 { status: 404 },
259 );
260 }).pipe(Effect.orDie),
261 };
262 }).pipe(
263 Effect.provide(
264 Layer.mergeAll(
265 AuditManager.GetAccountStatusHttp,
266 AuditManager.GetServicesInScopeHttp,
267 AuditManager.GetInsightsHttp,
268 AuditManager.ListControlDomainInsightsHttp,
269 AuditManager.ListControlInsightsByControlDomainHttp,
270 AuditManager.GetDelegationsHttp,
271 AuditManager.GetEvidenceFileUploadUrlHttp,
272 AuditManager.ListAssessmentReportsHttp,
273 AuditManager.ValidateAssessmentReportIntegrityHttp,
274 AuditManager.ListKeywordsForDataSourceHttp,
275 AuditManager.ListNotificationsHttp,
276 ),
277 ),
278 ),
279);