dedupe and tests
This commit is contained in:
parent
6c3bb681d1
commit
ad0ca7f183
@ -160,20 +160,7 @@ jobsRouter.post('/:id/check-sponsor', async (req: Request, res: Response) => {
|
||||
minScore: 50,
|
||||
});
|
||||
|
||||
let sponsorMatchScore = 0;
|
||||
let sponsorMatchNames: string | null = null;
|
||||
|
||||
if (sponsorResults.length > 0) {
|
||||
const topScore = sponsorResults[0].score;
|
||||
// Get all 100% matches, or just the top match
|
||||
const perfectMatches = sponsorResults.filter(r => r.score === 100);
|
||||
const matchesToReport = perfectMatches.length >= 2
|
||||
? perfectMatches.slice(0, 2)
|
||||
: [sponsorResults[0]];
|
||||
|
||||
sponsorMatchScore = topScore;
|
||||
sponsorMatchNames = JSON.stringify(matchesToReport.map(r => r.sponsor.organisationName));
|
||||
}
|
||||
const { sponsorMatchScore, sponsorMatchNames } = visaSponsors.calculateSponsorMatchSummary(sponsorResults);
|
||||
|
||||
// Update job with sponsor match info
|
||||
const updatedJob = await jobsRepo.updateJob(job.id, {
|
||||
|
||||
@ -304,17 +304,9 @@ export async function runPipeline(config: Partial<PipelineConfig> = {}): Promise
|
||||
minScore: 50,
|
||||
});
|
||||
|
||||
if (sponsorResults.length > 0) {
|
||||
const topScore = sponsorResults[0].score;
|
||||
// Get all 100% matches, or just the top match
|
||||
const perfectMatches = sponsorResults.filter(r => r.score === 100);
|
||||
const matchesToReport = perfectMatches.length >= 2
|
||||
? perfectMatches.slice(0, 2)
|
||||
: [sponsorResults[0]];
|
||||
|
||||
sponsorMatchScore = topScore;
|
||||
sponsorMatchNames = JSON.stringify(matchesToReport.map(r => r.sponsor.organisationName));
|
||||
}
|
||||
const summary = visaSponsors.calculateSponsorMatchSummary(sponsorResults);
|
||||
sponsorMatchScore = summary.sponsorMatchScore;
|
||||
sponsorMatchNames = summary.sponsorMatchNames ?? undefined;
|
||||
}
|
||||
|
||||
// Update score and sponsor match in database
|
||||
|
||||
@ -11,6 +11,7 @@ import type { Job } from '../../shared/types.js';
|
||||
// Mock the visa-sponsors module
|
||||
vi.mock('../services/visa-sponsors/index.js', () => ({
|
||||
searchSponsors: vi.fn(),
|
||||
calculateSponsorMatchSummary: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock the scorer module
|
||||
@ -115,6 +116,7 @@ const createMockJob = (overrides: Partial<Job> = {}): Job => ({
|
||||
|
||||
describe('Sponsor Match Calculation', () => {
|
||||
let searchSponsors: ReturnType<typeof vi.fn>;
|
||||
let calculateSponsorMatchSummary: ReturnType<typeof vi.fn>;
|
||||
let scoreJobSuitability: ReturnType<typeof vi.fn>;
|
||||
let updateJob: ReturnType<typeof vi.fn>;
|
||||
let getUnscoredDiscoveredJobs: ReturnType<typeof vi.fn>;
|
||||
@ -129,6 +131,7 @@ describe('Sponsor Match Calculation', () => {
|
||||
const jobsRepo = await import('../repositories/jobs.js');
|
||||
|
||||
searchSponsors = visaSponsors.searchSponsors as ReturnType<typeof vi.fn>;
|
||||
calculateSponsorMatchSummary = visaSponsors.calculateSponsorMatchSummary as ReturnType<typeof vi.fn>;
|
||||
scoreJobSuitability = scorer.scoreJobSuitability as ReturnType<typeof vi.fn>;
|
||||
updateJob = jobsRepo.updateJob as ReturnType<typeof vi.fn>;
|
||||
getUnscoredDiscoveredJobs = jobsRepo.getUnscoredDiscoveredJobs as ReturnType<typeof vi.fn>;
|
||||
@ -138,6 +141,17 @@ describe('Sponsor Match Calculation', () => {
|
||||
scoreJobSuitability.mockResolvedValue({ score: 75, reason: 'Good match' });
|
||||
bulkCreateJobs.mockResolvedValue({ created: 0, skipped: 0 });
|
||||
updateJob.mockResolvedValue(undefined);
|
||||
|
||||
calculateSponsorMatchSummary.mockImplementation((results: any[]) => {
|
||||
if (results.length === 0) return { sponsorMatchScore: 0, sponsorMatchNames: null };
|
||||
const topScore = results[0].score;
|
||||
const perfectMatches = results.filter((r: any) => r.score === 100);
|
||||
const matchesToReport = perfectMatches.length >= 2 ? perfectMatches.slice(0, 2) : [results[0]];
|
||||
return {
|
||||
sponsorMatchScore: topScore,
|
||||
sponsorMatchNames: JSON.stringify(matchesToReport.map((r: any) => r.sponsor.organisationName)),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
107
orchestrator/src/server/services/visa-sponsors/index.test.ts
Normal file
107
orchestrator/src/server/services/visa-sponsors/index.test.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calculateSponsorMatchSummary } from './index.js';
|
||||
import type { VisaSponsorSearchResult } from '../../../shared/types.js';
|
||||
|
||||
describe('calculateSponsorMatchSummary', () => {
|
||||
it('should return default values for empty results', () => {
|
||||
const results: VisaSponsorSearchResult[] = [];
|
||||
const summary = calculateSponsorMatchSummary(results);
|
||||
|
||||
expect(summary.sponsorMatchScore).toBe(0);
|
||||
expect(summary.sponsorMatchNames).toBeNull();
|
||||
});
|
||||
|
||||
it('should report the top match when it is not a perfect match', () => {
|
||||
const results: VisaSponsorSearchResult[] = [
|
||||
{
|
||||
score: 85,
|
||||
sponsor: { organisationName: 'Tech Corp' } as any,
|
||||
matchedName: 'tech corp'
|
||||
},
|
||||
{
|
||||
score: 60,
|
||||
sponsor: { organisationName: 'Other Ltd' } as any,
|
||||
matchedName: 'other'
|
||||
}
|
||||
];
|
||||
|
||||
const summary = calculateSponsorMatchSummary(results);
|
||||
|
||||
expect(summary.sponsorMatchScore).toBe(85);
|
||||
expect(summary.sponsorMatchNames).toBe(JSON.stringify(['Tech Corp']));
|
||||
});
|
||||
|
||||
it('should report a single perfect match', () => {
|
||||
const results: VisaSponsorSearchResult[] = [
|
||||
{
|
||||
score: 100,
|
||||
sponsor: { organisationName: 'Exact Match Ltd' } as any,
|
||||
matchedName: 'exact match'
|
||||
},
|
||||
{
|
||||
score: 90,
|
||||
sponsor: { organisationName: 'Close Match' } as any,
|
||||
matchedName: 'close'
|
||||
}
|
||||
];
|
||||
|
||||
const summary = calculateSponsorMatchSummary(results);
|
||||
|
||||
expect(summary.sponsorMatchScore).toBe(100);
|
||||
expect(summary.sponsorMatchNames).toBe(JSON.stringify(['Exact Match Ltd']));
|
||||
});
|
||||
|
||||
it('should report exactly two 100% matches when two or more exist', () => {
|
||||
const results: VisaSponsorSearchResult[] = [
|
||||
{
|
||||
score: 100,
|
||||
sponsor: { organisationName: 'First PerfectMatch' } as any,
|
||||
matchedName: 'match'
|
||||
},
|
||||
{
|
||||
score: 100,
|
||||
sponsor: { organisationName: 'Second PerfectMatch' } as any,
|
||||
matchedName: 'match'
|
||||
},
|
||||
{
|
||||
score: 100,
|
||||
sponsor: { organisationName: 'Third PerfectMatch' } as any,
|
||||
matchedName: 'match'
|
||||
},
|
||||
{
|
||||
score: 50,
|
||||
sponsor: { organisationName: 'Common Co' } as any,
|
||||
matchedName: 'common'
|
||||
}
|
||||
];
|
||||
|
||||
const summary = calculateSponsorMatchSummary(results);
|
||||
|
||||
expect(summary.sponsorMatchScore).toBe(100);
|
||||
const names = JSON.parse(summary.sponsorMatchNames!);
|
||||
expect(names).toHaveLength(2);
|
||||
expect(names).toContain('First PerfectMatch');
|
||||
expect(names).toContain('Second PerfectMatch');
|
||||
expect(names).not.toContain('Third PerfectMatch');
|
||||
});
|
||||
|
||||
it('should only report the single top result if no 100% matches exist', () => {
|
||||
const results: VisaSponsorSearchResult[] = [
|
||||
{
|
||||
score: 99,
|
||||
sponsor: { organisationName: 'Almost Perfect' } as any,
|
||||
matchedName: 'almost'
|
||||
},
|
||||
{
|
||||
score: 98,
|
||||
sponsor: { organisationName: 'Second Best' } as any,
|
||||
matchedName: 'best'
|
||||
}
|
||||
];
|
||||
|
||||
const summary = calculateSponsorMatchSummary(results);
|
||||
|
||||
expect(summary.sponsorMatchScore).toBe(99);
|
||||
expect(summary.sponsorMatchNames).toBe(JSON.stringify(['Almost Perfect']));
|
||||
});
|
||||
});
|
||||
@ -410,6 +410,29 @@ export function searchSponsors(
|
||||
return results.slice(0, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate match summary from search results
|
||||
*/
|
||||
export function calculateSponsorMatchSummary(
|
||||
results: VisaSponsorSearchResult[]
|
||||
): { sponsorMatchScore: number; sponsorMatchNames: string | null } {
|
||||
if (results.length === 0) {
|
||||
return { sponsorMatchScore: 0, sponsorMatchNames: null };
|
||||
}
|
||||
|
||||
const topScore = results[0].score;
|
||||
// Get all 100% matches, or just the top match
|
||||
const perfectMatches = results.filter(r => r.score === 100);
|
||||
const matchesToReport = perfectMatches.length >= 2
|
||||
? perfectMatches.slice(0, 2)
|
||||
: [results[0]];
|
||||
|
||||
return {
|
||||
sponsorMatchScore: topScore,
|
||||
sponsorMatchNames: JSON.stringify(matchesToReport.map(r => r.sponsor.organisationName)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of the visa sponsor service
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user