1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as vault from "@distilled.cloud/gcp/vault_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import { logLevel, runLifecycle } from "./common.ts";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const waitUntilGone = (matterId: string) =>
12 vault.getMatters({ matterId }).pipe(
13 Effect.map((matter) =>
14 matter.state === "DELETED" ? ("gone" as const) : ("found" as const),
15 ),
16 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
17 Effect.repeat({
18 schedule: Schedule.spaced("1 second"),
19 until: (status) => status === "gone",
20 times: 10,
21 }),
22 );
23
24test.provider(
25 "getMatters on a missing matter fails with a typed tag",
26 (stack) =>
27 Effect.gen(function* () {
28 yield* stack.destroy();
29
30 const error = yield* Effect.flip(
31 vault.getMatters({ matterId: "alchemy-missing-matter" }),
32 );
33 expect(error._tag).toEqual("VaultScopeInsufficient");
34
35 yield* stack.destroy();
36 }).pipe(logLevel),
37 { tags: ["provider:gcp", "provider:gcp:vault", "live"], timeout: 90_000 },
38);
39
40test.provider.skipIf(!!process.env.GCP_TEST_VAULT)(
41 "createMatters without the Vault OAuth scope fails with VaultScopeInsufficient",
42 (stack) =>
43 Effect.gen(function* () {
44 yield* stack.destroy();
45
46 const error = yield* Effect.flip(
47 vault.createMatters({
48 body: {
49 name: "Alchemy Vault Probe",
50 description: "alchemy probe",
51 },
52 }),
53 );
54 expect(error._tag).toEqual("VaultScopeInsufficient");
55
56 yield* stack.destroy();
57 }).pipe(logLevel),
58 { tags: ["provider:gcp", "provider:gcp:vault", "live"], timeout: 90_000 },
59);
60
61test.provider.skipIf(!runLifecycle)(
62 "create, update, and delete a matter",
63 (stack) =>
64 Effect.gen(function* () {
65 yield* stack.destroy();
66
67 const created = yield* stack.deploy(
68 Effect.gen(function* () {
69 return yield* GCP.Vault.Matter("Case", {
70 name: "Acme v Contoso",
71 description: "litigation hold",
72 });
73 }),
74 );
75
76 expect(created.matterId.length).toBeGreaterThan(0);
77 expect(created.name).toEqual("Acme v Contoso");
78 expect(created.description).toEqual("litigation hold");
79 expect(created.state).toEqual("OPEN");
80
81 const fetched = yield* vault.getMatters({
82 matterId: created.matterId,
83 view: "FULL",
84 });
85 expect(fetched.matterId).toEqual(created.matterId);
86 expect(fetched.description).toContain("[alchemy ");
87
88 const updated = yield* stack.deploy(
89 Effect.gen(function* () {
90 return yield* GCP.Vault.Matter("Case", {
91 matterId: created.matterId,
92 name: "Acme v Contoso 2026",
93 description: "litigation hold extended",
94 });
95 }),
96 );
97
98 expect(updated.matterId).toEqual(created.matterId);
99 expect(updated.name).toEqual("Acme v Contoso 2026");
100 expect(updated.description).toEqual("litigation hold extended");
101
102 yield* stack.destroy();
103
104 const gone = yield* waitUntilGone(created.matterId);
105 expect(gone).toEqual("gone");
106 }).pipe(logLevel),
107 { tags: ["provider:gcp", "provider:gcp:vault", "live"], timeout: 90_000 },
108);