t3-code-android-nightly/.repos/alchemy-effect/packages/alchemy/test/GCP/KMS/CryptoKey.test.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

242 lines
8.4 KiB
TypeScript

import * as GCP from "@/GCP";
import * as Test from "@/Test/Alchemy";
import { KEY_RING_ID, kmsTestId } from "./common.ts";
import * as kms from "@distilled.cloud/gcp/cloudkms_v1";
import { expect } from "alchemy-test";
import * as Effect from "effect/Effect";
import { MinimumLogLevel } from "effect/References";
import { GcpEnvironment } from "@/GCP/Environment";
const { test } = Test.make({ providers: GCP.providers() });
const logLevel = Effect.provideService(
MinimumLogLevel,
process.env.DEBUG ? "Debug" : "Info",
);
// Cloud KMS KeyRings cannot be deleted. Reuse the standing test ring.
// Encrypt/decrypt needs a version; versions cannot be deleted for ≥24h, so
// this key is reused across runs (names cannot be reused after delete).
const ENCRYPT_KEY_ID = kmsTestId("cryptokey");
test.provider(
"getProjectsLocationsKeyRingsCryptoKeys on a missing key fails with NotFound",
() =>
Effect.gen(function* () {
const { project } = yield* GcpEnvironment.current;
const error = yield* Effect.flip(
kms.getProjectsLocationsKeyRingsCryptoKeys({
name: `projects/${project}/locations/us-central1/keyRings/${KEY_RING_ID}/cryptoKeys/alchemy-cryptokey-does-not-exist`,
}),
);
expect(error._tag).toBe("NotFound");
}).pipe(logLevel),
{ tags: ["provider:gcp", "provider:gcp:kms", "live"] },
);
test.provider(
"create, update, and release a crypto key",
(stack) =>
Effect.gen(function* () {
const { project } = yield* GcpEnvironment.current;
yield* stack.destroy();
const created = yield* stack.deploy(
Effect.gen(function* () {
const ring = yield* GCP.KMS.KeyRing("Keys", {
keyRingId: KEY_RING_ID,
location: "us-central1",
});
return yield* GCP.KMS.CryptoKey("Data", {
keyRing: ring.name,
skipInitialVersionCreation: true,
labels: { env: "test" },
});
}),
);
expect(created.cryptoKeyId).toEqual(expect.any(String));
expect(created.location).toEqual("us-central1");
expect(created.project).toEqual(project);
expect(created.purpose).toEqual("ENCRYPT_DECRYPT");
expect(created.keyRing).toContain(`/keyRings/${KEY_RING_ID}`);
expect(created.name).toContain("/cryptoKeys/");
expect(created.labels).toMatchObject({ env: "test" });
expect(created.primaryVersion).toBeUndefined();
const fetched = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: created.name,
});
expect(fetched.name).toEqual(created.name);
expect(fetched.labels?.env).toEqual("test");
expect(fetched.purpose).toEqual("ENCRYPT_DECRYPT");
const updated = yield* stack.deploy(
Effect.gen(function* () {
const ring = yield* GCP.KMS.KeyRing("Keys", {
keyRingId: KEY_RING_ID,
location: "us-central1",
});
return yield* GCP.KMS.CryptoKey("Data", {
keyRing: ring.name,
cryptoKeyId: created.cryptoKeyId,
skipInitialVersionCreation: true,
labels: { env: "prod", role: "cmek" },
});
}),
);
expect(updated.name).toEqual(created.name);
expect(updated.labels).toMatchObject({ env: "prod", role: "cmek" });
const fetchedUpdate = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: created.name,
});
expect(fetchedUpdate.labels?.env).toEqual("prod");
expect(fetchedUpdate.labels?.role).toEqual("cmek");
yield* stack.destroy();
// Deleting would retire the name forever; destroy releases instead.
const released = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: created.name,
});
expect(released.labels?.["alchemy-released"]).toEqual("true");
expect(released.labels?.["alchemy-id"]).toBeUndefined();
}).pipe(logLevel),
{ tags: ["provider:gcp", "provider:gcp:kms", "live"], timeout: 90_000 },
);
const roundTrip = (name: string, text: string) =>
Effect.gen(function* () {
const plaintext = yield* Effect.sync(() =>
Buffer.from(text, "utf8").toString("base64"),
);
const { ciphertext } =
yield* kms.encryptProjectsLocationsKeyRingsCryptoKeys({
name,
body: { plaintext },
});
const decrypted = yield* kms.decryptProjectsLocationsKeyRingsCryptoKeys({
name,
body: { ciphertext },
});
return decrypted.plaintext === plaintext;
});
test.provider(
"destroy releases a fixed-id key and the next deploy reclaims it",
(stack) =>
Effect.gen(function* () {
yield* stack.destroy();
const deploy = stack.deploy(
Effect.gen(function* () {
const ring = yield* GCP.KMS.KeyRing("Keys", {
keyRingId: KEY_RING_ID,
location: "us-central1",
});
return yield* GCP.KMS.CryptoKey("Cipher", {
keyRing: ring.name,
cryptoKeyId: ENCRYPT_KEY_ID,
labels: { env: "test" },
});
}),
);
const key = yield* deploy;
expect(key.name).toContain(`/cryptoKeys/${ENCRYPT_KEY_ID}`);
expect(yield* roundTrip(key.name, "first")).toEqual(true);
const firstPrimary = (yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: key.name,
})).primary?.name;
yield* stack.destroy();
// KMS keeps the key; Alchemy releases it: every version scheduled for
// destruction, ownership labels swapped for the released marker.
const released = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: key.name,
});
expect(released.labels).toEqual({
env: "test",
"alchemy-released": "true",
});
expect(released.primary?.state).toEqual("DESTROY_SCHEDULED");
// Redeploying the same id reclaims it with fresh key material.
const reclaimed = yield* deploy;
expect(reclaimed.name).toEqual(key.name);
const live = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: key.name,
});
expect(live.labels?.["alchemy-released"]).toBeUndefined();
expect(live.labels?.["alchemy-id"]).toEqual("cipher");
expect(live.primary?.state).toEqual("ENABLED");
expect(live.primary?.name).not.toEqual(firstPrimary);
expect(yield* roundTrip(key.name, "second")).toEqual(true);
yield* stack.destroy();
}).pipe(logLevel),
{ tags: ["provider:gcp", "provider:gcp:kms", "live"], timeout: 120_000 },
);
test.provider(
"redeploy after destroy reuses the default ring and key ids",
(stack) =>
Effect.gen(function* () {
const { project } = yield* GcpEnvironment.current;
yield* stack.destroy();
const deploy = stack.deploy(
Effect.gen(function* () {
const ring = yield* GCP.KMS.KeyRing("RedeployKeys", {
location: "us-central1",
});
const key = yield* GCP.KMS.CryptoKey("RedeployData", {
keyRing: ring.name,
});
return { ring, key };
}),
);
// Only this test creates rings with generated ids in this file.
const generatedRings = kms
.listProjectsLocationsKeyRings({
parent: `projects/${project}/locations/us-central1`,
pageSize: 1000,
})
.pipe(
Effect.map((response) =>
(response.keyRings ?? [])
.map((ring) => ring.name ?? "")
.filter((name) => name.includes("/keyRings/gcp-kms-cryptokey-")),
),
);
const before = new Set(yield* generatedRings);
const first = yield* deploy;
expect(yield* roundTrip(first.key.name, "first")).toEqual(true);
yield* stack.destroy();
const second = yield* deploy;
expect(second.ring.name).toEqual(first.ring.name);
expect(second.key.name).toEqual(first.key.name);
expect(yield* roundTrip(second.key.name, "second")).toEqual(true);
// At most the one deterministic ring appeared — no leak per cycle.
const created = (yield* generatedRings).filter(
(name) => !before.has(name),
);
expect(created.filter((name) => name !== first.ring.name)).toEqual([]);
yield* stack.destroy();
const released = yield* kms.getProjectsLocationsKeyRingsCryptoKeys({
name: first.key.name,
});
expect(released.labels?.["alchemy-released"]).toEqual("true");
}).pipe(logLevel),
{ tags: ["provider:gcp", "provider:gcp:kms", "live"], timeout: 180_000 },
);