mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-04 02:33:47 +10:00
Fix authentication recovery, account imports, application tracking, resume editing and exports, sharing, API contracts, provider selection, and private local attachments. Preserve authored content during PDF pagination. Update guides, generated OpenAPI output, and translation catalogs to match verified behavior and documented constraints. Validation: 1,019 tests passed; 12 database/OAuth integration tests skipped. Ten affected package typechecks, production build, Biome, and package boundaries passed.
97 lines
3.7 KiB
TypeScript
97 lines
3.7 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const dbMock = vi.hoisted(() => ({ select: vi.fn(), update: vi.fn() }));
|
|
const resumeServiceMock = vi.hoisted(() => ({
|
|
getById: vi.fn(),
|
|
create: vi.fn(),
|
|
delete: vi.fn(),
|
|
setLocked: vi.fn(),
|
|
}));
|
|
const applicationServiceMock = vi.hoisted(() => ({ update: vi.fn() }));
|
|
vi.mock("@reactive-resume/db/client", () => ({ db: dbMock }));
|
|
vi.mock("../resume/service", () => ({ resumeService: resumeServiceMock }));
|
|
vi.mock("../cover-letters/service", () => ({ linkLetterApplication: vi.fn() }));
|
|
vi.mock("../applications/service", () => ({ applicationService: applicationServiceMock }));
|
|
|
|
const { documentsService } = await import("./service");
|
|
|
|
// `select().from().where()` resolving to `rows`.
|
|
const rows = (result: unknown[]) => ({ from: () => ({ where: () => Promise.resolve(result) }) });
|
|
|
|
const updates = () => {
|
|
const set = vi.fn((_changes: Record<string, unknown>) => ({
|
|
where: () => Promise.resolve(),
|
|
}));
|
|
dbMock.update.mockReturnValue({ set });
|
|
return set;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
for (const mock of [
|
|
...Object.values(dbMock),
|
|
...Object.values(resumeServiceMock),
|
|
...Object.values(applicationServiceMock),
|
|
])
|
|
mock.mockReset();
|
|
});
|
|
|
|
describe("Trash", () => {
|
|
it("won't move a locked document, and keeps the resume's error code", async () => {
|
|
dbMock.select.mockReturnValueOnce(rows([{ isLocked: true, trashedAt: null }]));
|
|
|
|
await expect(documentsService.trash({ userId: "u1", type: "resume", id: "r1" })).rejects.toMatchObject({
|
|
code: "RESUME_LOCKED",
|
|
status: 403,
|
|
});
|
|
expect(dbMock.update).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("deletes for good only what is already in Trash", async () => {
|
|
dbMock.select.mockReturnValueOnce(rows([{ isLocked: false, trashedAt: null }]));
|
|
await expect(documentsService.purge({ userId: "u1", type: "resume", id: "r1" })).rejects.toMatchObject({
|
|
code: "BAD_REQUEST",
|
|
});
|
|
|
|
dbMock.select.mockReturnValueOnce(rows([{ isLocked: false, trashedAt: new Date() }]));
|
|
await documentsService.purge({ userId: "u1", type: "resume", id: "r1" });
|
|
expect(resumeServiceMock.delete).toHaveBeenCalledWith({ id: "r1", userId: "u1" });
|
|
});
|
|
});
|
|
|
|
describe("copyForJob", () => {
|
|
const source = {
|
|
name: "Product Designer — Lumen",
|
|
tags: ["design"],
|
|
data: { metadata: { page: { locale: "en-US" } } },
|
|
};
|
|
|
|
it("links the copy to the job, and gives the job the copy when it has no resume", async () => {
|
|
resumeServiceMock.getById.mockResolvedValueOnce(source);
|
|
dbMock.select.mockReturnValueOnce(rows([{ id: "a1", company: "Orbital", resumeId: null }]));
|
|
resumeServiceMock.create.mockResolvedValueOnce("copy");
|
|
const set = updates();
|
|
|
|
const id = await documentsService.copyForJob({ userId: "u1", resumeId: "r1", applicationId: "a1" });
|
|
|
|
expect(id).toBe("copy");
|
|
expect(resumeServiceMock.create).toHaveBeenCalledWith(
|
|
expect.objectContaining({ name: "Product Designer — Orbital", tags: ["design"] }),
|
|
);
|
|
expect(set).toHaveBeenCalledWith({ applicationId: "a1" });
|
|
expect(applicationServiceMock.update).toHaveBeenCalledWith({ userId: "u1", id: "a1", resumeId: "copy" });
|
|
});
|
|
|
|
it("leaves an application's existing resume alone", async () => {
|
|
resumeServiceMock.getById.mockResolvedValueOnce(source);
|
|
dbMock.select.mockReturnValueOnce(rows([{ id: "a1", company: "Orbital", resumeId: "base" }]));
|
|
resumeServiceMock.create.mockResolvedValueOnce("copy");
|
|
const set = updates();
|
|
|
|
await documentsService.copyForJob({ userId: "u1", resumeId: "r1", applicationId: "a1", name: "Mine" });
|
|
|
|
expect(resumeServiceMock.create).toHaveBeenCalledWith(expect.objectContaining({ name: "Mine" }));
|
|
expect(set).toHaveBeenCalledTimes(1);
|
|
expect(applicationServiceMock.update).not.toHaveBeenCalled();
|
|
});
|
|
});
|