1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as datastore from "@distilled.cloud/gcp/datastore_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import { GcpEnvironment } from "@/GCP/Environment";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17const runLifecycle = !process.env.FAST;
18
19const waitUntilGone = (indexId: string) =>
20 Effect.gen(function* () {
21 const { project } = yield* GcpEnvironment.current;
22
23 return yield* datastore
24 .getProjectsIndexes({ projectId: project, indexId })
25 .pipe(
26 Effect.as("found" as const),
27 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
28 Effect.repeat({
29 schedule: Schedule.spaced("5 seconds"),
30 until: (status) => status === "gone",
31 times: 24,
32 }),
33 );
34 });
35
36test.provider(
37 "getProjectsIndexes on a missing index fails with a typed tag",
38 (stack) =>
39 Effect.gen(function* () {
40 const { project } = yield* GcpEnvironment.current;
41
42 yield* stack.destroy();
43
44 const error = yield* Effect.flip(
45 datastore.getProjectsIndexes({
46 projectId: project,
47 indexId: "CICAgOjXh4AA",
48 }),
49 );
50 expect(error._tag).toBe("NotFound");
51
52 const page = yield* datastore.listProjectsIndexes({
53 projectId: project,
54 });
55 expect((page.indexes ?? []).map((index) => index.indexId)).not.toContain(
56 "CICAgOjXh4AA",
57 );
58
59 yield* stack.destroy();
60 }).pipe(logLevel),
61 { tags: ["provider:gcp", "provider:gcp:datastore", "live"], timeout: 90_000 },
62);
63
64test.provider.skipIf(!runLifecycle)(
65 "create and delete a datastore composite index",
66 (stack) =>
67 Effect.gen(function* () {
68 const { project } = yield* GcpEnvironment.current;
69
70 yield* stack.destroy();
71
72 const created = yield* stack.deploy(
73 Effect.gen(function* () {
74 return yield* GCP.Datastore.Index("TasksByDone", {
75 ancestor: "NONE",
76 properties: [
77 { name: "done", direction: "ASCENDING" },
78 { name: "priority", direction: "DESCENDING" },
79 ],
80 });
81 }),
82 );
83
84 expect(created.indexId).toEqual(expect.any(String));
85 expect(created.indexId.length).toBeGreaterThan(0);
86 expect(created.kind.length).toBeGreaterThan(0);
87 expect(created.ancestor).toEqual("NONE");
88 expect(created.name).toContain("/indexes/");
89
90 const fetched = yield* datastore.getProjectsIndexes({
91 projectId: project,
92 indexId: created.indexId,
93 });
94 expect(fetched.indexId).toEqual(created.indexId);
95 expect(fetched.kind).toEqual(created.kind);
96 expect(
97 (fetched.properties ?? []).some((property) => property.name === "done"),
98 ).toEqual(true);
99
100 yield* stack.destroy();
101
102 const gone = yield* waitUntilGone(created.indexId);
103 expect(gone).toEqual("gone");
104 }).pipe(logLevel),
105 {
106 tags: ["provider:gcp", "provider:gcp:datastore", "live"],
107 timeout: 180_000,
108 },
109);