1import * as AWS from "@/AWS";
2import { EIP, InternetGateway, NatGateway, Subnet, Vpc } from "@/AWS/EC2";
3import * as Provider from "@/Provider";
4import * as Test from "./VpcTest.ts";
5import * as EC2 from "@distilled.cloud/aws/ec2";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import { assertEipGone, assertVpcGone } from "./Gone.ts";
10
11const { test } = Test.make({ providers: AWS.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18// NAT gateways take ~1–2 min to become `available` and ~1–2 min to delete, so a
19// full create + list + destroy cycle exceeds the 240s factory test budget.
20// They also require a free VPC quota slot and an Internet Gateway attached to
21// the VPC (a public NAT gateway fails with `Gateway.NotAttached` otherwise).
22// Gate behind AWS_TEST_NAT_GATEWAY=1 so an account with headroom runs it
23// unchanged; the list() implementation itself is exercised by this body.
24test.provider.skipIf(!process.env.AWS_TEST_NAT_GATEWAY)(
25 "list enumerates the deployed NAT Gateway",
26 (stack) =>
27 Effect.gen(function* () {
28 yield* stack.destroy();
29
30 const azResult = yield* EC2.describeAvailabilityZones({});
31 const az1 = azResult.AvailabilityZones?.find(
32 (az) => az.State === "available",
33 )?.ZoneName!;
34
35 // Phase 1: stand up the VPC + IGW + subnet + EIP. The IGW must be
36 // attached before the NAT gateway is created, so split the deploy.
37 yield* stack.deploy(
38 Effect.gen(function* () {
39 const vpc = yield* Vpc("ListNatVpc", {
40 cidrBlock: "10.0.0.0/16",
41 });
42
43 yield* InternetGateway("ListNatIgw", {
44 vpcId: vpc.vpcId,
45 });
46
47 yield* Subnet("ListNatSubnet", {
48 vpcId: vpc.vpcId,
49 cidrBlock: "10.0.1.0/24",
50 availabilityZone: az1,
51 mapPublicIpOnLaunch: true,
52 });
53
54 yield* EIP("ListNatEip", {});
55 }),
56 );
57
58 // Phase 2: add the NAT gateway now that the IGW is attached.
59 const deployed = yield* stack.deploy(
60 Effect.gen(function* () {
61 const vpc = yield* Vpc("ListNatVpc", {
62 cidrBlock: "10.0.0.0/16",
63 });
64
65 const igw = yield* InternetGateway("ListNatIgw", {
66 vpcId: vpc.vpcId,
67 });
68
69 const subnet = yield* Subnet("ListNatSubnet", {
70 vpcId: vpc.vpcId,
71 cidrBlock: "10.0.1.0/24",
72 availabilityZone: az1,
73 mapPublicIpOnLaunch: true,
74 });
75
76 const eip = yield* EIP("ListNatEip", {});
77
78 const natGateway = yield* NatGateway("ListNatGateway", {
79 subnetId: subnet.subnetId,
80 allocationId: eip.allocationId,
81 });
82
83 return { vpc, eip, igw, natGateway };
84 }),
85 );
86
87 const provider = yield* Provider.findProvider(NatGateway);
88 const all = yield* provider.list();
89
90 expect(
91 all.some((x) => x.natGatewayId === deployed.natGateway.natGatewayId),
92 ).toBe(true);
93
94 yield* stack.destroy();
95
96 // Zero-orphan proof: the EIP is released and the VPC (which cannot
97 // delete while the NAT gateway/subnet/IGW exist) is gone.
98 yield* assertEipGone(deployed.eip.allocationId);
99 yield* assertVpcGone(deployed.vpc.vpcId);
100 }).pipe(logLevel),
101 { tags: ["provider:aws", "provider:aws:ec2", "live"], timeout: 900_000 },
102);