1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as cw from "@distilled.cloud/gcp/contentwarehouse_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
17// Document AI Warehouse is disabled on the testing project: calls fail with
18// ServiceDisabled "Document AI Warehouse API has not been used in project ...
19// before or it is disabled" (the service also needs per-project
20// provisioning). Set GCP_TEST_CONTENTWAREHOUSE=1 on a provisioned project.
21const runLifecycle =
22 !process.env.FAST && !!process.env.GCP_TEST_CONTENTWAREHOUSE;
23
24const location = "us";
25
26const waitUntilGone = (name: string) =>
27 cw.getProjectsLocationsDocumentSchemas({ name }).pipe(
28 Effect.as("found" as const),
29 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
30 Effect.repeat({
31 schedule: Schedule.spaced("1 second"),
32 until: (status) => status === "gone",
33 times: 10,
34 }),
35 );
36
37test.provider.skipIf(runLifecycle)(
38 "getProjectsLocationsDocumentSchemas without Document AI Warehouse enabled fails with ServiceDisabled",
39 (stack) =>
40 Effect.gen(function* () {
41 const { project } = yield* GcpEnvironment.current;
42 const parent = `projects/${project}/locations/${location}`;
43 yield* stack.destroy();
44
45 const error = yield* Effect.flip(
46 cw.getProjectsLocationsDocumentSchemas({
47 name: `${parent}/documentSchemas/alchemy-missing-schema`,
48 }),
49 );
50 expect(error._tag).toEqual("ServiceDisabled");
51
52 yield* stack.destroy();
53 }).pipe(logLevel),
54 {
55 tags: ["provider:gcp", "provider:gcp:contentwarehouse", "live"],
56 timeout: 90_000,
57 },
58);
59
60test.provider.skipIf(!runLifecycle)(
61 "create, update, and delete a document schema",
62 (stack) =>
63 Effect.gen(function* () {
64 const { project } = yield* GcpEnvironment.current;
65 const parent = `projects/${project}/locations/${location}`;
66 yield* stack.destroy();
67
68 const created = yield* stack.deploy(
69 Effect.gen(function* () {
70 return yield* GCP.ContentWarehouse.DocumentSchema("Invoice", {
71 location,
72 displayName: "invoice",
73 description: "invoice fields",
74 propertyDefinitions: [
75 {
76 name: "title",
77 displayName: "Title",
78 isSearchable: true,
79 textTypeOptions: {},
80 },
81 ],
82 });
83 }),
84 );
85
86 expect(created.documentSchemaId).toEqual(expect.any(String));
87 expect(created.name).toContain("/documentSchemas/");
88 expect(created.location).toEqual(location);
89 expect(created.displayName).toEqual("invoice");
90 expect(created.description).toEqual("invoice fields");
91 expect((created.propertyDefinitions ?? []).length).toBeGreaterThan(0);
92
93 const fetched = yield* cw.getProjectsLocationsDocumentSchemas({
94 name: created.name,
95 });
96 expect(fetched.name).toEqual(created.name);
97 expect(fetched.description).toContain("alchemy-id=");
98 expect(fetched.description).toContain("invoice fields");
99
100 const updated = yield* stack.deploy(
101 Effect.gen(function* () {
102 return yield* GCP.ContentWarehouse.DocumentSchema("Invoice", {
103 documentSchemaId: created.documentSchemaId,
104 location,
105 displayName: "invoice-v2",
106 description: "invoice v2",
107 propertyDefinitions: [
108 {
109 name: "title",
110 displayName: "Title",
111 isSearchable: true,
112 textTypeOptions: {},
113 },
114 {
115 name: "amount",
116 displayName: "Amount",
117 isFilterable: true,
118 floatTypeOptions: {},
119 },
120 ],
121 });
122 }),
123 );
124
125 expect(updated.name).toEqual(created.name);
126 expect(updated.displayName).toEqual("invoice-v2");
127 expect(updated.description).toEqual("invoice v2");
128 expect(
129 (updated.propertyDefinitions ?? []).some(
130 (property) => property.name === "amount",
131 ),
132 ).toEqual(true);
133
134 yield* stack.destroy();
135 const gone = yield* waitUntilGone(created.name);
136 expect(gone).toEqual("gone");
137 }).pipe(logLevel),
138 {
139 tags: ["provider:gcp", "provider:gcp:contentwarehouse", "live"],
140 timeout: 90_000,
141 },
142);