1import * as AWS from "@/AWS";
2import { RetentionConfiguration } from "@/AWS/Config";
3import * as Test from "@/Test/Alchemy";
4import * as config from "@distilled.cloud/aws/config-service";
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: prove the distilled error union carries the
11// not-found tag this provider's read/delete paths depend on.
12test.provider(
13 "describeRetentionConfigurations on a nonexistent name fails with NoSuchRetentionConfigurationException",
14 () =>
15 Effect.gen(function* () {
16 const error = yield* Effect.flip(
17 config.describeRetentionConfigurations({
18 RetentionConfigurationNames: ["alchemy-nonexistent-retention-probe"],
19 }),
20 );
21 expect(error._tag).toBe("NoSuchRetentionConfigurationException");
22 }),
23 { tags: ["provider:aws", "provider:aws:config", "live"] },
24);
25
26const observeRetention = config.describeRetentionConfigurations({}).pipe(
27 Effect.map((r) => (r.RetentionConfigurations ?? []).at(0)),
28 Effect.catchTag("NoSuchRetentionConfigurationException", () =>
29 Effect.succeed(undefined),
30 ),
31);
32
33// The retention configuration is an account-region singleton AWS always
34// names `default` — capture any preexisting configuration up front and
35// restore it after the lifecycle (success, failure, or interruption) so the
36// test never permanently mutates a shared account setting.
37const restorePrior = (prior: config.RetentionConfiguration | undefined) =>
38 (prior === undefined
39 ? Effect.void
40 : config
41 .putRetentionConfiguration({
42 RetentionPeriodInDays: prior.RetentionPeriodInDays,
43 })
44 .pipe(Effect.asVoid)
45 ).pipe(Effect.orDie);
46
47test.provider(
48 "create, update, delete retention configuration (Duration retentionPeriod)",
49 (stack) =>
50 Effect.gen(function* () {
51 yield* stack.destroy();
52 const prior = yield* observeRetention;
53
54 yield* Effect.gen(function* () {
55 // Create — "90 days" exercises the Duration.Input -> wire-days path.
56 const created = yield* stack.deploy(
57 Effect.gen(function* () {
58 return yield* RetentionConfiguration("Retention", {
59 retentionPeriod: "90 days",
60 });
61 }),
62 );
63 expect(created.retentionConfigurationName).toBe("default");
64 expect(created.retentionPeriodInDays).toBe(90);
65
66 // Out-of-band verification via distilled.
67 const observed = yield* observeRetention;
68 expect(observed?.Name).toBe("default");
69 expect(observed?.RetentionPeriodInDays).toBe(90);
70
71 // Update in place (singleton upsert).
72 const updated = yield* stack.deploy(
73 Effect.gen(function* () {
74 return yield* RetentionConfiguration("Retention", {
75 retentionPeriod: "180 days",
76 });
77 }),
78 );
79 expect(updated.retentionConfigurationName).toBe("default");
80 expect(updated.retentionPeriodInDays).toBe(180);
81 const afterUpdate = yield* observeRetention;
82 expect(afterUpdate?.RetentionPeriodInDays).toBe(180);
83
84 // Destroy and verify the singleton is gone.
85 yield* stack.destroy();
86 const afterDestroy = yield* observeRetention;
87 expect(afterDestroy).toBeUndefined();
88 }).pipe(Effect.ensuring(restorePrior(prior)));
89 }),
90 { tags: ["provider:aws", "provider:aws:config", "live"], timeout: 120_000 },
91);