1import * as AWS from "@/AWS";
2import { Runtime } from "@/AWS/BedrockAgentCore";
3import { Role } from "@/AWS/IAM/Role.ts";
4import * as Test from "@/Test/Alchemy";
5import * as control from "@distilled.cloud/aws/bedrock-agentcore-control";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: AWS.providers() });
11
12// Ungated typed-error probe: prove the distilled error union carries the
13// not-found tag this provider's read/delete paths depend on.
14test.provider(
15 "getAgentRuntime on a nonexistent id fails with ResourceNotFoundException",
16 () =>
17 Effect.gen(function* () {
18 const error = yield* Effect.flip(
19 control.getAgentRuntime({
20 agentRuntimeId: "alchemy_nonexistent_probe-0000000000",
21 }),
22 );
23 expect(error._tag).toBe("ResourceNotFoundException");
24 }),
25 { tags: ["provider:aws", "provider:aws:bedrockagentcore", "live"] },
26);
27
28const assertRuntimeGone = (agentRuntimeId: string) =>
29 Effect.gen(function* () {
30 const status = yield* control.getAgentRuntime({ agentRuntimeId }).pipe(
31 Effect.map((r) => r.status as string),
32 Effect.catchTag("ResourceNotFoundException", () =>
33 Effect.succeed("GONE" as string),
34 ),
35 );
36 if (status !== "GONE") {
37 return yield* Effect.fail(
38 new Error(`runtime still exists (status: ${status})`),
39 );
40 }
41 }).pipe(
42 Effect.retry({
43 schedule: Schedule.max([
44 Schedule.fixed("5 seconds"),
45 Schedule.recurs(18),
46 ]),
47 }),
48 );
49
50// The full lifecycle needs a deployable agent container image in ECR —
51// something a CI account cannot fabricate cheaply. Gate behind
52// AWS_TEST_AGENTCORE=1 and provide the image via AWS_TEST_AGENTCORE_IMAGE
53// (an ECR image URI in the same account/region, e.g. built from the
54// AgentCore starter toolkit).
55test.provider.skipIf(
56 !process.env.AWS_TEST_AGENTCORE || !process.env.AWS_TEST_AGENTCORE_IMAGE,
57)(
58 "create container-backed agent runtime, verify, destroy",
59 (stack) =>
60 Effect.gen(function* () {
61 yield* stack.destroy();
62
63 const { runtime } = yield* stack.deploy(
64 Effect.gen(function* () {
65 const role = yield* Role("RuntimeRole", {
66 assumeRolePolicyDocument: {
67 Version: "2012-10-17",
68 Statement: [
69 {
70 Effect: "Allow",
71 Principal: { Service: "bedrock-agentcore.amazonaws.com" },
72 Action: ["sts:AssumeRole"],
73 },
74 ],
75 },
76 managedPolicyArns: [
77 "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
78 ],
79 });
80 const runtime = yield* Runtime("TestAgent", {
81 agentRuntimeArtifact: {
82 containerConfiguration: {
83 containerUri: process.env.AWS_TEST_AGENTCORE_IMAGE!,
84 },
85 },
86 roleArn: role.roleArn,
87 tags: { fixture: "agentcore-runtime" },
88 });
89 return { runtime };
90 }),
91 );
92
93 expect(runtime.agentRuntimeId).toBeTruthy();
94 expect(runtime.agentRuntimeArn).toContain(":runtime/");
95 expect(runtime.status).toBe("READY");
96 expect(runtime.agentRuntimeVersion).toBeTruthy();
97
98 // out-of-band verification via distilled
99 const observed = yield* control.getAgentRuntime({
100 agentRuntimeId: runtime.agentRuntimeId,
101 });
102 expect(observed.status).toBe("READY");
103
104 yield* stack.destroy();
105 yield* assertRuntimeGone(runtime.agentRuntimeId);
106 }),
107 {
108 tags: [
109 "provider:aws",
110 "provider:aws:bedrockagentcore",
111 "provider:aws:iam",
112 "live",
113 ],
114 timeout: 600_000,
115 },
116);