1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as storage from "@distilled.cloud/gcp/storage_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
16const SERVICE_ACCOUNT_EMAIL =
17 "alchemy-testing@alchemy-gcp-testing-83661.iam.gserviceaccount.com";
18const ENTITY = `user-${SERVICE_ACCOUNT_EMAIL}`;
19
20const waitUntilGone = (bucketName: string, entity: string) =>
21 storage.getDefaultObjectAccessControls({ bucket: bucketName, entity }).pipe(
22 Effect.as("found" as const),
23 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
24 Effect.repeat({
25 schedule: Schedule.spaced("1 second"),
26 until: (status) => status === "gone",
27 times: 10,
28 }),
29 );
30
31test.provider(
32 "create, update, and delete a default object ACL",
33 (stack) =>
34 Effect.gen(function* () {
35 yield* stack.destroy();
36
37 const created = yield* stack.deploy(
38 Effect.gen(function* () {
39 const bucket = yield* GCP.Storage.Bucket("Assets", {
40 location: "US-CENTRAL1",
41 forceDestroy: true,
42 });
43 return yield* GCP.Storage.DefaultObjectAccessControl("Reader", {
44 bucketName: bucket.bucketName,
45 entity: ENTITY,
46 role: "READER",
47 });
48 }),
49 );
50
51 expect(created.bucketName).toEqual(expect.any(String));
52 expect(created.entity).toEqual(ENTITY);
53 expect(created.role).toEqual("READER");
54
55 const fetched = yield* storage.getDefaultObjectAccessControls({
56 bucket: created.bucketName,
57 entity: ENTITY,
58 });
59 expect(fetched.entity).toEqual(ENTITY);
60 expect(fetched.role).toEqual("READER");
61
62 const updated = yield* stack.deploy(
63 Effect.gen(function* () {
64 const bucket = yield* GCP.Storage.Bucket("Assets", {
65 bucketName: created.bucketName,
66 location: "US-CENTRAL1",
67 forceDestroy: true,
68 });
69 return yield* GCP.Storage.DefaultObjectAccessControl("Reader", {
70 bucketName: bucket.bucketName,
71 entity: ENTITY,
72 role: "OWNER",
73 });
74 }),
75 );
76
77 expect(updated.bucketName).toEqual(created.bucketName);
78 expect(updated.entity).toEqual(ENTITY);
79 expect(updated.role).toEqual("OWNER");
80
81 const refetched = yield* storage.getDefaultObjectAccessControls({
82 bucket: updated.bucketName,
83 entity: ENTITY,
84 });
85 expect(refetched.role).toEqual("OWNER");
86
87 yield* stack.destroy();
88
89 const gone = yield* waitUntilGone(created.bucketName, ENTITY);
90 expect(gone).toEqual("gone");
91 }).pipe(logLevel),
92 { tags: ["provider:gcp", "provider:gcp:storage", "live"], timeout: 90_000 },
93);