1import * as AWS from "@/AWS";
2import { DataIntegration } from "@/AWS/AppIntegrations";
3import { Key } from "@/AWS/KMS";
4import { Bucket } from "@/AWS/S3";
5import * as Output from "@/Output";
6import * as Test from "@/Test/Alchemy";
7import * as appintegrations from "@distilled.cloud/aws/appintegrations";
8import { expect } from "alchemy-test";
9import * as Effect from "effect/Effect";
10import * as Schedule from "effect/Schedule";
11
12const { test } = Test.make({ providers: AWS.providers() });
13
14// Ungated typed-error probe: prove the distilled error union carries the
15// not-found tag this provider's read/delete paths depend on.
16test.provider(
17 "getDataIntegration on a nonexistent id fails with ResourceNotFoundException",
18 () =>
19 Effect.gen(function* () {
20 const error = yield* Effect.flip(
21 appintegrations.getDataIntegration({
22 Identifier: "00000000-0000-0000-0000-000000000000",
23 }),
24 );
25 expect(error._tag).toBe("ResourceNotFoundException");
26 }),
27 { tags: ["provider:aws", "provider:aws:appintegrations", "live"] },
28);
29
30const assertGone = (id: string) =>
31 appintegrations.getDataIntegration({ Identifier: id }).pipe(
32 Effect.flatMap(() =>
33 Effect.fail(new Error(`data integration '${id}' still exists`)),
34 ),
35 Effect.catchTag("ResourceNotFoundException", () => Effect.void),
36 Effect.retry({
37 schedule: Schedule.max([
38 Schedule.fixed("2 seconds"),
39 Schedule.recurs(10),
40 ]),
41 }),
42 );
43
44test.provider(
45 "create an S3 data integration, update description, delete",
46 (stack) =>
47 Effect.gen(function* () {
48 yield* stack.destroy();
49
50 const { integration } = yield* stack.deploy(
51 Effect.gen(function* () {
52 const bucket = yield* Bucket("Content", { forceDestroy: true });
53 const key = yield* Key("ContentKey", {
54 description: "alchemy appintegrations test key",
55 deletionWindow: "7 days",
56 });
57 const integration = yield* DataIntegration("ContentIntegration", {
58 kmsKey: key.keyArn,
59 sourceURI: Output.interpolate`s3://${bucket.bucketName}`,
60 description: "alchemy data integration",
61 tags: { purpose: "alchemy-test" },
62 });
63 return { integration };
64 }),
65 );
66
67 expect(integration.dataIntegrationId).toBeDefined();
68 expect(integration.dataIntegrationArn).toContain(":data-integration/");
69 expect(integration.sourceURI).toMatch(/^s3:\/\//);
70
71 // Out-of-band verification via distilled.
72 const observed = yield* appintegrations.getDataIntegration({
73 Identifier: integration.dataIntegrationId,
74 });
75 expect(observed.Name).toBe(integration.dataIntegrationName);
76 expect(observed.Description).toBe("alchemy data integration");
77 expect(observed.SourceURI).toBe(integration.sourceURI);
78 expect(observed.KmsKey).toBe(integration.kmsKey);
79 expect(observed.Tags?.purpose).toBe("alchemy-test");
80
81 // Update the description in place (id/arn stable).
82 const { integration: updated } = yield* stack.deploy(
83 Effect.gen(function* () {
84 const bucket = yield* Bucket("Content", { forceDestroy: true });
85 const key = yield* Key("ContentKey", {
86 description: "alchemy appintegrations test key",
87 deletionWindow: "7 days",
88 });
89 const integration = yield* DataIntegration("ContentIntegration", {
90 kmsKey: key.keyArn,
91 sourceURI: Output.interpolate`s3://${bucket.bucketName}`,
92 description: "alchemy data integration v2",
93 tags: { purpose: "alchemy-test", phase: "two" },
94 });
95 return { integration };
96 }),
97 );
98 expect(updated.dataIntegrationId).toBe(integration.dataIntegrationId);
99 expect(updated.dataIntegrationArn).toBe(integration.dataIntegrationArn);
100
101 const reobserved = yield* appintegrations.getDataIntegration({
102 Identifier: integration.dataIntegrationId,
103 });
104 expect(reobserved.Description).toBe("alchemy data integration v2");
105 expect(reobserved.Tags?.phase).toBe("two");
106
107 yield* stack.destroy();
108 yield* assertGone(integration.dataIntegrationId);
109 }),
110 {
111 tags: [
112 "provider:aws",
113 "provider:aws:appintegrations",
114 "provider:aws:kms",
115 "provider:aws:s3",
116 "live",
117 ],
118 timeout: 240_000,
119 },
120);