1import * as AWS from "@/AWS";
2import { Application } from "@/AWS/AppIntegrations";
3import * as Test from "@/Test/Alchemy";
4import * as appintegrations from "@distilled.cloud/aws/appintegrations";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: AWS.providers() });
10
11// Ungated typed-error probe: prove the distilled error union carries the
12// not-found tag this provider's read/delete paths depend on.
13test.provider(
14 "getApplication on a nonexistent id fails with ResourceNotFoundException",
15 () =>
16 Effect.gen(function* () {
17 const error = yield* Effect.flip(
18 appintegrations.getApplication({
19 Arn: "00000000-0000-0000-0000-000000000000",
20 }),
21 );
22 expect(error._tag).toBe("ResourceNotFoundException");
23 }),
24 { tags: ["provider:aws", "provider:aws:appintegrations", "live"] },
25);
26
27const assertGone = (arn: string) =>
28 appintegrations.getApplication({ Arn: arn }).pipe(
29 Effect.flatMap(() =>
30 Effect.fail(new Error(`application '${arn}' still exists`)),
31 ),
32 Effect.catchTag("ResourceNotFoundException", () => Effect.void),
33 Effect.retry({
34 schedule: Schedule.max([
35 Schedule.fixed("2 seconds"),
36 Schedule.recurs(10),
37 ]),
38 }),
39 );
40
41test.provider(
42 "create, update in place, replace on namespace change, delete an application",
43 (stack) =>
44 Effect.gen(function* () {
45 yield* stack.destroy();
46
47 const { app } = yield* stack.deploy(
48 Effect.gen(function* () {
49 const app = yield* Application("AgentApp", {
50 namespace: "com.alchemy.testapp",
51 accessUrl: "https://example.com",
52 description: "alchemy application",
53 tags: { purpose: "alchemy-test" },
54 });
55 return { app };
56 }),
57 );
58
59 expect(app.applicationId).toBeDefined();
60 expect(app.applicationArn).toContain(":application/");
61 expect(app.applicationName).toBeDefined();
62 expect(app.namespace).toBe("com.alchemy.testapp");
63
64 // Out-of-band verification via distilled.
65 const observed = yield* appintegrations.getApplication({
66 Arn: app.applicationArn,
67 });
68 expect(observed.Name).toBe(app.applicationName);
69 expect(observed.Namespace).toBe("com.alchemy.testapp");
70 expect(observed.Description).toBe("alchemy application");
71 expect(
72 observed.ApplicationSourceConfig?.ExternalUrlConfig?.AccessUrl,
73 ).toBe("https://example.com");
74 expect(observed.Tags?.purpose).toBe("alchemy-test");
75
76 // Update description, access URL, and permissions in place (arn/id
77 // stable).
78 const { app: updated } = yield* stack.deploy(
79 Effect.gen(function* () {
80 const app = yield* Application("AgentApp", {
81 namespace: "com.alchemy.testapp",
82 accessUrl: "https://updated.example.com",
83 description: "alchemy application v2",
84 permissions: ["User.Details.View"],
85 tags: { purpose: "alchemy-test", phase: "two" },
86 });
87 return { app };
88 }),
89 );
90 expect(updated.applicationArn).toBe(app.applicationArn);
91 expect(updated.applicationId).toBe(app.applicationId);
92
93 const reobserved = yield* appintegrations.getApplication({
94 Arn: app.applicationArn,
95 });
96 expect(reobserved.Description).toBe("alchemy application v2");
97 expect(
98 reobserved.ApplicationSourceConfig?.ExternalUrlConfig?.AccessUrl,
99 ).toBe("https://updated.example.com");
100 expect(reobserved.Permissions).toEqual(["User.Details.View"]);
101 expect(reobserved.Tags?.phase).toBe("two");
102
103 // Changing the namespace replaces the application.
104 const { app: replaced } = yield* stack.deploy(
105 Effect.gen(function* () {
106 const app = yield* Application("AgentApp", {
107 namespace: "com.alchemy.testapp.v2",
108 accessUrl: "https://updated.example.com",
109 description: "alchemy application v2",
110 });
111 return { app };
112 }),
113 );
114 expect(replaced.namespace).toBe("com.alchemy.testapp.v2");
115 expect(replaced.applicationArn).not.toBe(app.applicationArn);
116
117 // The replaced (old) application is deleted.
118 yield* assertGone(app.applicationArn);
119
120 yield* stack.destroy();
121 yield* assertGone(replaced.applicationArn);
122 }),
123 {
124 tags: ["provider:aws", "provider:aws:appintegrations", "live"],
125 timeout: 240_000,
126 },
127);