1import * as AWS from "@/AWS";
2import { PlaybackRestrictionPolicy } from "@/AWS/IVS";
3import * as Test from "@/Test/Alchemy";
4import * as ivs from "@distilled.cloud/aws/ivs";
5import * as sts from "@distilled.cloud/aws/sts";
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: proves the distilled error union carries the
13// not-found tag this provider's read/delete paths depend on.
14test.provider(
15 "getPlaybackRestrictionPolicy on a nonexistent ARN fails with ResourceNotFoundException",
16 () =>
17 Effect.gen(function* () {
18 const region = yield* yield* AWS.Region;
19 const { Account } = yield* sts.getCallerIdentity({});
20 const error = yield* Effect.flip(
21 ivs.getPlaybackRestrictionPolicy({
22 arn: `arn:aws:ivs:${region}:${Account}:playback-restriction-policy/AbCdEfGh1234`,
23 }),
24 );
25 expect(error._tag).toBe("ResourceNotFoundException");
26 }),
27 { tags: ["provider:aws", "provider:aws:ivs", "live"], timeout: 60_000 },
28);
29
30const assertPolicyGone = (arn: string) =>
31 Effect.gen(function* () {
32 const policy = yield* ivs.getPlaybackRestrictionPolicy({ arn }).pipe(
33 Effect.map((r) => r.playbackRestrictionPolicy),
34 Effect.catchTag("ResourceNotFoundException", () =>
35 Effect.succeed(undefined),
36 ),
37 );
38 if (policy !== undefined) {
39 return yield* Effect.fail(new Error(`policy '${arn}' still exists`));
40 }
41 }).pipe(
42 Effect.retry({
43 schedule: Schedule.max([Schedule.fixed("3 seconds"), Schedule.recurs(8)]),
44 }),
45 );
46
47// Playback restriction policies are free and provision synchronously — the
48// full lifecycle (create, no-op, update-in-place, destroy) runs ungated.
49test.provider(
50 "create, update, and destroy an IVS playback restriction policy",
51 (stack) =>
52 Effect.gen(function* () {
53 // Clean slate in case a previous run died mid-flight.
54 yield* stack.destroy();
55
56 const props = {
57 playbackRestrictionPolicyName: "alchemy-test-ivs-prp",
58 allowedCountries: ["US", "CA"],
59 allowedOrigins: ["https://example.com"],
60 tags: { fixture: "ivs-prp" },
61 };
62
63 // Create.
64 const created = yield* stack.deploy(
65 PlaybackRestrictionPolicy("GeoFence", props),
66 );
67 expect(created.playbackRestrictionPolicyArn).toContain(
68 ":playback-restriction-policy/",
69 );
70 expect(created.playbackRestrictionPolicyName).toBe(
71 "alchemy-test-ivs-prp",
72 );
73 expect([...created.allowedCountries].sort()).toEqual(["CA", "US"]);
74 expect(created.allowedOrigins).toEqual(["https://example.com"]);
75
76 // Out-of-band verification via distilled.
77 const observed = yield* ivs.getPlaybackRestrictionPolicy({
78 arn: created.playbackRestrictionPolicyArn,
79 });
80 expect(observed.playbackRestrictionPolicy?.name).toBe(
81 "alchemy-test-ivs-prp",
82 );
83 expect(observed.playbackRestrictionPolicy?.tags?.fixture).toBe("ivs-prp");
84 expect(observed.playbackRestrictionPolicy?.tags?.["alchemy::id"]).toBe(
85 "GeoFence",
86 );
87
88 // No-op redeploy keeps the same policy.
89 const noop = yield* stack.deploy(
90 PlaybackRestrictionPolicy("GeoFence", props),
91 );
92 expect(noop.playbackRestrictionPolicyArn).toBe(
93 created.playbackRestrictionPolicyArn,
94 );
95
96 // Update in place — countries, origins, and strict enforcement are
97 // all mutable; the ARN must not change.
98 const updated = yield* stack.deploy(
99 PlaybackRestrictionPolicy("GeoFence", {
100 ...props,
101 allowedCountries: ["US"],
102 allowedOrigins: ["https://example.com", "https://example.org"],
103 enableStrictOriginEnforcement: true,
104 }),
105 );
106 expect(updated.playbackRestrictionPolicyArn).toBe(
107 created.playbackRestrictionPolicyArn,
108 );
109 expect(updated.allowedCountries).toEqual(["US"]);
110 expect([...updated.allowedOrigins].sort()).toEqual([
111 "https://example.com",
112 "https://example.org",
113 ]);
114 expect(updated.enableStrictOriginEnforcement).toBe(true);
115
116 const reobserved = yield* ivs.getPlaybackRestrictionPolicy({
117 arn: created.playbackRestrictionPolicyArn,
118 });
119 expect(
120 reobserved.playbackRestrictionPolicy?.enableStrictOriginEnforcement,
121 ).toBe(true);
122 expect(reobserved.playbackRestrictionPolicy?.allowedCountries).toEqual([
123 "US",
124 ]);
125
126 // Destroy and verify out-of-band with a typed wait-until-gone.
127 yield* stack.destroy();
128 yield* assertPolicyGone(created.playbackRestrictionPolicyArn);
129 }),
130 { tags: ["provider:aws", "provider:aws:ivs", "live"], timeout: 240_000 },
131);