1import * as AWS from "@/AWS";
2import { Thing } from "@/AWS/IoT";
3import * as Test from "@/Test/Alchemy";
4import * as iot from "@distilled.cloud/aws/iot";
5import { describe, 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
11const assertThingGone = (thingName: string) =>
12 iot.describeThing({ thingName }).pipe(
13 Effect.flatMap(() =>
14 Effect.fail(new Error(`thing ${thingName} still exists`)),
15 ),
16 Effect.catchTag("ResourceNotFoundException", () => Effect.void),
17 Effect.retry({
18 while: (e) => e instanceof Error,
19 schedule: Schedule.max([
20 Schedule.fixed("2 seconds"),
21 Schedule.recurs(10),
22 ]),
23 }),
24 );
25
26describe.sequential(
27 "AWS.IoT.Thing",
28 { tags: ["provider:aws", "provider:aws:iot", "live"] },
29 () => {
30 test.provider(
31 "creates a thing with attributes, updates attributes, and deletes it",
32 (stack) =>
33 Effect.gen(function* () {
34 yield* stack.destroy();
35
36 const created = yield* stack.deploy(
37 Effect.gen(function* () {
38 const thing = yield* Thing("Sensor", {
39 attributes: { location: "warehouse-a" },
40 });
41 return { thingName: thing.thingName };
42 }),
43 );
44
45 const observed = yield* iot.describeThing({
46 thingName: created.thingName,
47 });
48 expect(observed.attributes?.location).toEqual("warehouse-a");
49
50 // Update attributes in place.
51 yield* stack.deploy(
52 Effect.gen(function* () {
53 yield* Thing("Sensor", {
54 attributes: { location: "warehouse-b", model: "t1000" },
55 });
56 }),
57 );
58 const updated = yield* iot.describeThing({
59 thingName: created.thingName,
60 });
61 expect(updated.attributes?.location).toEqual("warehouse-b");
62 expect(updated.attributes?.model).toEqual("t1000");
63
64 yield* stack.destroy();
65 yield* assertThingGone(created.thingName);
66 }),
67 { timeout: 180_000 },
68 );
69 },
70);