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 waitUntilGone = (bucketName: string) =>
17 storage.getBuckets({ bucket: bucketName }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("1 second"),
22 until: (status) => status === "gone",
23 times: 10,
24 }),
25 );
26
27test.provider(
28 "create, update, and delete a bucket",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const created = yield* stack.deploy(
34 Effect.gen(function* () {
35 return yield* GCP.Storage.Bucket("Assets", {
36 location: "US-CENTRAL1",
37 labels: { env: "test" },
38 forceDestroy: true,
39 });
40 }),
41 );
42
43 expect(created.bucketName).toEqual(expect.any(String));
44 expect(created.location.toUpperCase()).toEqual("US-CENTRAL1");
45 expect(created.labels).toMatchObject({ env: "test" });
46 expect(created.versioning).toEqual(false);
47
48 const fetched = yield* storage.getBuckets({
49 bucket: created.bucketName,
50 projection: "full",
51 });
52 expect(fetched.name).toEqual(created.bucketName);
53 expect(fetched.labels?.env).toEqual("test");
54
55 const updated = yield* stack.deploy(
56 Effect.gen(function* () {
57 return yield* GCP.Storage.Bucket("Assets", {
58 bucketName: created.bucketName,
59 location: "US-CENTRAL1",
60 storageClass: "NEARLINE",
61 versioning: true,
62 labels: { env: "prod", role: "assets" },
63 forceDestroy: true,
64 });
65 }),
66 );
67
68 expect(updated.bucketName).toEqual(created.bucketName);
69 expect(updated.versioning).toEqual(true);
70 expect(updated.storageClass).toEqual("NEARLINE");
71 expect(updated.labels).toMatchObject({ env: "prod", role: "assets" });
72
73 const trimmed = yield* stack.deploy(
74 Effect.gen(function* () {
75 return yield* GCP.Storage.Bucket("Assets", {
76 bucketName: created.bucketName,
77 location: "US-CENTRAL1",
78 storageClass: "NEARLINE",
79 versioning: true,
80 labels: { env: "prod" },
81 forceDestroy: true,
82 });
83 }),
84 );
85
86 expect(trimmed.labels).toMatchObject({ env: "prod" });
87 expect(trimmed.labels.role).toBeUndefined();
88 const fetchedTrimmed = yield* storage.getBuckets({
89 bucket: trimmed.bucketName,
90 projection: "full",
91 });
92 expect(fetchedTrimmed.labels?.role).toBeUndefined();
93
94 yield* stack.destroy();
95
96 const gone = yield* waitUntilGone(created.bucketName);
97 expect(gone).toEqual("gone");
98 }).pipe(logLevel),
99 { tags: ["provider:gcp", "provider:gcp:storage", "live"], timeout: 120_000 },
100);