pdf tailoring tets
This commit is contained in:
parent
22f4735674
commit
0ad4b4c4f0
165
orchestrator/src/server/services/pdf-tailoring.test.ts
Normal file
165
orchestrator/src/server/services/pdf-tailoring.test.ts
Normal file
@ -0,0 +1,165 @@
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import * as projectSelection from './projectSelection.js';
|
||||
|
||||
// Define mock data in hoisted block
|
||||
const { mocks, mockProfile } = vi.hoisted(() => {
|
||||
const profile = {
|
||||
sections: {
|
||||
summary: { content: 'Original Summary' },
|
||||
skills: { items: ['Original Skill'] },
|
||||
projects: {
|
||||
items: [
|
||||
// Start with visible=true to test if they get hidden
|
||||
{ id: 'p1', name: 'Project 1', visible: true },
|
||||
{ id: 'p2', name: 'Project 2', visible: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
basics: { headline: 'Original Headline' }
|
||||
};
|
||||
|
||||
return {
|
||||
mockProfile: profile,
|
||||
mocks: {
|
||||
readFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
mkdir: vi.fn().mockResolvedValue(undefined),
|
||||
access: vi.fn().mockResolvedValue(undefined),
|
||||
unlink: vi.fn().mockResolvedValue(undefined),
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Configure base mock implementations
|
||||
mocks.readFile.mockResolvedValue(JSON.stringify(mockProfile));
|
||||
mocks.writeFile.mockResolvedValue(undefined);
|
||||
|
||||
vi.mock('fs/promises', async () => {
|
||||
return {
|
||||
default: mocks,
|
||||
...mocks
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: vi.fn().mockReturnValue(true),
|
||||
default: { existsSync: vi.fn().mockReturnValue(true) }
|
||||
}));
|
||||
|
||||
vi.mock('../repositories/settings.js', () => ({
|
||||
getSetting: vi.fn().mockResolvedValue(null),
|
||||
}));
|
||||
|
||||
vi.mock('./projectSelection.js', () => ({
|
||||
pickProjectIdsForJob: vi.fn().mockResolvedValue([]),
|
||||
}));
|
||||
|
||||
vi.mock('./resumeProjects.js', () => ({
|
||||
extractProjectsFromProfile: vi.fn().mockReturnValue({
|
||||
catalog: [],
|
||||
selectionItems: [
|
||||
{ id: 'p1', name: 'Project 1' },
|
||||
{ id: 'p2', name: 'Project 2' }
|
||||
]
|
||||
}),
|
||||
resolveResumeProjectsSettings: vi.fn().mockReturnValue({
|
||||
resumeProjects: {
|
||||
lockedProjectIds: [],
|
||||
aiSelectableProjectIds: ['p1', 'p2'],
|
||||
maxProjects: 3
|
||||
}
|
||||
})
|
||||
}));
|
||||
|
||||
vi.mock('child_process', () => ({
|
||||
spawn: vi.fn().mockImplementation(() => ({
|
||||
stdout: { on: vi.fn() },
|
||||
stderr: { on: vi.fn() },
|
||||
on: vi.fn().mockImplementation((event, cb) => {
|
||||
if (event === 'close') cb(0);
|
||||
return {};
|
||||
}),
|
||||
})),
|
||||
default: {
|
||||
spawn: vi.fn().mockImplementation(() => ({
|
||||
stdout: { on: vi.fn() },
|
||||
stderr: { on: vi.fn() },
|
||||
on: vi.fn().mockImplementation((event, cb) => {
|
||||
if (event === 'close') cb(0);
|
||||
return {};
|
||||
}),
|
||||
}))
|
||||
}
|
||||
}));
|
||||
|
||||
import { generatePdf } from './pdf.js';
|
||||
|
||||
describe('PDF Service Tailoring Logic', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Reset default behaviors
|
||||
mocks.readFile.mockResolvedValue(JSON.stringify(mockProfile));
|
||||
mocks.writeFile.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('should use provided selectedProjectIds and BYPASS AI selection', async () => {
|
||||
const tailoredContent = { summary: 'New Sum', headline: 'New Head', skills: [] };
|
||||
|
||||
await generatePdf('job-1', tailoredContent, 'Job Desc', 'base.json', 'p2');
|
||||
|
||||
// 1. pickProjectIdsForJob should NOT be called
|
||||
expect(projectSelection.pickProjectIdsForJob).not.toHaveBeenCalled();
|
||||
|
||||
// 2. Verify writeFile content
|
||||
expect(mocks.writeFile).toHaveBeenCalled();
|
||||
const callArgs = mocks.writeFile.mock.calls[0];
|
||||
const savedResumeJson = JSON.parse(callArgs[1] as string);
|
||||
|
||||
const projects = savedResumeJson.sections.projects.items;
|
||||
const p1 = projects.find((p: any) => p.id === 'p1');
|
||||
const p2 = projects.find((p: any) => p.id === 'p2');
|
||||
|
||||
expect(p2.visible).toBe(true);
|
||||
expect(p1.visible).toBe(false);
|
||||
|
||||
// 3. Verify Summary Update
|
||||
const summary = savedResumeJson.sections.summary.content;
|
||||
expect(summary).toBe('New Sum');
|
||||
});
|
||||
|
||||
it('should handle comma-separated project IDs correctly', async () => {
|
||||
await generatePdf('job-2', {}, 'desc', 'base.json', 'p1, p2 ');
|
||||
|
||||
expect(mocks.writeFile).toHaveBeenCalled();
|
||||
const callArgs = mocks.writeFile.mock.calls[0];
|
||||
const savedResumeJson = JSON.parse(callArgs[1] as string);
|
||||
const projects = savedResumeJson.sections.projects.items;
|
||||
|
||||
expect(projects.find((p: any) => p.id === 'p1').visible).toBe(true);
|
||||
expect(projects.find((p: any) => p.id === 'p2').visible).toBe(true);
|
||||
});
|
||||
|
||||
it('should fall back to AI selection if selectedProjectIds is null/undefined', async () => {
|
||||
// Setup AI selection mock for this test
|
||||
vi.mocked(projectSelection.pickProjectIdsForJob).mockResolvedValue(['p1']);
|
||||
|
||||
await generatePdf('job-3', {}, 'desc', 'base.json', undefined);
|
||||
|
||||
expect(projectSelection.pickProjectIdsForJob).toHaveBeenCalled();
|
||||
|
||||
expect(mocks.writeFile).toHaveBeenCalled();
|
||||
const callArgs = mocks.writeFile.mock.calls[0];
|
||||
const savedResumeJson = JSON.parse(callArgs[1] as string);
|
||||
|
||||
const p1 = savedResumeJson.sections.projects.items.find((p: any) => p.id === 'p1');
|
||||
const p2 = savedResumeJson.sections.projects.items.find((p: any) => p.id === 'p2');
|
||||
|
||||
expect(p1.visible).toBe(true);
|
||||
expect(p2.visible).toBe(false);
|
||||
|
||||
const visibleCount = savedResumeJson.sections.projects.items.filter((p:any) => p.visible).length;
|
||||
expect(visibleCount).toBe(1);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user