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, folderName: string) =>
17 storage.getFolders({ bucket: bucketName, folder: folderName }).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, replace, and delete a folder",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const created = yield* stack.deploy(
34 Effect.gen(function* () {
35 const bucket = yield* GCP.Storage.Bucket("Tree", {
36 location: "US-CENTRAL1",
37 hierarchicalNamespace: true,
38 uniformBucketLevelAccess: true,
39 forceDestroy: true,
40 });
41 const folder = yield* GCP.Storage.Folder("Uploads", {
42 bucketName: bucket.bucketName,
43 folderName: "uploads/",
44 });
45 return { bucket, folder };
46 }),
47 );
48
49 expect(created.folder.bucketName).toEqual(created.bucket.bucketName);
50 expect(created.folder.folderName).toEqual("uploads/");
51 expect(created.bucket.hierarchicalNamespace).toEqual(true);
52
53 const fetched = yield* storage.getFolders({
54 bucket: created.folder.bucketName,
55 folder: created.folder.folderName,
56 });
57 expect(fetched.name).toEqual("uploads/");
58
59 const updated = yield* stack.deploy(
60 Effect.gen(function* () {
61 const bucket = yield* GCP.Storage.Bucket("Tree", {
62 bucketName: created.bucket.bucketName,
63 location: "US-CENTRAL1",
64 hierarchicalNamespace: true,
65 uniformBucketLevelAccess: true,
66 forceDestroy: true,
67 });
68 const folder = yield* GCP.Storage.Folder("Uploads", {
69 bucketName: bucket.bucketName,
70 folderName: "incoming/",
71 });
72 return { bucket, folder };
73 }),
74 );
75
76 expect(updated.folder.bucketName).toEqual(created.bucket.bucketName);
77 expect(updated.folder.folderName).toEqual("incoming/");
78 expect(updated.folder.folderName).not.toEqual(created.folder.folderName);
79
80 const refetched = yield* storage.getFolders({
81 bucket: updated.folder.bucketName,
82 folder: updated.folder.folderName,
83 });
84 expect(refetched.name).toEqual("incoming/");
85
86 const previousGone = yield* waitUntilGone(
87 created.folder.bucketName,
88 created.folder.folderName,
89 );
90 expect(previousGone).toEqual("gone");
91
92 yield* stack.destroy();
93
94 const gone = yield* waitUntilGone(
95 updated.folder.bucketName,
96 updated.folder.folderName,
97 );
98 expect(gone).toEqual("gone");
99 }).pipe(logLevel),
100 { tags: ["provider:gcp", "provider:gcp:storage", "live"], timeout: 90_000 },
101);