1import * as GCP from "@/GCP";
2import { GcpEnvironment } from "@/GCP/Environment";
3import * as Test from "@/Test/Alchemy";
4import * as aiplatform from "@distilled.cloud/gcp/aiplatform_v1";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import { MinimumLogLevel } from "effect/References";
8import * as Schedule from "effect/Schedule";
9
10const { test } = Test.make({ providers: GCP.providers() });
11
12const logLevel = Effect.provideService(
13 MinimumLogLevel,
14 process.env.DEBUG ? "Debug" : "Info",
15);
16
17// Vertex AI data labeling is shut down for new projects: create fails with
18// BadRequest "Vertex DataLabelingJob is deprecated, so new project ... will
19// not be able to use the service unless they opt-in to use Labelbox human
20// labelers." Set GCP_TEST_AIPLATFORM_DATA_LABELING=1 on an opted-in project.
21const runLifecycle =
22 !process.env.FAST && !!process.env.GCP_TEST_AIPLATFORM_DATA_LABELING;
23
24const waitUntilGone = (name: string) =>
25 aiplatform.getProjectsLocationsDataLabelingJobs({ name }).pipe(
26 Effect.as("found" as const),
27 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
28 Effect.repeat({
29 schedule: Schedule.spaced("2 seconds"),
30 until: (status) => status === "gone",
31 times: 10,
32 }),
33 );
34
35test.provider(
36 "getProjectsLocationsDataLabelingJobs on a missing job fails with a typed tag",
37 (stack) =>
38 Effect.gen(function* () {
39 const { project } = yield* GcpEnvironment.current;
40 const parent = `projects/${project}/locations/us-central1`;
41 yield* stack.destroy();
42
43 const error = yield* Effect.flip(
44 aiplatform.getProjectsLocationsDataLabelingJobs({
45 name: `${parent}/dataLabelingJobs/1234567890123456789`,
46 }),
47 );
48 expect(error._tag).toEqual("NotFound");
49
50 yield* stack.destroy();
51 }).pipe(logLevel),
52 {
53 tags: ["provider:gcp", "provider:gcp:aiplatform", "live"],
54 timeout: 90_000,
55 },
56);
57
58test.provider.skipIf(!runLifecycle)(
59 "create and delete a data labeling job",
60 (stack) =>
61 Effect.gen(function* () {
62 yield* stack.destroy();
63
64 const created = yield* stack.deploy(
65 Effect.gen(function* () {
66 const dataset = yield* GCP.AIPlatform.Dataset("ToLabel", {
67 location: "us-central1",
68 displayName: "label-source",
69 metadataSchemaUri:
70 "gs://google-cloud-aiplatform/schema/dataset/metadata/image_1.0.0.yaml",
71 metadata: {},
72 labels: { env: "test" },
73 });
74 return yield* GCP.AIPlatform.DataLabelingJob("Label", {
75 location: "us-central1",
76 displayName: "alchemy-label",
77 datasets: [dataset.name],
78 labelerCount: 1,
79 instructionUri: "gs://cloud-samples-data/ai-platform/label.pdf",
80 inputsSchemaUri:
81 "gs://google-cloud-aiplatform/schema/datalabelingjob/inputs/image_classification_1.0.0.yaml",
82 inputs: { annotationSpecs: ["cat", "dog"] },
83 labels: { env: "test" },
84 });
85 }),
86 );
87
88 expect(created.name).toContain("/dataLabelingJobs/");
89 expect(created.labels).toMatchObject({ env: "test" });
90
91 const fetched = yield* aiplatform.getProjectsLocationsDataLabelingJobs({
92 name: created.name,
93 });
94 expect(fetched.name).toEqual(created.name);
95
96 yield* stack.destroy();
97
98 const gone = yield* waitUntilGone(created.name);
99 expect(gone).toEqual("gone");
100 }).pipe(logLevel),
101 {
102 tags: ["provider:gcp", "provider:gcp:aiplatform", "live"],
103 timeout: 120_000,
104 },
105);