1import { GcpEnvironment } from "@/GCP/Environment";
2import * as GCP from "@/GCP";
3import * as Test from "@/Test/Alchemy";
4import * as backupdr from "@distilled.cloud/gcp/backupdr_v1";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16const dailyRule = {
17 ruleId: "daily",
18 backupRetentionDays: 1,
19 standardSchedule: {
20 recurrenceType: "DAILY" as const,
21 timeZone: "UTC",
22 backupWindow: { startHourOfDay: 1, endHourOfDay: 5 },
23 },
24};
25
26test.provider(
27 "getProjectsLocationsBackupPlanAssociations on a missing association fails with a typed tag",
28 (stack) =>
29 Effect.gen(function* () {
30 const { project } = yield* GcpEnvironment.current;
31
32 yield* stack.destroy();
33
34 const error = yield* Effect.flip(
35 backupdr.getProjectsLocationsBackupPlanAssociations({
36 name: `projects/${project}/locations/us-central1/backupPlanAssociations/alchemy-backupdr-missing`,
37 }),
38 );
39 expect(error._tag).toEqual("NotFound");
40
41 const page = yield* backupdr.listProjectsLocationsBackupPlanAssociations({
42 parent: `projects/${project}/locations/-`,
43 pageSize: 10,
44 });
45 expect(
46 (page.backupPlanAssociations ?? []).map((item) => item.name),
47 ).not.toContain(
48 `projects/${project}/locations/us-central1/backupPlanAssociations/alchemy-backupdr-missing`,
49 );
50
51 yield* stack.destroy();
52 }).pipe(logLevel),
53 { tags: ["provider:gcp", "provider:gcp:backupdr", "live"], timeout: 90_000 },
54);
55
56test.provider(
57 "create against a missing plan is rejected with a typed tag",
58 (stack) =>
59 Effect.gen(function* () {
60 const { project } = yield* GcpEnvironment.current;
61
62 yield* stack.destroy();
63
64 const error = yield* Effect.flip(
65 stack.deploy(
66 Effect.gen(function* () {
67 return yield* GCP.BackupDR.BackupPlanAssociation("VmPlan", {
68 resource: `projects/${project}/zones/us-central1-a/instances/alchemy-backupdr-missing`,
69 resourceType: "compute.googleapis.com/Instance",
70 backupPlan: `projects/${project}/locations/us-central1/backupPlans/alchemy-backupdr-missing`,
71 });
72 }),
73 ),
74 );
75 expect(error._tag).toEqual("BackupPlanReferenceNotFound");
76
77 yield* stack.destroy();
78 }).pipe(logLevel),
79 { tags: ["provider:gcp", "provider:gcp:backupdr", "live"], timeout: 90_000 },
80);
81
82test.provider(
83 "create against a missing compute instance is rejected with a typed tag",
84 (stack) =>
85 Effect.gen(function* () {
86 const { project } = yield* GcpEnvironment.current;
87
88 yield* stack.destroy();
89
90 const error = yield* Effect.flip(
91 stack.deploy(
92 Effect.gen(function* () {
93 const vault = yield* GCP.BackupDR.BackupVault("Vault", {
94 backupMinimumEnforcedRetentionDuration: "86400s",
95 description: "alchemy-test-bpa-vault",
96 labels: { env: "test" },
97 });
98 const plan = yield* GCP.BackupDR.BackupPlan("Nightly", {
99 backupVault: vault.name,
100 resourceType: "compute.googleapis.com/Instance",
101 backupRules: [dailyRule],
102 description: "alchemy-test-bpa-plan",
103 labels: { env: "test" },
104 });
105 return yield* GCP.BackupDR.BackupPlanAssociation("VmPlan", {
106 resource: `projects/${project}/zones/us-central1-a/instances/alchemy-backupdr-missing`,
107 resourceType: "compute.googleapis.com/Instance",
108 backupPlan: plan.name,
109 });
110 }),
111 ),
112 );
113 expect(error._tag).toEqual("BackupResourceNotFound");
114
115 yield* stack.destroy();
116 }).pipe(logLevel),
117 { tags: ["provider:gcp", "provider:gcp:backupdr", "live"], timeout: 300_000 },
118);