1import * as AWS from "@/AWS";
2import { ClusterParameterGroup } from "@/AWS/Redshift";
3import * as Test from "@/Test/Alchemy";
4import * as redshift from "@distilled.cloud/aws/redshift";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7
8const { test } = Test.make({ providers: AWS.providers() });
9
10// Ungated typed-error probe: proves the distilled error union carries the
11// not-found tag this provider's read/delete paths depend on.
12test.provider(
13 "describeClusterParameterGroups on a nonexistent group fails with ClusterParameterGroupNotFoundFault",
14 () =>
15 Effect.gen(function* () {
16 const error = yield* Effect.flip(
17 redshift.describeClusterParameterGroups({
18 ParameterGroupName: "alchemy-nonexistent-redshift-cpg-probe",
19 }),
20 );
21 expect(error._tag).toBe("ClusterParameterGroupNotFoundFault");
22 }),
23 { tags: ["provider:aws", "provider:aws:redshift", "live"] },
24);
25
26// Read the user-sourced parameter overrides out-of-band.
27const readUserParameters = (name: string) =>
28 Effect.gen(function* () {
29 const response = yield* redshift.describeClusterParameters({
30 ParameterGroupName: name,
31 Source: "user",
32 });
33 return Object.fromEntries(
34 (response.Parameters ?? []).flatMap((p) =>
35 p.ParameterName !== undefined && p.ParameterValue !== undefined
36 ? [[p.ParameterName, p.ParameterValue]]
37 : [],
38 ),
39 );
40 });
41
42test.provider(
43 "create with parameters, update/reset parameters, destroy",
44 (stack) =>
45 Effect.gen(function* () {
46 yield* stack.destroy();
47
48 // Create with two parameter overrides.
49 const created = yield* stack.deploy(
50 Effect.gen(function* () {
51 return yield* ClusterParameterGroup("Params", {
52 family: "redshift-1.0",
53 parameters: {
54 enable_user_activity_logging: "true",
55 statement_timeout: "60000",
56 },
57 tags: { fixture: "redshift-parameter-group" },
58 });
59 }),
60 );
61
62 expect(created.clusterParameterGroupName).toBeDefined();
63 expect(created.clusterParameterGroupArn).toContain(":parametergroup:");
64 expect(created.family).toBe("redshift-1.0");
65 expect(created.parameters).toEqual({
66 enable_user_activity_logging: "true",
67 statement_timeout: "60000",
68 });
69
70 // Out-of-band verification via distilled.
71 const observedParams = yield* readUserParameters(
72 created.clusterParameterGroupName,
73 );
74 expect(observedParams).toEqual({
75 enable_user_activity_logging: "true",
76 statement_timeout: "60000",
77 });
78 const observed = yield* redshift.describeClusterParameterGroups({
79 ParameterGroupName: created.clusterParameterGroupName,
80 });
81 expect(
82 observed.ParameterGroups?.[0]?.Tags?.some(
83 (t) => t.Key === "fixture" && t.Value === "redshift-parameter-group",
84 ),
85 ).toBe(true);
86
87 // Update — change one value, drop the other (reset to engine
88 // default), keep the same name/family/description so it's in-place.
89 const updated = yield* stack.deploy(
90 Effect.gen(function* () {
91 return yield* ClusterParameterGroup("Params", {
92 family: "redshift-1.0",
93 parameters: { statement_timeout: "120000" },
94 tags: { fixture: "redshift-parameter-group" },
95 });
96 }),
97 );
98
99 expect(updated.clusterParameterGroupName).toBe(
100 created.clusterParameterGroupName,
101 );
102 expect(updated.parameters).toEqual({ statement_timeout: "120000" });
103
104 const reobservedParams = yield* readUserParameters(
105 created.clusterParameterGroupName,
106 );
107 expect(reobservedParams).toEqual({ statement_timeout: "120000" });
108
109 // Destroy and verify gone with the typed not-found tag.
110 yield* stack.destroy();
111 const error = yield* Effect.flip(
112 redshift.describeClusterParameterGroups({
113 ParameterGroupName: created.clusterParameterGroupName,
114 }),
115 );
116 expect(error._tag).toBe("ClusterParameterGroupNotFoundFault");
117 }),
118 { tags: ["provider:aws", "provider:aws:redshift", "live"], timeout: 120_000 },
119);
120
121test.provider(
122 "changing the family replaces the parameter group",
123 (stack) =>
124 Effect.gen(function* () {
125 yield* stack.destroy();
126
127 // Engine-generated name so the replacement can create the new group
128 // under a fresh physical name before deleting the old one.
129 const created = yield* stack.deploy(
130 Effect.gen(function* () {
131 return yield* ClusterParameterGroup("ReplaceParams", {
132 family: "redshift-1.0",
133 description: "before replacement",
134 });
135 }),
136 );
137 expect(created.description).toBe("before replacement");
138
139 // Description is create-only — changing it must replace.
140 const replaced = yield* stack.deploy(
141 Effect.gen(function* () {
142 return yield* ClusterParameterGroup("ReplaceParams", {
143 family: "redshift-1.0",
144 description: "after replacement",
145 });
146 }),
147 );
148 expect(replaced.description).toBe("after replacement");
149 expect(replaced.clusterParameterGroupName).not.toBe(
150 created.clusterParameterGroupName,
151 );
152
153 // Old group is gone, new group exists.
154 const oldGone = yield* Effect.flip(
155 redshift.describeClusterParameterGroups({
156 ParameterGroupName: created.clusterParameterGroupName,
157 }),
158 );
159 expect(oldGone._tag).toBe("ClusterParameterGroupNotFoundFault");
160 const observed = yield* redshift.describeClusterParameterGroups({
161 ParameterGroupName: replaced.clusterParameterGroupName,
162 });
163 expect(observed.ParameterGroups?.[0]?.Description).toBe(
164 "after replacement",
165 );
166
167 yield* stack.destroy();
168 const error = yield* Effect.flip(
169 redshift.describeClusterParameterGroups({
170 ParameterGroupName: replaced.clusterParameterGroupName,
171 }),
172 );
173 expect(error._tag).toBe("ClusterParameterGroupNotFoundFault");
174 }),
175 { tags: ["provider:aws", "provider:aws:redshift", "live"], timeout: 120_000 },
176);