1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as securityposture from "@distilled.cloud/gcp/securityposture_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import {
7 logLevel,
8 organization,
9 runLifecycle,
10 waitUntilDeploymentGone,
11 waitUntilPostureGone,
12} from "./common.ts";
13
14const { test } = Test.make({ providers: GCP.providers() });
15
16test.provider.skipIf(!runLifecycle)(
17 "getOrganizationsLocationsPostureDeployments on a missing deployment fails with a typed tag",
18 (stack) =>
19 Effect.gen(function* () {
20 yield* stack.destroy();
21
22 const error = yield* Effect.flip(
23 securityposture.getOrganizationsLocationsPostureDeployments({
24 name: `${organization}/locations/global/postureDeployments/alchemy-missing`,
25 }),
26 );
27 expect(error._tag).toEqual("NotFound");
28
29 yield* stack.destroy();
30 }).pipe(logLevel),
31 {
32 tags: ["provider:gcp", "provider:gcp:securityposture", "live"],
33 timeout: 90_000,
34 },
35);
36
37test.provider.skipIf(!runLifecycle)(
38 "create, update, and delete a posture deployment",
39 (stack) =>
40 Effect.gen(function* () {
41 yield* stack.destroy();
42
43 const created = yield* stack.deploy(
44 Effect.gen(function* () {
45 const posture = yield* GCP.SecurityPosture.Posture("Baseline", {
46 organization,
47 state: "ACTIVE",
48 description: "deployable baseline",
49 annotations: { env: "test" },
50 });
51 const deployment = yield* GCP.SecurityPosture.PostureDeployment(
52 "Staging",
53 {
54 organization,
55 postureId: posture.name,
56 postureRevisionId: posture.revisionId.as<string>(),
57 description: "staging deployment",
58 annotations: { env: "test" },
59 },
60 );
61 return { posture, deployment };
62 }),
63 );
64
65 expect(created.posture.state).toEqual("ACTIVE");
66 expect(created.posture.revisionId).toEqual(expect.any(String));
67 expect(created.deployment.postureDeploymentId).toEqual(
68 expect.any(String),
69 );
70 expect(created.deployment.organization).toEqual(organization);
71 expect(created.deployment.name).toEqual(
72 `${organization}/locations/global/postureDeployments/${created.deployment.postureDeploymentId}`,
73 );
74 expect(created.deployment.postureId).toEqual(created.posture.name);
75 expect(created.deployment.postureRevisionId).toEqual(
76 created.posture.revisionId,
77 );
78 expect(created.deployment.description).toEqual("staging deployment");
79 expect(created.deployment.annotations).toMatchObject({ env: "test" });
80
81 const fetched =
82 yield* securityposture.getOrganizationsLocationsPostureDeployments({
83 name: created.deployment.name,
84 });
85 expect(fetched.name).toEqual(created.deployment.name);
86 expect(fetched.annotations?.["alchemy-id"]).toEqual(expect.any(String));
87
88 const updated = yield* stack.deploy(
89 Effect.gen(function* () {
90 const posture = yield* GCP.SecurityPosture.Posture("Baseline", {
91 organization,
92 postureId: created.posture.postureId,
93 state: "ACTIVE",
94 description: "deployable baseline v2",
95 annotations: { env: "prod" },
96 });
97 const deployment = yield* GCP.SecurityPosture.PostureDeployment(
98 "Staging",
99 {
100 organization,
101 postureDeploymentId: created.deployment.postureDeploymentId,
102 postureId: posture.name,
103 postureRevisionId: posture.revisionId.as<string>(),
104 description: "staging deployment v2",
105 annotations: { env: "prod" },
106 },
107 );
108 return { posture, deployment };
109 }),
110 );
111
112 expect(updated.deployment.name).toEqual(created.deployment.name);
113 expect(updated.deployment.postureId).toEqual(updated.posture.name);
114 expect(updated.posture.description).toEqual("deployable baseline v2");
115
116 yield* stack.destroy();
117
118 const deploymentGone = yield* waitUntilDeploymentGone(
119 created.deployment.name,
120 );
121 expect(deploymentGone).toEqual("gone");
122 const postureGone = yield* waitUntilPostureGone(created.posture.name);
123 expect(postureGone).toEqual("gone");
124 }).pipe(logLevel),
125 {
126 tags: ["provider:gcp", "provider:gcp:securityposture", "live"],
127 timeout: 120_000,
128 },
129);