1import * as AWS from "@/AWS";
2import { Function } from "@/AWS/CloudFront";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import * as cloudfront from "@distilled.cloud/aws/cloudfront";
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
12test.provider(
13 "create and delete a CloudFront Function with key value store associations",
14 (stack) =>
15 Effect.gen(function* () {
16 yield* stack.destroy();
17
18 const deployed = yield* stack.deploy(
19 Effect.gen(function* () {
20 const store = yield* AWS.CloudFront.KeyValueStore("RequestStore", {
21 comment: "request metadata",
22 });
23 const fn = yield* AWS.CloudFront.Function("RequestFn", {
24 comment: "request handler",
25 keyValueStoreArns: [store.keyValueStoreArn],
26 code: `async function handler(event) {
27 return event.request;
28}`,
29 });
30 return { store, fn };
31 }),
32 );
33
34 const current = yield* cloudfront.describeFunction({
35 Name: deployed.fn.functionName,
36 Stage: "LIVE",
37 });
38 expect(current.FunctionSummary?.Name).toEqual(deployed.fn.functionName);
39 expect(
40 current.FunctionSummary?.FunctionConfig.KeyValueStoreAssociations
41 ?.Items?.[0]?.KeyValueStoreARN,
42 ).toEqual(deployed.store.keyValueStoreArn);
43
44 yield* stack.destroy();
45 yield* assertFunctionDeleted(deployed.fn.functionName);
46 }),
47 {
48 tags: ["provider:aws", "provider:aws:cloudfront", "live"],
49 timeout: 300_000,
50 },
51);
52
53// `FunctionConfig.Comment` is patched optional in
54// submodules/distilled/packages/aws/patches/cloudfront.json — CloudFront omits `Comment`
55// in `listFunctions` responses for functions created without one, and a
56// comment-less function anywhere in the account used to fail the decode with
57// `SchemaError: Missing key ... FunctionConfig.Comment`.
58test.provider(
59 "list enumerates the deployed CloudFront Function",
60 (stack) =>
61 Effect.gen(function* () {
62 yield* stack.destroy();
63
64 const deployed = yield* stack.deploy(
65 Effect.gen(function* () {
66 return yield* AWS.CloudFront.Function("ListFn", {
67 comment: "list handler",
68 code: `async function handler(event) {
69 return event.request;
70}`,
71 });
72 }),
73 );
74
75 const provider = yield* Provider.findProvider(Function);
76 const all = yield* provider.list();
77
78 expect(all.some((fn) => fn.functionName === deployed.functionName)).toBe(
79 true,
80 );
81
82 yield* stack.destroy();
83 yield* assertFunctionDeleted(deployed.functionName);
84 }),
85 {
86 tags: ["provider:aws", "provider:aws:cloudfront", "live"],
87 timeout: 300_000,
88 },
89);
90
91const assertFunctionDeleted = (name: string) =>
92 cloudfront
93 .describeFunction({
94 Name: name,
95 Stage: "LIVE",
96 })
97 .pipe(
98 Effect.flatMap(() => Effect.fail(new Error("FunctionStillExists"))),
99 Effect.catchTag("NoSuchFunctionExists", () => Effect.void),
100 Effect.retry({
101 while: (error) =>
102 error instanceof Error && error.message === "FunctionStillExists",
103 schedule: Schedule.max([
104 Schedule.fixed("5 seconds"),
105 Schedule.recurs(24),
106 ]),
107 }),
108 );