1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Test from "@/Test/Alchemy";
4import * as rma from "@distilled.cloud/gcp/rapidmigrationassessment_v1";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16// The Rapid Migration Assessment API is disabled on the testing project
17// (every call fails with ServiceDisabled). Set GCP_TEST_RMA=1 on a project
18// with the API enabled.
19const runLifecycle = !!process.env.GCP_TEST_RMA && !process.env.FAST;
20
21const waitUntilGone = (name: string) =>
22 rma.getProjectsLocationsCollectors({ name }).pipe(
23 Effect.as("found" as const),
24 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
25 Effect.repeat({
26 schedule: Schedule.spaced("2 seconds"),
27 until: (status) => status === "gone",
28 times: 10,
29 }),
30 );
31
32test.provider.skipIf(runLifecycle)(
33 "getProjectsLocationsCollectors on a missing collector fails with a typed tag",
34 (stack) =>
35 Effect.gen(function* () {
36 const { project } = yield* GcpEnvironment.current;
37 const parent = `projects/${project}/locations/us-central1`;
38 const missingName = `${parent}/collectors/alchemy-missing-collector`;
39 yield* stack.destroy();
40
41 const error = yield* Effect.flip(
42 rma.getProjectsLocationsCollectors({ name: missingName }),
43 );
44 expect(error._tag).toEqual("ServiceDisabled");
45
46 yield* stack.destroy();
47 }).pipe(logLevel),
48 {
49 tags: ["provider:gcp", "provider:gcp:rapidmigrationassessment", "live"],
50 timeout: 90_000,
51 },
52);
53
54test.provider.skipIf(runLifecycle)(
55 "createProjectsLocationsCollectors is rejected with ServiceDisabled when Rapid Migration Assessment is disabled",
56 (stack) =>
57 Effect.gen(function* () {
58 const { project } = yield* GcpEnvironment.current;
59 const parent = `projects/${project}/locations/us-central1`;
60 const serviceAccount = `alchemy-testing@${project}.iam.gserviceaccount.com`;
61 yield* stack.destroy();
62
63 const error = yield* Effect.flip(
64 rma.createProjectsLocationsCollectors({
65 parent,
66 collectorId: "alchemyrmaprobe",
67 body: {
68 displayName: "alchemy-probe",
69 collectionDays: 7,
70 expectedAssetCount: "1",
71 serviceAccount,
72 },
73 }),
74 );
75 expect(error._tag).toEqual("ServiceDisabled");
76
77 yield* stack.destroy();
78 }).pipe(logLevel),
79 {
80 tags: ["provider:gcp", "provider:gcp:rapidmigrationassessment", "live"],
81 timeout: 90_000,
82 },
83);
84
85test.provider.skipIf(!runLifecycle)(
86 "create, update, and delete a collector",
87 (stack) =>
88 Effect.gen(function* () {
89 const { project } = yield* GcpEnvironment.current;
90 const parent = `projects/${project}/locations/us-central1`;
91 const missingName = `${parent}/collectors/alchemy-missing-collector`;
92 const serviceAccount = `alchemy-testing@${project}.iam.gserviceaccount.com`;
93 yield* stack.destroy();
94
95 const created = yield* stack.deploy(
96 Effect.gen(function* () {
97 return yield* GCP.RapidMigrationAssessment.Collector("OnPrem", {
98 location: "us-central1",
99 displayName: "on-prem collector",
100 description: "inventory appliance",
101 collectionDays: 7,
102 expectedAssetCount: 10,
103 serviceAccount,
104 labels: { env: "test" },
105 });
106 }),
107 );
108
109 expect(created.collectorId).toEqual(expect.any(String));
110 expect(created.name).toEqual(
111 `${parent}/collectors/${created.collectorId}`,
112 );
113 expect(created.project).toEqual(project);
114 expect(created.location).toEqual("us-central1");
115 expect(created.displayName).toEqual("on-prem collector");
116 expect(created.description).toEqual("inventory appliance");
117 expect(created.collectionDays).toEqual(7);
118 expect(created.labels).toMatchObject({ env: "test" });
119
120 const fetched = yield* rma.getProjectsLocationsCollectors({
121 name: created.name,
122 });
123 expect(fetched.name).toEqual(created.name);
124 expect(fetched.displayName).toEqual("on-prem collector");
125 expect(fetched.labels?.env).toEqual("test");
126 expect(fetched.labels?.["alchemy-id"]).toEqual(expect.any(String));
127 expect(fetched.collectionDays).toEqual(7);
128
129 const updated = yield* stack.deploy(
130 Effect.gen(function* () {
131 return yield* GCP.RapidMigrationAssessment.Collector("OnPrem", {
132 collectorId: created.collectorId,
133 location: "us-central1",
134 displayName: "on-prem collector v2",
135 description: "inventory appliance v2",
136 collectionDays: 14,
137 expectedAssetCount: 25,
138 serviceAccount,
139 labels: { env: "prod", role: "rma" },
140 });
141 }),
142 );
143
144 expect(updated.name).toEqual(created.name);
145 expect(updated.collectorId).toEqual(created.collectorId);
146 expect(updated.displayName).toEqual("on-prem collector v2");
147 expect(updated.description).toEqual("inventory appliance v2");
148 expect(updated.collectionDays).toEqual(14);
149 expect(updated.labels).toMatchObject({ env: "prod", role: "rma" });
150
151 const fetchedUpdate = yield* rma.getProjectsLocationsCollectors({
152 name: created.name,
153 });
154 expect(fetchedUpdate.displayName).toEqual("on-prem collector v2");
155 expect(fetchedUpdate.collectionDays).toEqual(14);
156 expect(fetchedUpdate.labels?.env).toEqual("prod");
157 expect(fetchedUpdate.labels?.role).toEqual("rma");
158
159 yield* stack.destroy();
160 const gone = yield* waitUntilGone(created.name);
161 expect(gone).toEqual("gone");
162 }).pipe(logLevel),
163 {
164 tags: ["provider:gcp", "provider:gcp:rapidmigrationassessment", "live"],
165 timeout: 120_000,
166 },
167);