1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as jobs from "@distilled.cloud/gcp/jobs_v4";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import { logLevel, currentProject, runLifecycle } from "./common.ts";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const waitUntilGone = (name: string) =>
12 jobs.getProjectsTenantsJobs({ name }).pipe(
13 Effect.as("found" as const),
14 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
15 Effect.repeat({
16 schedule: Schedule.spaced("1 second"),
17 until: (status) => status === "gone",
18 times: 10,
19 }),
20 );
21
22test.provider.skipIf(!runLifecycle)(
23 "getProjectsTenantsJobs on a missing job fails with a typed tag",
24 (stack) =>
25 Effect.gen(function* () {
26 const project = yield* currentProject;
27 yield* stack.destroy();
28
29 const error = yield* Effect.flip(
30 jobs.getProjectsTenantsJobs({
31 name: `projects/${project}/tenants/alchemy-missing/jobs/alchemy-missing`,
32 }),
33 );
34 expect(error._tag).toEqual("NotFound");
35
36 yield* stack.destroy();
37 }).pipe(logLevel),
38 { tags: ["provider:gcp", "provider:gcp:jobs", "live"], timeout: 90_000 },
39);
40
41test.provider.skipIf(runLifecycle)(
42 "createProjectsTenantsJobs fails with TalentDataPermissionRequired until onboarded",
43 (stack) =>
44 Effect.gen(function* () {
45 const project = yield* currentProject;
46 yield* stack.destroy();
47
48 const error = yield* Effect.flip(
49 jobs.createProjectsTenantsJobs({
50 parent: `projects/${project}/tenants/alchemy-missing`,
51 body: {
52 company: `projects/${project}/tenants/alchemy-missing/companies/alchemy-missing`,
53 title: "Alchemy Probe",
54 description: "probe",
55 requisitionId: "alchemy-job-probe",
56 },
57 }),
58 );
59 expect(error._tag).toEqual("TalentDataPermissionRequired");
60
61 yield* stack.destroy();
62 }).pipe(logLevel),
63 { tags: ["provider:gcp", "provider:gcp:jobs", "live"], timeout: 90_000 },
64);
65
66test.provider.skipIf(!runLifecycle)(
67 "create, update, and delete a job",
68 (stack) =>
69 Effect.gen(function* () {
70 yield* stack.destroy();
71
72 const created = yield* stack.deploy(
73 Effect.gen(function* () {
74 const tenant = yield* GCP.Jobs.Tenant("AcmeJobs", {
75 externalId: "acme-job-tenant",
76 });
77 const company = yield* GCP.Jobs.TenantsCompany("Hiring", {
78 parent: tenant.name,
79 externalId: "acme-hiring",
80 displayName: "Acme Hiring",
81 });
82 const job = yield* GCP.Jobs.TenantsJob("Engineer", {
83 parent: tenant.name,
84 company: company.name,
85 title: "Software Engineer",
86 description: "Build Cloud Talent integrations.",
87 requisitionId: "eng-001",
88 addresses: ["Mountain View, CA"],
89 employmentTypes: ["FULL_TIME"],
90 applicationInfo: { uris: ["https://www.example.com/apply"] },
91 });
92 return { tenant, company, job };
93 }),
94 );
95
96 expect(created.job.name).toContain("/jobs/");
97 expect(created.job.parent).toEqual(created.tenant.name);
98 expect(created.job.company).toEqual(created.company.name);
99 expect(created.job.title).toEqual("Software Engineer");
100 expect(created.job.description).toEqual(
101 "Build Cloud Talent integrations.",
102 );
103 expect(created.job.requisitionId).toEqual("eng-001");
104
105 const fetched = yield* jobs.getProjectsTenantsJobs({
106 name: created.job.name,
107 });
108 expect(fetched.name).toEqual(created.job.name);
109 expect(fetched.description).toEqual("Build Cloud Talent integrations.");
110 expect(fetched.requisitionId).toContain("[alchemy ");
111 expect(fetched.title).toEqual("Software Engineer");
112
113 const updated = yield* stack.deploy(
114 Effect.gen(function* () {
115 const tenant = yield* GCP.Jobs.Tenant("AcmeJobs", {
116 tenantId: created.tenant.tenantId,
117 externalId: "acme-job-tenant",
118 });
119 const company = yield* GCP.Jobs.TenantsCompany("Hiring", {
120 parent: tenant.name,
121 companyId: created.company.companyId,
122 externalId: "acme-hiring",
123 displayName: "Acme Hiring",
124 });
125 const job = yield* GCP.Jobs.TenantsJob("Engineer", {
126 parent: tenant.name,
127 company: company.name,
128 jobId: created.job.jobId,
129 title: "Staff Software Engineer",
130 description: "Build Cloud Talent integrations.",
131 requisitionId: "eng-001",
132 addresses: ["Mountain View, CA"],
133 employmentTypes: ["FULL_TIME"],
134 promotionValue: 1,
135 applicationInfo: { uris: ["https://www.example.com/apply"] },
136 });
137 return { tenant, company, job };
138 }),
139 );
140
141 expect(updated.job.name).toEqual(created.job.name);
142 expect(updated.job.title).toEqual("Staff Software Engineer");
143 expect(updated.job.promotionValue).toEqual(1);
144
145 yield* stack.destroy();
146
147 const gone = yield* waitUntilGone(created.job.name);
148 expect(gone).toEqual("gone");
149 }).pipe(logLevel),
150 { tags: ["provider:gcp", "provider:gcp:jobs", "live"], timeout: 90_000 },
151);