1import * as Test from "@/Test/Alchemy";
2import type { TestOptions } from "alchemy-test";
3import { makeApiGatewayTestLease } from "./TestLease.ts";
4
5const apiGatewayOptions = (options: TestOptions | undefined): TestOptions => {
6 const normalized =
7 typeof options === "number" ? { timeout: options } : options;
8 return {
9 ...normalized,
10 // Once a file owns the service lease an individual lifecycle is fast —
11 // observed passes at suite concurrency 32 run 3–30s. The failures that
12 // motivated this budget were throttle storms at concurrency 64: the
13 // account-wide REST control-plane quota is shared with ApiGatewayV2 and
14 // the rest of the run, so distilled's TooManyRequests backoff can stack
15 // a 3s operation past 120s. 300s absorbs the storm while still failing
16 // a genuine provider hang fast relative to the full-run timeout.
17 timeout: normalized?.timeout ?? 300_000,
18 // API Gateway REST mutations share an account-wide throttle. A timed-out
19 // retry starts with a fresh scratch state and therefore cannot reclaim the
20 // physical API from the interrupted attempt, so these live tests run once.
21 // TestLease serializes only this service's files; do not use the runner's
22 // global `exclusive` lock here because that would also block unrelated AWS
23 // services while API Gateway waits on its slow control plane.
24 retry: 0,
25 };
26};
27
28/**
29 * API Gateway REST test adapter.
30 *
31 * The old Vitest project ran this directory in a single fork with file
32 * concurrency disabled. A shared root-hook lease restores that scheduling
33 * contract without teaching the generic runner about service paths.
34 */
35export const make = <ROut = any>(options: Test.MakeOptions<ROut>) => {
36 const api = Test.make(options);
37 const testLease = makeApiGatewayTestLease();
38
39 // The first 12 files can enter the runner together. Waiting here is
40 // intentional service scheduling rather than a cloud operation, so allow
41 // the queue to span the full hard-walled AWS run.
42 api.beforeAll(testLease.acquire, { timeout: 3_600_000 });
43 api.afterAll(testLease.release);
44
45 const provider = api.test.provider;
46
47 const providerWithOptions = ((name, fn, testOptions) =>
48 provider(name, fn, apiGatewayOptions(testOptions))) as typeof provider;
49 providerWithOptions.skip = (name, fn, testOptions) =>
50 provider.skip(name, fn, apiGatewayOptions(testOptions));
51 providerWithOptions.skipIf = (condition) => (name, fn, testOptions) =>
52 provider.skipIf(condition)(name, fn, apiGatewayOptions(testOptions));
53
54 api.test.provider = providerWithOptions;
55 return api;
56};