dedupe and tests

This commit is contained in:
DaKheera47 2026-01-20 23:27:57 +00:00
parent 6c3bb681d1
commit ad0ca7f183
5 changed files with 204 additions and 81 deletions

View File

@ -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, {

View File

@ -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

View File

@ -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(() => {

View 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']));
});
});

View File

@ -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
*/