2026-01-22 11:45:17 +00:00

123 lines
3.5 KiB
TypeScript

import { mkdtemp, rm } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import type { Server } from 'http';
import { vi } from 'vitest';
vi.mock('../../pipeline/index.js', () => {
const progress = {
step: 'idle',
message: 'Ready',
crawlingListPagesProcessed: 0,
crawlingListPagesTotal: 0,
crawlingJobCardsFound: 0,
crawlingJobPagesEnqueued: 0,
crawlingJobPagesSkipped: 0,
crawlingJobPagesProcessed: 0,
jobsDiscovered: 0,
jobsScored: 0,
jobsProcessed: 0,
totalToProcess: 0,
};
return {
runPipeline: vi.fn().mockResolvedValue({ success: true, jobsDiscovered: 0, jobsProcessed: 0 }),
processJob: vi.fn().mockResolvedValue({ success: true }),
summarizeJob: vi.fn().mockResolvedValue({ success: true }),
generateFinalPdf: vi.fn().mockResolvedValue({ success: true }),
getPipelineStatus: vi.fn(() => ({ isRunning: false })),
subscribeToProgress: vi.fn((listener: (data: unknown) => void) => {
listener(progress);
return () => { };
}),
};
});
vi.mock('../../services/notion.js', () => ({
createNotionEntry: vi.fn(),
}));
vi.mock('../../services/manualJob.js', () => ({
inferManualJobDetails: vi.fn(),
}));
vi.mock('../../services/scorer.js', () => ({
scoreJobSuitability: vi.fn(),
}));
vi.mock('../../services/ukvisajobs.js', () => ({
fetchUkVisaJobsPage: vi.fn(),
}));
vi.mock('../../services/visa-sponsors/index.js', () => ({
getStatus: vi.fn(),
searchSponsors: vi.fn(),
getOrganizationDetails: vi.fn(),
downloadLatestCsv: vi.fn(),
calculateSponsorMatchSummary: vi.fn((results) => {
if (!results || results.length === 0) return { sponsorMatchScore: 0, sponsorMatchNames: null };
return {
sponsorMatchScore: results[0].score,
sponsorMatchNames: JSON.stringify(results.map((r: any) => r.sponsor.organisationName))
};
}),
}));
const originalEnv = { ...process.env };
export async function startServer(options?: {
env?: Record<string, string | undefined>;
}): Promise<{
server: Server;
baseUrl: string;
closeDb: () => void;
tempDir: string;
}> {
vi.resetModules();
const tempDir = await mkdtemp(join(tmpdir(), 'job-ops-api-test-'));
const envOverrides = options?.env ?? {};
process.env = {
...originalEnv,
DATA_DIR: tempDir,
NODE_ENV: 'test',
MODEL: 'test-model',
JOBSPY_SEARCH_TERMS: 'alpha|beta',
...envOverrides,
};
await import('../../db/migrate.js');
const { applyStoredEnvOverrides } = await import('../../services/envSettings.js');
const { createApp } = await import('../../app.js');
const { closeDb } = await import('../../db/index.js');
const { getPipelineStatus } = await import('../../pipeline/index.js');
vi.mocked(getPipelineStatus).mockReturnValue({ isRunning: false });
await applyStoredEnvOverrides();
const app = createApp();
const server = app.listen(0);
await new Promise<void>((resolve) => server.once('listening', () => resolve()));
const address = server.address();
if (!address || typeof address === 'string') {
throw new Error('Failed to resolve server address');
}
return {
server,
baseUrl: `http://127.0.0.1:${address.port}`,
closeDb,
tempDir,
};
}
export async function stopServer(args: {
server: Server;
closeDb: () => void;
tempDir: string;
}) {
await new Promise<void>((resolve) => args.server.close(() => resolve()));
args.closeDb();
await rm(args.tempDir, { recursive: true, force: true });
process.env = { ...originalEnv };
vi.clearAllMocks();
}