1import * as AWS from "@/AWS";
2import { SubnetGroup } from "@/AWS/MemoryDB";
3import * as Test from "@/Test/Alchemy";
4import * as EC2 from "@distilled.cloud/aws/ec2";
5import * as memorydb from "@distilled.cloud/aws/memorydb";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import * as Schedule from "effect/Schedule";
9import { getDefaultVpc } from "../DefaultVpc.ts";
10
11const { test } = Test.make({ providers: AWS.providers() });
12
13// Resolve two default-for-AZ subnets from the account's default VPC.
14const defaultSubnetIds = Effect.gen(function* () {
15 const vpc = yield* getDefaultVpc;
16 const subnets = yield* EC2.describeSubnets({
17 Filters: [
18 { Name: "vpc-id", Values: [vpc.vpcId] },
19 { Name: "default-for-az", Values: ["true"] },
20 ],
21 });
22 const ids = (subnets.Subnets ?? [])
23 .map((s) => s.SubnetId)
24 .filter((id): id is string => id !== undefined)
25 .sort();
26 if (ids.length < 2) {
27 return yield* Effect.die(
28 new Error("default VPC has fewer than 2 default-for-AZ subnets"),
29 );
30 }
31 return ids;
32});
33
34const assertGone = (name: string) =>
35 memorydb.describeSubnetGroups({ SubnetGroupName: name }).pipe(
36 Effect.flatMap(() =>
37 Effect.fail(new Error(`subnet group '${name}' still exists`)),
38 ),
39 Effect.catchTag("SubnetGroupNotFoundFault", () => Effect.void),
40 Effect.retry({
41 schedule: Schedule.max([
42 Schedule.fixed("2 seconds"),
43 Schedule.recurs(10),
44 ]),
45 }),
46 );
47
48test.provider(
49 "create, update description, delete a MemoryDB subnet group",
50 (stack) =>
51 Effect.gen(function* () {
52 yield* stack.destroy();
53
54 const subnetIds = yield* defaultSubnetIds;
55
56 const { group } = yield* stack.deploy(
57 Effect.gen(function* () {
58 const group = yield* SubnetGroup("Subnets", {
59 description: "alchemy memorydb subnet group",
60 subnetIds: subnetIds.slice(0, 2),
61 tags: { fixture: "memorydb-subnet-group" },
62 });
63 return { group };
64 }),
65 );
66
67 expect(group.subnetGroupName).toBeDefined();
68 expect(group.subnetGroupArn).toContain(":subnetgroup/");
69 expect(new Set(group.subnetIds)).toEqual(new Set(subnetIds.slice(0, 2)));
70
71 // Out-of-band verification.
72 const described = yield* memorydb.describeSubnetGroups({
73 SubnetGroupName: group.subnetGroupName,
74 });
75 const observed = described.SubnetGroups?.[0];
76 expect(observed?.Description).toBe("alchemy memorydb subnet group");
77 expect(observed?.VpcId).toBeDefined();
78
79 // Update the description in place (name unchanged).
80 const { group: updated } = yield* stack.deploy(
81 Effect.gen(function* () {
82 const group = yield* SubnetGroup("Subnets", {
83 description: "alchemy memorydb subnet group v2",
84 subnetIds: subnetIds.slice(0, 2),
85 tags: { fixture: "memorydb-subnet-group", env: "test" },
86 });
87 return { group };
88 }),
89 );
90 expect(updated.subnetGroupName).toBe(group.subnetGroupName);
91 expect(updated.description).toBe("alchemy memorydb subnet group v2");
92
93 const redescribed = yield* memorydb.describeSubnetGroups({
94 SubnetGroupName: group.subnetGroupName,
95 });
96 expect(redescribed.SubnetGroups?.[0]?.Description).toBe(
97 "alchemy memorydb subnet group v2",
98 );
99
100 yield* stack.destroy();
101 yield* assertGone(group.subnetGroupName);
102 }),
103 {
104 tags: ["provider:aws", "provider:aws:ec2", "provider:aws:memorydb", "live"],
105 timeout: 240_000,
106 },
107);