Jobber/orchestrator/src/server/api/routes/post-application-review.test.ts
Shaheer Sarfaraz 3da5ea35b4
Deduplicate shared helpers and enforce aliased imports (#228)
* Deduplicate string cleanup helpers and not-found responses

* Enforce aliased imports for infra and shared modules

* Enforce @client/@server aliases for deep relative imports

* Deduplicate visa sponsor and location filter definitions

* Use shared city filter export in extractor location checks
2026-02-22 16:13:52 +00:00

304 lines
9.4 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { Server } from "node:http";
import type {
PostApplicationMessage,
PostApplicationRouterStageTarget,
} from "@shared/types";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { startServer, stopServer } from "./test-utils";
describe.sequential("Post-Application Review Workflow API", () => {
let server: Server;
let baseUrl: string;
let closeDb: () => void;
let tempDir: string;
beforeEach(async () => {
({ server, baseUrl, closeDb, tempDir } = await startServer());
});
afterEach(async () => {
await stopServer({ server, closeDb, tempDir });
});
async function seedPendingMessage(input?: {
syncRunId?: string | null;
matchedJobId?: string | null;
stageTarget?: PostApplicationRouterStageTarget;
}): Promise<{
message: PostApplicationMessage;
jobId: string;
}> {
const { createJob } = await import("@server/repositories/jobs");
const { upsertPostApplicationMessage } = await import(
"@server/repositories/post-application-messages"
);
const job = await createJob({
source: "manual",
title: "Front End JavaScript Developer",
employer: "Roku Interactive",
jobUrl: `https://example.com/jobs/${randomUUID()}`,
});
const { message } = await upsertPostApplicationMessage({
provider: "gmail",
accountKey: "default",
integrationId: null,
syncRunId: input?.syncRunId ?? null,
externalMessageId: randomUUID(),
fromAddress: "roku@smartrecruiters.com",
fromDomain: "smartrecruiters.com",
senderName: "Roku",
subject: "Interview invitation",
receivedAt: Date.now(),
snippet: "Please schedule an interview.",
classificationLabel: "interview",
classificationConfidence: 0.97,
classificationPayload: {
reason: "High confidence",
},
relevanceLlmScore: 97,
relevanceDecision: "relevant",
matchConfidence: 97,
stageTarget: input?.stageTarget ?? "technical_interview",
messageType:
input?.stageTarget === "rejected" || input?.stageTarget === "withdrawn"
? "rejection"
: "interview",
stageEventPayload: { note: "from test" },
processingStatus: "pending_user",
matchedJobId:
input?.matchedJobId === undefined ? job.id : input.matchedJobId,
});
return { message, jobId: job.id };
}
it("lists pending inbox items", async () => {
const { message } = await seedPendingMessage();
const res = await fetch(
`${baseUrl}/api/post-application/inbox?provider=gmail&accountKey=default`,
);
const body = await res.json();
expect(res.status).toBe(200);
expect(body.ok).toBe(true);
expect(body.data.total).toBe(1);
expect(body.data.items[0].message.id).toBe(message.id);
expect(body.data.items[0].message.processingStatus).toBe("pending_user");
expect(typeof body.meta.requestId).toBe("string");
});
it("approves an inbox item and writes stage event", async () => {
const { message, jobId } = await seedPendingMessage();
const { db, schema } = await import("@server/db");
const res = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/approve`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
jobId,
decidedBy: "tester",
}),
},
);
const body = await res.json();
expect(res.status).toBe(200);
expect(body.ok).toBe(true);
expect(body.data.message.processingStatus).toBe("manual_linked");
expect(body.data.message.matchedJobId).toBe(jobId);
const stageRows = await db.select().from(schema.stageEvents);
expect(stageRows.length).toBeGreaterThan(0);
});
it("returns conflict on second approve and increments sync-run approval once", async () => {
const { startPostApplicationSyncRun, getPostApplicationSyncRunById } =
await import("@server/repositories/post-application-sync-runs");
const run = await startPostApplicationSyncRun({
provider: "gmail",
accountKey: "default",
integrationId: null,
});
const { message, jobId } = await seedPendingMessage({ syncRunId: run.id });
const firstRes = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/approve`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
jobId,
decidedBy: "tester",
}),
},
);
expect(firstRes.status).toBe(200);
const secondRes = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/approve`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
jobId,
decidedBy: "tester",
}),
},
);
const secondBody = await secondRes.json();
expect(secondRes.status).toBe(409);
expect(secondBody.ok).toBe(false);
expect(secondBody.error.code).toBe("CONFLICT");
const updatedRun = await getPostApplicationSyncRunById(run.id);
expect(updatedRun?.messagesApproved).toBe(1);
expect(updatedRun?.messagesDenied).toBe(0);
});
it("denies an inbox item as ignored", async () => {
const { message } = await seedPendingMessage();
const denyRes = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/deny`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
decidedBy: "tester",
}),
},
);
const denyBody = await denyRes.json();
expect(denyRes.status).toBe(200);
expect(denyBody.ok).toBe(true);
expect(denyBody.data.message.processingStatus).toBe("ignored");
expect(denyBody.data.message.matchedJobId).toBeNull();
});
it("counts no-suggested-match approve items as skipped, not failed", async () => {
await seedPendingMessage({ matchedJobId: null });
const res = await fetch(`${baseUrl}/api/post-application/inbox/actions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: "approve",
provider: "gmail",
accountKey: "default",
decidedBy: "tester",
}),
});
const body = await res.json();
expect(res.status).toBe(200);
expect(body.ok).toBe(true);
expect(body.data.requested).toBe(1);
expect(body.data.succeeded).toBe(0);
expect(body.data.skipped).toBe(1);
expect(body.data.failed).toBe(0);
expect(body.data.results[0].ok).toBe(false);
expect(body.data.results[0].error.code).toBe("NO_SUGGESTED_MATCH");
});
it("lists messages for a sync run", async () => {
const { startPostApplicationSyncRun } = await import(
"@server/repositories/post-application-sync-runs"
);
const run = await startPostApplicationSyncRun({
provider: "gmail",
accountKey: "default",
integrationId: null,
});
const { message } = await seedPendingMessage({ syncRunId: run.id });
const res = await fetch(
`${baseUrl}/api/post-application/runs/${run.id}/messages?provider=gmail&accountKey=default`,
);
const body = await res.json();
expect(res.status).toBe(200);
expect(body.ok).toBe(true);
expect(body.data.run.id).toBe(run.id);
expect(body.data.total).toBe(1);
expect(body.data.items[0].message.id).toBe(message.id);
});
it("approves rejected target and sets closed stage with rejected outcome", async () => {
const { message, jobId } = await seedPendingMessage({
stageTarget: "rejected",
});
const { db, schema } = await import("@server/db");
const res = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/approve`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
jobId,
}),
},
);
expect(res.status).toBe(200);
const stageRows = await db.select().from(schema.stageEvents);
expect(stageRows.at(-1)?.toStage).toBe("closed");
expect(stageRows.at(-1)?.outcome).toBe("rejected");
const jobRow = (await db.select().from(schema.jobs)).find(
(job) => job.id === jobId,
);
expect(jobRow?.outcome).toBe("rejected");
});
it("approves withdrawn target and sets closed stage with withdrawn outcome", async () => {
const { message, jobId } = await seedPendingMessage({
stageTarget: "withdrawn",
});
const { db, schema } = await import("@server/db");
const res = await fetch(
`${baseUrl}/api/post-application/inbox/${message.id}/approve`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
provider: "gmail",
accountKey: "default",
jobId,
}),
},
);
expect(res.status).toBe(200);
const stageRows = await db.select().from(schema.stageEvents);
expect(stageRows.at(-1)?.toStage).toBe("closed");
expect(stageRows.at(-1)?.outcome).toBe("withdrawn");
const jobRow = (await db.select().from(schema.jobs)).find(
(job) => job.id === jobId,
);
expect(jobRow?.outcome).toBe("withdrawn");
});
});