1import { GcpEnvironment } from "@/GCP/Environment";
2import * as GCP from "@/GCP";
3import { quotaTolerant } from "./parent.ts";
4import * as Test from "@/Test/Alchemy";
5import * as discoveryengine from "@distilled.cloud/gcp/discoveryengine_v1";
6import { expect } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import { MinimumLogLevel } from "effect/References";
9import * as Schedule from "effect/Schedule";
10
11const { test } = Test.make({ providers: GCP.providers() });
12
13const logLevel = Effect.provideService(
14 MinimumLogLevel,
15 process.env.DEBUG ? "Debug" : "Info",
16);
17
18const runLifecycle = !process.env.FAST;
19
20const waitUntilGone = (name: string) =>
21 discoveryengine
22 .getProjectsLocationsCollectionsDataStoresControls({ name })
23 .pipe(
24 Effect.as("found" as const),
25 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
26 Effect.repeat({
27 schedule: Schedule.spaced("2 seconds"),
28 until: (status) => status === "gone",
29 times: 10,
30 }),
31 );
32
33test.provider(
34 "getProjectsLocationsCollectionsDataStoresControls on a missing control fails with a typed tag",
35 (stack) =>
36 Effect.gen(function* () {
37 const { project } = yield* GcpEnvironment.current;
38
39 yield* stack.destroy();
40
41 const error = yield* Effect.flip(
42 discoveryengine.getProjectsLocationsCollectionsDataStoresControls({
43 name: `projects/${project}/locations/global/collections/default_collection/dataStores/alchemy-missing/controls/alchemy-missing`,
44 }),
45 );
46 expect(error._tag).toEqual("NotFound");
47
48 yield* stack.destroy();
49 }).pipe(logLevel, quotaTolerant),
50 {
51 tags: ["provider:gcp", "provider:gcp:discoveryengine", "live"],
52 timeout: 90_000,
53 },
54);
55
56test.provider.skipIf(!runLifecycle)(
57 "create, update, and delete a collection data store control",
58 (stack) =>
59 Effect.gen(function* () {
60 yield* stack.destroy();
61
62 const parent = yield* stack.deploy(
63 Effect.gen(function* () {
64 const store = yield* GCP.DiscoveryEngine.CollectionsDataStore(
65 "Docs",
66 {
67 location: "global",
68 displayName: "docs",
69 disableCmek: true,
70 },
71 );
72 const engine = yield* GCP.DiscoveryEngine.CollectionsEngine(
73 "Search",
74 {
75 location: "global",
76 dataStoreIds: [store.dataStoreId],
77 displayName: "eng",
78 solutionType: "SOLUTION_TYPE_SEARCH",
79 industryVertical: "GENERIC",
80 },
81 );
82 return { store, engine };
83 }),
84 );
85
86 const created = yield* stack.deploy(
87 Effect.gen(function* () {
88 const store = yield* GCP.DiscoveryEngine.CollectionsDataStore(
89 "Docs",
90 {
91 dataStoreId: parent.store.dataStoreId,
92 location: "global",
93 collection: parent.store.collection,
94 displayName: "docs",
95 disableCmek: true,
96 },
97 );
98 const engine = yield* GCP.DiscoveryEngine.CollectionsEngine(
99 "Search",
100 {
101 engineId: parent.engine.engineId,
102 location: "global",
103 dataStoreIds: [store.dataStoreId],
104 displayName: "eng",
105 solutionType: "SOLUTION_TYPE_SEARCH",
106 industryVertical: "GENERIC",
107 },
108 );
109 const control =
110 yield* GCP.DiscoveryEngine.CollectionsDataStoresControl(
111 "Greetings",
112 {
113 dataStore: store.name,
114 displayName: parent.engine.engineId.slice(0, 2),
115 synonyms: ["hello", "hi"],
116 },
117 );
118 return { store, engine, control };
119 }),
120 );
121
122 expect(created.control.name).toContain("/controls/");
123 expect(created.control.synonyms).toEqual(
124 expect.arrayContaining(["hello", "hi"]),
125 );
126
127 const fetched =
128 yield* discoveryengine.getProjectsLocationsCollectionsDataStoresControls(
129 { name: created.control.name },
130 );
131 expect(fetched.name).toEqual(created.control.name);
132 expect(fetched.displayName).toEqual(created.control.displayName);
133
134 const updated = yield* stack.deploy(
135 Effect.gen(function* () {
136 const store = yield* GCP.DiscoveryEngine.CollectionsDataStore(
137 "Docs",
138 {
139 dataStoreId: created.store.dataStoreId,
140 location: "global",
141 collection: created.store.collection,
142 displayName: "docs",
143 disableCmek: true,
144 },
145 );
146 const engine = yield* GCP.DiscoveryEngine.CollectionsEngine(
147 "Search",
148 {
149 engineId: created.engine.engineId,
150 location: "global",
151 dataStoreIds: [store.dataStoreId],
152 displayName: "eng",
153 solutionType: "SOLUTION_TYPE_SEARCH",
154 industryVertical: "GENERIC",
155 },
156 );
157 const control =
158 yield* GCP.DiscoveryEngine.CollectionsDataStoresControl(
159 "Greetings",
160 {
161 dataStore: store.name,
162 controlId: created.control.controlId,
163 displayName: created.engine.engineId.slice(0, 4),
164 synonyms: ["hello", "hi"],
165 },
166 );
167 return { store, engine, control };
168 }),
169 );
170
171 expect(updated.control.name).toEqual(created.control.name);
172
173 yield* stack.destroy();
174
175 const gone = yield* waitUntilGone(created.control.name);
176 expect(gone).toEqual("gone");
177 }).pipe(logLevel, quotaTolerant),
178 {
179 tags: ["provider:gcp", "provider:gcp:discoveryengine", "live"],
180 timeout: 120_000,
181 },
182);