1import * as placementGroups from "@distilled.cloud/hetzner/placement_groups";
2import * as Hetzner from "@/Hetzner";
3import * as Provider from "@/Provider";
4import * as Test from "@/Test/Alchemy";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8
9const { test } = Test.make({ providers: Hetzner.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const hasHetznerCreds = !!process.env.HCLOUD_TOKEN;
17
18const waitUntilGone = Hetzner.waitUntilPlacementGroupGone;
19
20test.provider.skipIf(!hasHetznerCreds)(
21 "create, update, and delete a placement group",
22 (stack) =>
23 Effect.gen(function* () {
24 yield* stack.destroy();
25
26 const created = yield* stack.deploy(
27 Effect.gen(function* () {
28 return yield* Hetzner.PlacementGroup("Web", {
29 type: "spread",
30 labels: { env: "test" },
31 });
32 }),
33 );
34
35 expect(created.id).toEqual(expect.any(Number));
36 expect(created.name).toEqual(expect.any(String));
37 expect(created.type).toEqual("spread");
38 expect(created.created).toEqual(expect.any(String));
39 expect(created.servers).toEqual([]);
40 expect(created.labels).toMatchObject({ env: "test" });
41
42 const fetched = yield* placementGroups.getPlacementGroup({
43 id: created.id,
44 });
45 expect(fetched.placement_group.id).toEqual(created.id);
46 expect(fetched.placement_group.name).toEqual(created.name);
47 expect(fetched.placement_group.type).toEqual("spread");
48 expect(fetched.placement_group.labels.env).toEqual("test");
49 expect(fetched.placement_group.labels["alchemy.id"]).toBeDefined();
50 expect(fetched.placement_group.servers).toEqual([]);
51
52 const updated = yield* stack.deploy(
53 Effect.gen(function* () {
54 return yield* Hetzner.PlacementGroup("Web", {
55 name: `${created.name.slice(0, 55)}-renamed`,
56 type: "spread",
57 labels: { env: "prod", role: "web" },
58 });
59 }),
60 );
61
62 expect(updated.id).toEqual(created.id);
63 expect(updated.type).toEqual("spread");
64 expect(updated.name).toEqual(`${created.name.slice(0, 55)}-renamed`);
65 expect(updated.labels).toMatchObject({ env: "prod", role: "web" });
66
67 const refetched = yield* placementGroups.getPlacementGroup({
68 id: updated.id,
69 });
70 expect(refetched.placement_group.name).toEqual(updated.name);
71 expect(refetched.placement_group.labels.env).toEqual("prod");
72 expect(refetched.placement_group.labels.role).toEqual("web");
73 expect(refetched.placement_group.labels["alchemy.id"]).toBeDefined();
74
75 yield* stack.destroy();
76
77 const gone = yield* waitUntilGone(created.id);
78 expect(gone).toEqual("gone");
79 }).pipe(logLevel),
80 {
81 tags: [
82 "provider:hetzner",
83 "provider:hetzner:placementgroup",
84 "provider:hetzner:service",
85 "live",
86 ],
87 timeout: 120_000,
88 },
89);
90
91test.provider.skipIf(!hasHetznerCreds)(
92 "list enumerates the deployed placement group",
93 (stack) =>
94 Effect.gen(function* () {
95 yield* stack.destroy();
96
97 const deployed = yield* stack.deploy(
98 Effect.gen(function* () {
99 return yield* Hetzner.PlacementGroup("ListGroup", {
100 type: "spread",
101 });
102 }),
103 );
104
105 const provider = yield* Provider.findProvider(Hetzner.PlacementGroup);
106 const all = yield* provider.list();
107 const found = all.find((group) => group.id === deployed.id);
108 expect(found).toBeDefined();
109 expect(found?.name).toEqual(deployed.name);
110 expect(found?.type).toEqual("spread");
111
112 yield* stack.destroy();
113 const gone = yield* waitUntilGone(deployed.id);
114 expect(gone).toEqual("gone");
115 }).pipe(logLevel),
116 {
117 tags: ["provider:hetzner", "provider:hetzner:placementgroup", "live"],
118 timeout: 120_000,
119 },
120);