1import * as AWS from "@/AWS";
2import { DomainName } from "@/AWS/ApiGatewayV2";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as agw2 from "@distilled.cloud/aws/apigatewayv2";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// Ungated live probe: `list()` enumerates every v2 custom domain in the
12// account/region and maps each item to the `read` Attributes shape. A
13// custom domain cannot be provisioned in CI (it needs a validated ACM
14// certificate for a domain we own — see the gated test below), but this
15// still verifies pagination + mapping against the live API.
16test.provider.skipIf(!!process.env.FAST)(
17 "list returns the account/region domain names",
18 () =>
19 Effect.gen(function* () {
20 const provider = yield* Provider.findProvider(DomainName);
21 const all = yield* provider.list();
22
23 expect(Array.isArray(all)).toBe(true);
24 for (const domain of all) {
25 expect(typeof domain.domainName).toBe("string");
26 expect(domain.tags).toBeDefined();
27 }
28 }),
29 { tags: ["provider:aws", "provider:aws:apigatewayv2", "live"] },
30);
31
32// Full lifecycle. SKIPPED by default: an API Gateway v2 custom domain
33// requires a validated REGIONAL ACM certificate for a domain you own (ACM
34// issuance needs DNS/email validation, which CI cannot complete). Set
35// AWS_TEST_APIGATEWAY_DOMAIN_NAME (a domain you own) and
36// AWS_TEST_APIGATEWAY_CERT_ARN (a validated regional ACM cert in this
37// region) to run it on an entitled account unchanged — the same env vars
38// the v1 DomainName test uses.
39const domainName = process.env.AWS_TEST_APIGATEWAY_DOMAIN_NAME;
40const certificateArn = process.env.AWS_TEST_APIGATEWAY_CERT_ARN;
41
42test.provider.skipIf(!!process.env.FAST || !domainName || !certificateArn)(
43 "create domain name, map an API, delete",
44 (stack) =>
45 Effect.gen(function* () {
46 yield* stack.destroy();
47
48 const out = yield* stack.deploy(
49 Effect.gen(function* () {
50 const domain = yield* AWS.ApiGatewayV2.DomainName("V2Domain", {
51 domainName: domainName!,
52 domainNameConfigurations: [
53 {
54 CertificateArn: certificateArn!,
55 EndpointType: "REGIONAL",
56 SecurityPolicy: "TLS_1_2",
57 },
58 ],
59 });
60
61 const api = yield* AWS.ApiGatewayV2.Api("V2DomainApi", {});
62 const stage = yield* AWS.ApiGatewayV2.Stage("V2DomainStage", {
63 api,
64 autoDeploy: true,
65 });
66
67 const mapping = yield* AWS.ApiGatewayV2.ApiMapping("V2Mapping", {
68 api,
69 domainName: domain.domainName,
70 stage: stage.stageName,
71 apiMappingKey: "v2test",
72 });
73
74 return {
75 domainName: domain.domainName,
76 apiId: api.apiId,
77 apiMappingId: mapping.apiMappingId,
78 };
79 }),
80 );
81
82 const remoteDomain = yield* agw2.getDomainName({
83 DomainName: out.domainName,
84 });
85 expect(remoteDomain.DomainName).toBe(domainName);
86
87 const remoteMapping = yield* agw2.getApiMapping({
88 DomainName: out.domainName,
89 ApiMappingId: out.apiMappingId,
90 });
91 expect(remoteMapping.ApiId).toBe(out.apiId);
92 expect(remoteMapping.ApiMappingKey).toBe("v2test");
93
94 yield* stack.destroy();
95
96 const gone = yield* agw2
97 .getApiMapping({
98 DomainName: out.domainName,
99 ApiMappingId: out.apiMappingId,
100 })
101 .pipe(
102 Effect.map(() => "still-exists" as const),
103 Effect.catchTag("NotFoundException", () =>
104 Effect.succeed("deleted" as const),
105 ),
106 );
107 expect(gone).toBe("deleted");
108 }),
109 {
110 tags: ["provider:aws", "provider:aws:apigatewayv2", "live"],
111 timeout: 240_000,
112 },
113);