1import * as AWS from "@/AWS";
2import type { SubnetId } from "@/AWS/EC2/Subnet.ts";
3import { ClusterSubnetGroup } from "@/AWS/Redshift";
4import * as Test from "@/Test/Alchemy";
5import * as EC2 from "@distilled.cloud/aws/ec2";
6import * as redshift from "@distilled.cloud/aws/redshift";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import { getDefaultVpc } from "../DefaultVpc.ts";
10
11const { test } = Test.make({ providers: AWS.providers() });
12
13// Ungated typed-error probe: proves the distilled error union carries the
14// not-found tag this provider's read/delete paths depend on.
15test.provider(
16 "describeClusterSubnetGroups on a nonexistent group fails with ClusterSubnetGroupNotFoundFault",
17 () =>
18 Effect.gen(function* () {
19 const error = yield* Effect.flip(
20 redshift.describeClusterSubnetGroups({
21 ClusterSubnetGroupName: "alchemy-nonexistent-redshift-sng-probe",
22 }),
23 );
24 expect(error._tag).toBe("ClusterSubnetGroupNotFoundFault");
25 }),
26 { tags: ["provider:aws", "provider:aws:redshift", "live"] },
27);
28
29// Resolve default-for-AZ subnets in the default VPC (sorted for determinism).
30const defaultSubnets = Effect.gen(function* () {
31 const vpc = yield* getDefaultVpc;
32 const subnets = yield* EC2.describeSubnets({
33 Filters: [
34 { Name: "vpc-id", Values: [vpc.vpcId] },
35 { Name: "default-for-az", Values: ["true"] },
36 ],
37 });
38 const subnetIds = (subnets.Subnets ?? [])
39 .map((s) => s.SubnetId)
40 .filter((id): id is string => id !== undefined)
41 .sort();
42 if (subnetIds.length < 2) {
43 return yield* Effect.die(
44 new Error("default VPC has fewer than 2 default-for-az subnets"),
45 );
46 }
47 return subnetIds as SubnetId[];
48});
49
50test.provider(
51 "create, update subnets and tags, destroy",
52 (stack) =>
53 Effect.gen(function* () {
54 yield* stack.destroy();
55
56 const subnetIds = yield* defaultSubnets;
57
58 // Create with a single subnet.
59 const created = yield* stack.deploy(
60 Effect.gen(function* () {
61 return yield* ClusterSubnetGroup("Subnets", {
62 description: "alchemy redshift subnet group",
63 subnetIds: subnetIds.slice(0, 1),
64 tags: { fixture: "redshift-subnet-group" },
65 });
66 }),
67 );
68
69 expect(created.clusterSubnetGroupName).toBeDefined();
70 expect(created.clusterSubnetGroupArn).toContain(":subnetgroup:");
71 expect(created.subnetIds).toEqual(subnetIds.slice(0, 1));
72 expect(created.vpcId).toBeDefined();
73 expect(created.tags.fixture).toBe("redshift-subnet-group");
74
75 // Out-of-band verification via distilled, tags included.
76 const observed = yield* redshift.describeClusterSubnetGroups({
77 ClusterSubnetGroupName: created.clusterSubnetGroupName,
78 });
79 const group = observed.ClusterSubnetGroups?.[0];
80 expect(group?.SubnetGroupStatus).toBe("Complete");
81 expect(
82 group?.Tags?.some(
83 (t) => t.Key === "fixture" && t.Value === "redshift-subnet-group",
84 ),
85 ).toBe(true);
86
87 // Update — expand to two subnets, change description, swap tags.
88 const updated = yield* stack.deploy(
89 Effect.gen(function* () {
90 return yield* ClusterSubnetGroup("Subnets", {
91 description: "alchemy redshift subnet group (updated)",
92 subnetIds: subnetIds.slice(0, 2),
93 tags: { stage: "updated" },
94 });
95 }),
96 );
97
98 expect(updated.clusterSubnetGroupName).toBe(
99 created.clusterSubnetGroupName,
100 );
101 expect(updated.description).toBe(
102 "alchemy redshift subnet group (updated)",
103 );
104 expect([...updated.subnetIds].sort()).toEqual(subnetIds.slice(0, 2));
105 expect(updated.tags.stage).toBe("updated");
106 expect(updated.tags.fixture).toBeUndefined();
107
108 const reobserved = yield* redshift.describeClusterSubnetGroups({
109 ClusterSubnetGroupName: created.clusterSubnetGroupName,
110 });
111 const regroup = reobserved.ClusterSubnetGroups?.[0];
112 expect(regroup?.Subnets?.length).toBe(2);
113 expect(regroup?.Tags?.some((t) => t.Key === "fixture")).toBe(false);
114
115 // Destroy and verify gone with the typed not-found tag.
116 yield* stack.destroy();
117 const error = yield* Effect.flip(
118 redshift.describeClusterSubnetGroups({
119 ClusterSubnetGroupName: created.clusterSubnetGroupName,
120 }),
121 );
122 expect(error._tag).toBe("ClusterSubnetGroupNotFoundFault");
123 }),
124 {
125 tags: ["provider:aws", "provider:aws:ec2", "provider:aws:redshift", "live"],
126 timeout: 120_000,
127 },
128);