1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as ssm from "@distilled.cloud/gcp/securesourcemanager_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 currentProject,
9 logLevel,
10 missingRepoOf,
11 runLifecycle,
12} from "./common.ts";
13
14const { test } = Test.make({ providers: GCP.providers() });
15
16const waitUntilGone = (name: string) =>
17 ssm.getProjectsLocationsRepositoriesBranchRules({ name }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("2 seconds"),
22 until: (status) => status === "gone",
23 times: 10,
24 }),
25 );
26
27test.provider(
28 "getProjectsLocationsRepositoriesBranchRules on a missing rule fails with a typed tag",
29 (stack) =>
30 Effect.gen(function* () {
31 const project = yield* currentProject;
32 const missingRepo = missingRepoOf(project);
33 yield* stack.destroy();
34
35 const error = yield* Effect.flip(
36 ssm.getProjectsLocationsRepositoriesBranchRules({
37 name: `${missingRepo}/branchRules/alchemy-missing-rule`,
38 }),
39 );
40 expect(error._tag).toEqual("NotFound");
41
42 yield* stack.destroy();
43 }).pipe(logLevel),
44 {
45 tags: ["provider:gcp", "provider:gcp:securesourcemanager", "live"],
46 timeout: 90_000,
47 },
48);
49
50test.provider(
51 "create against a missing repository is rejected with a typed tag",
52 (stack) =>
53 Effect.gen(function* () {
54 const project = yield* currentProject;
55 const missingRepo = missingRepoOf(project);
56 yield* stack.destroy();
57
58 const error = yield* Effect.flip(
59 stack.deploy(
60 Effect.gen(function* () {
61 return yield* GCP.SecureSourceManager.RepositoriesBranchRule(
62 "Main",
63 {
64 repository: missingRepo,
65 includePattern: "main",
66 requirePullRequest: true,
67 },
68 );
69 }),
70 ),
71 );
72 expect(error._tag).toEqual("NotFound");
73
74 yield* stack.destroy();
75 }).pipe(logLevel),
76 {
77 tags: ["provider:gcp", "provider:gcp:securesourcemanager", "live"],
78 timeout: 90_000,
79 },
80);
81
82test.provider.skipIf(!runLifecycle)(
83 "create, update, and delete a branch rule",
84 (stack) =>
85 Effect.gen(function* () {
86 const project = yield* currentProject;
87 const missingRepo = missingRepoOf(project);
88 yield* stack.destroy();
89
90 const created = yield* stack.deploy(
91 Effect.gen(function* () {
92 return yield* GCP.SecureSourceManager.RepositoriesBranchRule("Main", {
93 repository:
94 process.env.GCP_TEST_SECURE_SOURCE_MANAGER_REPO ?? missingRepo,
95 includePattern: "main",
96 requirePullRequest: true,
97 minimumApprovalsCount: 1,
98 annotations: { env: "test" },
99 });
100 }),
101 );
102
103 expect(created.name).toContain("/branchRules/");
104 expect(created.includePattern).toEqual("main");
105 expect(created.requirePullRequest).toEqual(true);
106 expect(created.minimumApprovalsCount).toEqual(1);
107 expect(created.annotations).toMatchObject({ env: "test" });
108
109 const fetched = yield* ssm.getProjectsLocationsRepositoriesBranchRules({
110 name: created.name,
111 });
112 expect(fetched.name).toEqual(created.name);
113 expect(fetched.annotations?.env).toEqual("test");
114 expect(fetched.annotations?.["alchemy-id"]).toEqual(expect.any(String));
115
116 const updated = yield* stack.deploy(
117 Effect.gen(function* () {
118 return yield* GCP.SecureSourceManager.RepositoriesBranchRule("Main", {
119 repository: created.repository,
120 branchRuleId: created.branchRuleId,
121 includePattern: "main",
122 requirePullRequest: true,
123 minimumApprovalsCount: 2,
124 requireCommentsResolved: true,
125 annotations: { env: "prod", team: "platform" },
126 });
127 }),
128 );
129
130 expect(updated.branchRuleId).toEqual(created.branchRuleId);
131 expect(updated.minimumApprovalsCount).toEqual(2);
132 expect(updated.requireCommentsResolved).toEqual(true);
133 expect(updated.annotations).toMatchObject({
134 env: "prod",
135 team: "platform",
136 });
137
138 yield* stack.destroy();
139
140 const gone = yield* waitUntilGone(created.name);
141 expect(gone).toEqual("gone");
142 }).pipe(logLevel),
143 {
144 tags: ["provider:gcp", "provider:gcp:securesourcemanager", "live"],
145 timeout: 120_000,
146 },
147);