1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as binaryauthorization from "@distilled.cloud/gcp/binaryauthorization_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 logLevel,
9 currentProject,
10 TEST_PKIX_PUBLIC_KEY_PEM,
11} from "./common.ts";
12
13const { test } = Test.make({ providers: GCP.providers() });
14
15const waitUntilGone = (name: string) =>
16 binaryauthorization.getProjectsAttestors({ name }).pipe(
17 Effect.as("found" as const),
18 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
19 Effect.repeat({
20 schedule: Schedule.spaced("1 second"),
21 until: (status) => status === "gone",
22 times: 10,
23 }),
24 );
25
26test.provider(
27 "getProjectsAttestors on a missing attestor fails with a typed tag",
28 (stack) =>
29 Effect.gen(function* () {
30 const project = yield* currentProject;
31 yield* stack.destroy();
32
33 const error = yield* Effect.flip(
34 binaryauthorization.getProjectsAttestors({
35 name: `projects/${project}/attestors/alchemy-missing-attestor`,
36 }),
37 );
38 expect(error._tag).toEqual("NotFound");
39
40 yield* stack.destroy();
41 }).pipe(logLevel),
42 {
43 tags: ["provider:gcp", "provider:gcp:binaryauthorization", "live"],
44 timeout: 90_000,
45 },
46);
47
48test.provider(
49 "create, update, and delete an attestor",
50 (stack) =>
51 Effect.gen(function* () {
52 yield* stack.destroy();
53
54 const created = yield* stack.deploy(
55 Effect.gen(function* () {
56 const note = yield* GCP.ContainerAnalysis.Note("Authority", {
57 shortDescription: "alchemy binauthz attestor",
58 attestation: { hint: { humanReadableName: "Alchemy QA" } },
59 });
60 const attestor = yield* GCP.BinaryAuthorization.Attestor("Qa", {
61 noteReference: note.name,
62 description: "initial",
63 });
64 return { note, attestor };
65 }),
66 );
67
68 expect(created.attestor.name).toContain("/attestors/");
69 expect(created.attestor.attestorId).toEqual(expect.any(String));
70 expect(created.attestor.description).toEqual("initial");
71 expect(created.attestor.noteReference).toEqual(created.note.name);
72 expect(created.attestor.publicKeys).toEqual([]);
73
74 const fetched = yield* binaryauthorization.getProjectsAttestors({
75 name: created.attestor.name,
76 });
77 expect(fetched.name).toEqual(created.attestor.name);
78 expect(fetched.description).toContain("[alchemy ");
79 expect(fetched.description).toContain("initial");
80 expect(fetched.userOwnedGrafeasNote?.noteReference).toEqual(
81 created.note.name,
82 );
83
84 const updated = yield* stack.deploy(
85 Effect.gen(function* () {
86 const note = yield* GCP.ContainerAnalysis.Note("Authority", {
87 noteId: created.note.noteId,
88 shortDescription: "alchemy binauthz attestor",
89 attestation: { hint: { humanReadableName: "Alchemy QA" } },
90 });
91 const attestor = yield* GCP.BinaryAuthorization.Attestor("Qa", {
92 attestorId: created.attestor.attestorId,
93 noteReference: note.name,
94 description: "updated",
95 publicKeys: [
96 {
97 comment: "ci",
98 pkixPublicKey: {
99 publicKeyPem: TEST_PKIX_PUBLIC_KEY_PEM,
100 signatureAlgorithm: "EC_SIGN_P256_SHA256",
101 },
102 },
103 ],
104 });
105 return { note, attestor };
106 }),
107 );
108
109 expect(updated.attestor.name).toEqual(created.attestor.name);
110 expect(updated.attestor.description).toEqual("updated");
111 expect(updated.attestor.publicKeys.length).toEqual(1);
112 expect(updated.attestor.publicKeys[0]?.comment).toEqual("ci");
113
114 const fetchedUpdate = yield* binaryauthorization.getProjectsAttestors({
115 name: updated.attestor.name,
116 });
117 expect(fetchedUpdate.description).toContain("updated");
118 expect(fetchedUpdate.userOwnedGrafeasNote?.publicKeys?.length).toEqual(1);
119
120 yield* stack.destroy();
121 const gone = yield* waitUntilGone(created.attestor.name);
122 expect(gone).toEqual("gone");
123 }).pipe(logLevel),
124 {
125 tags: ["provider:gcp", "provider:gcp:binaryauthorization", "live"],
126 timeout: 90_000,
127 },
128);