1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as certificatemanager from "@distilled.cloud/gcp/certificatemanager_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const logLevel = Effect.provideService(
12 MinimumLogLevel,
13 process.env.DEBUG ? "Debug" : "Info",
14);
15
16// Self-signed RSA-2048 fixtures generated once with openssl (not at test time).
17const CERT_A = `-----BEGIN CERTIFICATE-----
18MIIDOjCCAiKgAwIBAgIUY07uz+vKYeBK3NYMhk03eWyTPKkwDQYJKoZIhvcNAQEL
19BQAwHTEbMBkGA1UEAwwSYWxjaGVteS1zc2wtYS50ZXN0MB4XDTI2MDgyNDE1NDM0
20NFoXDTM2MDgyMTE1NDM0NFowHTEbMBkGA1UEAwwSYWxjaGVteS1zc2wtYS50ZXN0
21MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUAYVVO5iOluteYkxOqA
22bBF+EnWrtfsUMoPJPXBEIrmT66G+vx+rArDsRxJqnWr9YstEQKsHtkErGF37avZr
2312jTIiKWk9auxyhN8O6WD2jND13m+IfsdeWKSIFx3j/RJ0McJQYu4YuN8ViZm3t9
24o3f5N+4DczgqzloV7U6/DFeMZ23nwBD9IgrUf6KyGoC/sJ4twTjpxSECekiOHgyn
25/M7SwrBTJYJTsMXzqYBPVjk7f17+7LAwNqBNzQx6HsGV8sZX4kDlcMAL53obLV1z
26kkJHhP3suaWCUl5uarZ9r5vc7DC2j6vvtghFsDvRWqqram3fCEOJOQmoAVlChbtq
27SwIDAQABo3IwcDAdBgNVHQ4EFgQUEicpezwWkZY3JMEwx1D0eraiFCEwHwYDVR0j
28BBgwFoAUEicpezwWkZY3JMEwx1D0eraiFCEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
29HREEFjAUghJhbGNoZW15LXNzbC1hLnRlc3QwDQYJKoZIhvcNAQELBQADggEBAMRj
30u6+KYAYwX/cuJruXaX7Pa9kX4Es+637dDPWBP9lLedVOGEpOujiDn2JUuNZNl6vo
31eqaJrjQuJkSgVVYikz3dGWTEvqlDIJ5nbRIo8etW1d5eoZeWcd95t2VXNRlfULBS
32eNOFYOI6iaPW2CQXFvNAgER/Azj8Y4ilky504m0bqkZWTl/2f+x1KaiBqkxqiOdX
33n5gPpu+jujVOyJni7KJo4+iIdWtQI1xAazXyOFefTMkPGLkXeQ/+tXrmY9NoVvNO
34JBEe8o+wi/LxH/K9/It1rCfWckb5zHU57li4ZxetlwYssVW4Of25sMp9c4JNF3gJ
35mkhcNRA0cRkbnom+kn8=
36-----END CERTIFICATE-----`;
37
38const CERT_B = `-----BEGIN CERTIFICATE-----
39MIIDOjCCAiKgAwIBAgIUW1TNbunaoG4/NzJZSThMKRTZuw4wDQYJKoZIhvcNAQEL
40BQAwHTEbMBkGA1UEAwwSYWxjaGVteS1zc2wtYi50ZXN0MB4XDTI2MDgyNDE1NDM0
41NFoXDTM2MDgyMTE1NDM0NFowHTEbMBkGA1UEAwwSYWxjaGVteS1zc2wtYi50ZXN0
42MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA77IIHhQKx9IOuqN9lUwn
43oqPG+LeafxTE9cKaQsO21FgjlsSvHlZqGdtdojevDYucWYgWwtOpj5FASpMGW5Kg
442PcZk8dgpXT6byYIicDA8jJVx93sFBxn2BhYvvCfWRbqPSo3SoF3DCS6AQiZX8zl
45u+Cs63Tfo2OCbCqlPylm2DGhNfs1KI22lLrU3ay0zHjeUtiK4+CQVij8He+e48mH
46MwwWtWXKP/eikQlnV4c6OYA6rLLXBvKy3g1zavBAEY5GLECP/xt1bj2qgTUge8L4
47Xr45pBj3l6Ku1thDefS9mXcHt456VeEkWir0O7cnBN903BbzhNV7vTHVfS5I8ZTg
48jQIDAQABo3IwcDAdBgNVHQ4EFgQU07mM+3JilvETyfGSF5I6Al+B8gIwHwYDVR0j
49BBgwFoAU07mM+3JilvETyfGSF5I6Al+B8gIwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
50HREEFjAUghJhbGNoZW15LXNzbC1iLnRlc3QwDQYJKoZIhvcNAQELBQADggEBAOFT
51KwWAxvo2/g+ErYxKT7Tl3ObBmUGC93T2KX38bw2jq7+7dgTJnz2kN6V5PRaq1iXj
52lmCF++dJJpCPdSiBhYNkoUIk7st0n9Mz/3oJBzass3wFY0dHw7r895rTfjkRF+F5
53Fw9dTXzy0a0Gni4I6jEWflEVXAFb0q6RJ6zHdGiriJsXvXKC8eVBJqRCy+NbufpL
54efSfgplQfXSteOlOnRP9iWsK3AH1TVw6RwLaT3iVWSbWjICf3f+8xJ65j/vNjbnY
552F2+hBmKEXfRv5liZIhV1Q9gxbpjvzw2OzaoQxkZYNbaU96yy5TsG1jGd0wqX/vT
56MbTsvjb7gbJeTqYGQeA=
57-----END CERTIFICATE-----`;
58
59const waitUntilGone = (name: string) =>
60 certificatemanager.getProjectsLocationsTrustConfigs({ name }).pipe(
61 Effect.as("found" as const),
62 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
63 Effect.repeat({
64 schedule: Schedule.spaced("1 second"),
65 until: (status) => status === "gone",
66 times: 10,
67 }),
68 );
69
70const pemOf = (value: string | undefined) => (value ?? "").replace(/\s+/g, "");
71
72test.provider(
73 "create, update, and delete a trust config",
74 (stack) =>
75 Effect.gen(function* () {
76 yield* stack.destroy();
77
78 const created = yield* stack.deploy(
79 Effect.gen(function* () {
80 return yield* GCP.CertificateManager.TrustConfig("ClientMtls", {
81 location: "global",
82 description: "mtls a",
83 labels: { env: "test" },
84 trustStores: [
85 {
86 trustAnchors: [{ pemCertificate: CERT_A }],
87 },
88 ],
89 });
90 }),
91 );
92
93 expect(created.name).toContain("/trustConfigs/");
94 expect(created.trustConfigId).toEqual(expect.any(String));
95 expect(created.location).toEqual("global");
96 expect(created.description).toEqual("mtls a");
97 expect(created.labels).toMatchObject({ env: "test" });
98 expect(created.trustStores).toHaveLength(1);
99 expect(
100 pemOf(created.trustStores[0]?.trustAnchors?.[0]?.pemCertificate),
101 ).toEqual(pemOf(CERT_A));
102
103 const fetched =
104 yield* certificatemanager.getProjectsLocationsTrustConfigs({
105 name: created.name,
106 });
107 expect(fetched.name).toEqual(created.name);
108 expect(fetched.description).toEqual("mtls a");
109 expect(fetched.labels?.env).toEqual("test");
110 expect(fetched.trustStores).toHaveLength(1);
111 expect(
112 pemOf(fetched.trustStores?.[0]?.trustAnchors?.[0]?.pemCertificate),
113 ).toEqual(pemOf(CERT_A));
114 expect(
115 Object.keys(fetched.labels ?? {}).some((key) =>
116 key.startsWith("alchemy-"),
117 ),
118 ).toEqual(true);
119
120 const updated = yield* stack.deploy(
121 Effect.gen(function* () {
122 return yield* GCP.CertificateManager.TrustConfig("ClientMtls", {
123 trustConfigId: created.trustConfigId,
124 location: "global",
125 description: "mtls b",
126 labels: { env: "prod", role: "mtls" },
127 trustStores: [
128 {
129 trustAnchors: [{ pemCertificate: CERT_A }],
130 },
131 ],
132 allowlistedCertificates: [{ pemCertificate: CERT_B }],
133 });
134 }),
135 );
136
137 expect(updated.name).toEqual(created.name);
138 expect(updated.description).toEqual("mtls b");
139 expect(updated.labels).toMatchObject({ env: "prod", role: "mtls" });
140 expect(updated.allowlistedCertificates).toHaveLength(1);
141 expect(pemOf(updated.allowlistedCertificates[0]?.pemCertificate)).toEqual(
142 pemOf(CERT_B),
143 );
144
145 const refetched =
146 yield* certificatemanager.getProjectsLocationsTrustConfigs({
147 name: created.name,
148 });
149 expect(refetched.description).toEqual("mtls b");
150 expect(refetched.labels?.env).toEqual("prod");
151 expect(refetched.labels?.role).toEqual("mtls");
152 expect(
153 pemOf(refetched.allowlistedCertificates?.[0]?.pemCertificate),
154 ).toEqual(pemOf(CERT_B));
155 expect(
156 pemOf(refetched.trustStores?.[0]?.trustAnchors?.[0]?.pemCertificate),
157 ).toEqual(pemOf(CERT_A));
158
159 yield* stack.destroy();
160
161 const gone = yield* waitUntilGone(created.name);
162 expect(gone).toEqual("gone");
163 }).pipe(logLevel),
164 {
165 tags: ["provider:gcp", "provider:gcp:certificatemanager", "live"],
166 timeout: 90_000,
167 },
168);