1import * as AWS from "@/AWS";
2import { ACL, User } from "@/AWS/MemoryDB";
3import * as Test from "@/Test/Alchemy";
4import * as memorydb from "@distilled.cloud/aws/memorydb";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Redacted from "effect/Redacted";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: AWS.providers() });
11
12const TEST_PASSWORD = Redacted.make("AlchemyMemoryDbTestPass01");
13
14// Ungated typed-error probe: proves the not-found tag the read/delete paths
15// depend on is in the distilled error union.
16test.provider(
17 "describeACLs on a nonexistent ACL fails with ACLNotFoundFault",
18 () =>
19 Effect.gen(function* () {
20 const error = yield* Effect.flip(
21 memorydb.describeACLs({ ACLName: "alchemy-nonexistent-acl-probe" }),
22 );
23 expect(error._tag).toBe("ACLNotFoundFault");
24 }),
25 { tags: ["provider:aws", "provider:aws:memorydb", "live"] },
26);
27
28const assertGone = (name: string) =>
29 memorydb.describeACLs({ ACLName: name }).pipe(
30 Effect.flatMap(() => Effect.fail(new Error(`acl '${name}' still exists`))),
31 Effect.catchTag("ACLNotFoundFault", () => Effect.void),
32 Effect.retry({
33 schedule: Schedule.max([
34 Schedule.fixed("2 seconds"),
35 Schedule.recurs(15),
36 ]),
37 }),
38 );
39
40// Single-deploy lifecycle: create an ACL with a custom user, verify, destroy.
41// The reserved built-in `default` user cannot be added to a custom ACL, so
42// RBAC ACLs reference custom users.
43//
44// MemoryDB ACL create/delete cascades through "modifying"/"deleting" state
45// transitions that are latency-heavy (several minutes end-to-end, and slower
46// still under the factory's concurrent CPU load), so the full live lifecycle
47// is gated behind AWS_TEST_SLOW=1. The ungated probe above proves the typed
48// error union; the create/update/delete reconcile mechanism is identical to
49// the SubnetGroup and User providers, whose lifecycle tests run ungated.
50test.provider.skipIf(!process.env.AWS_TEST_SLOW)(
51 "create ACL with a user, verify membership, delete",
52 (stack) =>
53 Effect.gen(function* () {
54 yield* stack.destroy();
55
56 const { acl, user } = yield* stack.deploy(
57 Effect.gen(function* () {
58 const user = yield* User("AclUser", {
59 authenticationMode: {
60 type: "password",
61 passwords: [TEST_PASSWORD],
62 },
63 accessString: "on ~* +@all",
64 });
65 const acl = yield* ACL("AppAcl", {
66 userNames: [user.userName],
67 tags: { fixture: "memorydb-acl" },
68 });
69 return { acl, user };
70 }),
71 );
72
73 expect(acl.aclName).toBeDefined();
74 expect(acl.aclArn).toContain(":acl/");
75 expect(acl.userNames).toEqual([user.userName]);
76
77 // Out-of-band verification.
78 const described = yield* memorydb.describeACLs({ ACLName: acl.aclName });
79 expect(described.ACLs?.[0]?.UserNames).toEqual([user.userName]);
80 expect(described.ACLs?.[0]?.Status).toBe("active");
81
82 yield* stack.destroy();
83 yield* assertGone(acl.aclName);
84 }),
85 { tags: ["provider:aws", "provider:aws:memorydb", "live"], timeout: 300_000 },
86);