1import * as AWS from "@/AWS";
2import { Domain } from "@/AWS/SimpleDB";
3import * as Test from "@/Test/Alchemy";
4import * as sdb from "@distilled.cloud/aws/simpledb";
5import { expect } from "alchemy-test";
6import * as Data from "effect/Data";
7import * as Effect from "effect/Effect";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: AWS.providers() });
11
12const findDomain = (domainName: string) =>
13 sdb
14 .domainMetadata({ DomainName: domainName })
15 .pipe(Effect.catchTag("NoSuchDomain", () => Effect.succeed(undefined)));
16
17class DomainStillExists extends Data.TaggedError("DomainStillExists")<{
18 readonly domainName: string;
19}> {}
20
21const assertDomainDeleted = (domainName: string) =>
22 findDomain(domainName).pipe(
23 Effect.flatMap((metadata) =>
24 metadata === undefined
25 ? Effect.void
26 : Effect.fail(new DomainStillExists({ domainName })),
27 ),
28 Effect.retry({
29 while: (e) => e._tag === "DomainStillExists",
30 schedule: Schedule.max([
31 Schedule.spaced("2 seconds"),
32 Schedule.recurs(10),
33 ]),
34 }),
35 );
36
37test.provider(
38 "create and delete a domain (generated name)",
39 (stack) =>
40 Effect.gen(function* () {
41 yield* stack.destroy();
42
43 const domain = yield* stack.deploy(
44 Effect.gen(function* () {
45 return yield* Domain("TestSdbDomain", {});
46 }),
47 );
48
49 expect(domain.domainName).toBeDefined();
50 expect(domain.domainArn).toContain(":domain/");
51 expect(domain.domainArn).toContain(domain.domainName);
52
53 // out-of-band verification via distilled
54 const metadata = yield* findDomain(domain.domainName);
55 expect(metadata).toBeDefined();
56 expect(metadata?.ItemCount).toBe(0);
57
58 // idempotent re-deploy: same physical domain
59 const again = yield* stack.deploy(
60 Effect.gen(function* () {
61 return yield* Domain("TestSdbDomain", {});
62 }),
63 );
64 expect(again.domainName).toBe(domain.domainName);
65 expect(again.domainArn).toBe(domain.domainArn);
66
67 yield* stack.destroy();
68 yield* assertDomainDeleted(domain.domainName);
69 }),
70 { tags: ["provider:aws", "provider:aws:simpledb", "live"], timeout: 120_000 },
71);
72
73test.provider(
74 "explicit name and replacement on rename",
75 (stack) =>
76 Effect.gen(function* () {
77 yield* stack.destroy();
78
79 const first = yield* stack.deploy(
80 Effect.gen(function* () {
81 return yield* Domain("NamedSdbDomain", {
82 domainName: "alchemy-test-simpledb-a",
83 });
84 }),
85 );
86 expect(first.domainName).toBe("alchemy-test-simpledb-a");
87
88 // renaming triggers a replacement: new physical domain, old gone
89 const second = yield* stack.deploy(
90 Effect.gen(function* () {
91 return yield* Domain("NamedSdbDomain", {
92 domainName: "alchemy-test-simpledb-b",
93 });
94 }),
95 );
96 expect(second.domainName).toBe("alchemy-test-simpledb-b");
97 expect(second.domainArn).not.toBe(first.domainArn);
98
99 yield* assertDomainDeleted(first.domainName);
100
101 yield* stack.destroy();
102 yield* assertDomainDeleted(second.domainName);
103 }),
104 { tags: ["provider:aws", "provider:aws:simpledb", "live"], timeout: 120_000 },
105);
106
107test.provider(
108 "domainMetadata on a missing domain fails with typed NoSuchDomain",
109 () =>
110 Effect.gen(function* () {
111 const error = yield* sdb
112 .domainMetadata({ DomainName: "alchemy-test-simpledb-bogus" })
113 .pipe(Effect.flip);
114 expect(error._tag).toBe("NoSuchDomain");
115 }),
116 { tags: ["provider:aws", "provider:aws:simpledb", "live"], timeout: 60_000 },
117);