1import * as AWS from "@/AWS";
2import * as Test from "@/Test/Alchemy";
3import * as ElastiCache from "@distilled.cloud/aws/elasticache";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 assertReplicationGroupGone,
9 getProvisionedNetwork,
10 shareProvisionedNetwork,
11} from "./ProvisionedFixture.ts";
12const { test, beforeAll, afterAll } = Test.make({
13 providers: AWS.providers(),
14});
15shareProvisionedNetwork({ beforeAll, afterAll });
16const snapshotName = "alchemy-elasticache-provisioned-restore";
17
18const deleteSnapshot = () =>
19 ElastiCache.deleteSnapshot({ SnapshotName: snapshotName }).pipe(
20 Effect.catchTag("SnapshotNotFoundFault", () => Effect.void),
21 Effect.retry({
22 while: (error) => error._tag === "InvalidSnapshotStateFault",
23 schedule: Schedule.max([
24 Schedule.fixed("15 seconds"),
25 Schedule.recurs(12),
26 ]),
27 }),
28 );
29
30const waitForSnapshot = () =>
31 ElastiCache.describeSnapshots({ SnapshotName: snapshotName }).pipe(
32 Effect.flatMap((response) =>
33 response.Snapshots?.[0]?.SnapshotStatus === "available"
34 ? Effect.void
35 : Effect.fail(new Error(`snapshot '${snapshotName}' is not available`)),
36 ),
37 Effect.retry({
38 schedule: Schedule.max([
39 Schedule.fixed("30 seconds"),
40 Schedule.recurs(30),
41 ]),
42 }),
43 );
44
45test.provider.skipIf(!process.env.AWS_TEST_SLOW)(
46 "restores a new Valkey replication group from a final snapshot",
47 (stack) =>
48 Effect.gen(function* () {
49 yield* stack.destroy();
50 yield* deleteSnapshot();
51 const deploy = (
52 snapshotNameToRestore?: string,
53 finalSnapshotName?: string,
54 ) =>
55 stack.deploy(
56 Effect.gen(function* () {
57 const net = yield* getProvisionedNetwork;
58 const cache = yield* AWS.ElastiCache.ReplicationGroup("Cache", {
59 description: "alchemy snapshot restore cache",
60 engine: "valkey",
61 nodeType: "cache.t4g.micro",
62 subnetGroupName: net.subnetGroupName,
63 securityGroupIds: [net.securityGroupId],
64 replicasPerNodeGroup: 0,
65 transitEncryptionEnabled: true,
66 snapshotName: snapshotNameToRestore,
67 finalSnapshotName,
68 });
69 return { cache };
70 }),
71 );
72
73 const source = yield* deploy(undefined, snapshotName);
74 yield* stack.destroy();
75 yield* assertReplicationGroupGone(source.cache.replicationGroupId);
76 yield* waitForSnapshot();
77
78 const restored = yield* deploy(snapshotName);
79 const observed = yield* ElastiCache.describeReplicationGroups({
80 ReplicationGroupId: restored.cache.replicationGroupId,
81 });
82 expect(observed.ReplicationGroups?.[0]?.Status).toBe("available");
83 expect(observed.ReplicationGroups?.[0]?.Engine).toBe("valkey");
84
85 yield* stack.destroy();
86 yield* assertReplicationGroupGone(restored.cache.replicationGroupId);
87 yield* deleteSnapshot();
88 }).pipe(Effect.ensuring(deleteSnapshot().pipe(Effect.ignore))),
89 {
90 tags: [
91 "provider:aws",
92 "provider:aws:ec2",
93 "provider:aws:elasticache",
94 "live",
95 ],
96 timeout: 2_700_000,
97 },
98);