Files
santifer--career-ops/tests/providers/ibm.test.mjs
T
wehub-resource-sync d083df1fdb
CodeQL Analysis / Analyze (javascript-typescript) (push) Failing after 2s
Web CI / web typecheck + build (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
CodeQL Analysis / Analyze (go) (push) Failing after 16s
chore: import upstream snapshot with attribution
2026-07-13 12:02:43 +08:00

101 lines
4.7 KiB
JavaScript

// tests/providers/ibm.test.mjs — moved verbatim from test-all.mjs (#1440).
import { pass, fail, ROOT } from '../helpers.mjs';
import { join } from 'path';
import { pathToFileURL } from 'url';
console.log('\nProvider — ibm');
try {
const ibmModule = await import(pathToFileURL(join(ROOT, 'providers/ibm.mjs')).href);
const ibm = ibmModule.default;
const { parseIbmResponse, buildPostFilter } = ibmModule;
if (ibm.id === 'ibm') pass('ibm.id is "ibm"');
else fail(`ibm.id is ${JSON.stringify(ibm.id)}`);
// buildPostFilter — empty config yields no filter terms
if (buildPostFilter({}).bool.must.length === 0) pass('buildPostFilter({}) → no must terms');
else fail(`buildPostFilter({}) = ${JSON.stringify(buildPostFilter({}))}`);
// buildPostFilter — country + categories produce the expected facet terms
const pf = buildPostFilter({ country: 'Germany', categories: ['Software Engineering', 'Data & Analytics'] });
const countryTerm = pf.bool.must.find(m => m.term && m.term.field_keyword_05);
const catTerm = pf.bool.must.find(m => m.bool && m.bool.should);
if (countryTerm?.term.field_keyword_05 === 'Germany' && catTerm?.bool.should.length === 2) {
pass('buildPostFilter maps country → field_keyword_05 and categories → field_keyword_08 should[]');
} else {
fail(`buildPostFilter facets = ${JSON.stringify(pf)}`);
}
// buildPostFilter — sanitizes empty/non-string category entries
const sanitized = buildPostFilter({ categories: ['Valid', '', ' ', 42, null] });
const sanitizedShould = sanitized.bool.must.find(m => m.bool && m.bool.should)?.bool.should;
if (sanitizedShould?.length === 1 && sanitizedShould[0].term.field_keyword_08 === 'Valid') {
pass('buildPostFilter drops empty/non-string category entries');
} else {
fail(`buildPostFilter sanitization = ${JSON.stringify(sanitizedShould)}`);
}
// parseIbmResponse — happy path, location assembled from keyword_19 · keyword_17
const sample = {
hits: {
hits: [
{ _source: { title: 'ML Engineer', url: 'https://ibm.com/careers/1', field_keyword_19: 'Berlin, Germany', field_keyword_17: 'Hybrid' } },
{ _source: { title: 'Data Scientist', url: 'https://ibm.com/careers/2', field_keyword_19: 'Remote' } },
],
},
};
const jobs = parseIbmResponse(sample);
if (jobs.length === 2 && jobs[0].company === 'IBM') pass('parseIbmResponse extracts 2 jobs with company "IBM"');
else fail(`parseIbmResponse returned ${JSON.stringify(jobs)}`);
if (jobs[0].location === 'Berlin, Germany · Hybrid') pass('parseIbmResponse joins location · work mode');
else fail(`row 0 location = ${JSON.stringify(jobs[0]?.location)}`);
if (jobs[1].location === 'Remote') pass('parseIbmResponse omits the separator when work mode is absent');
else fail(`row 1 location = ${JSON.stringify(jobs[1]?.location)}`);
// parseIbmResponse — drops title-less, url-less, and non-http(s) entries
const dirty = parseIbmResponse({
hits: {
hits: [
{ _source: { title: '', url: 'https://ibm.com/careers/3' } },
{ _source: { title: 'No URL' } },
{ _source: { title: 'Bad scheme', url: 'ftp://ibm.com/careers/4' } },
{ _source: { title: 'Good', url: 'https://ibm.com/careers/5' } },
],
},
});
if (dirty.length === 1 && dirty[0].title === 'Good') pass('parseIbmResponse drops title-less, url-less, and non-http(s) entries');
else fail(`parseIbmResponse dirty = ${JSON.stringify(dirty)}`);
// parseIbmResponse — throws on unexpected shape (endpoint drift surfaces loudly)
let drifted = false;
try { parseIbmResponse({ results: [] }); } catch { drifted = true; }
if (drifted) pass('parseIbmResponse throws when hits.hits[] is missing');
else fail('parseIbmResponse should throw on unexpected API response shape');
// fetch() — paginates until a short page, via mock ctx
let calls = 0;
const mockCtx = {
fetchJson: async (url, opts) => {
calls++;
if (url !== 'https://www-api.ibm.com/search/api/v2') throw new Error(`unexpected url ${url}`);
if (opts?.method !== 'POST') throw new Error('Expected POST');
// Page 1: a full page (30 hits) → keep paging; page 2: short page → stop.
const n = calls === 1 ? 30 : 2;
const hits = Array.from({ length: n }, (_, i) => ({
_source: { title: `Role ${calls}-${i}`, url: `https://ibm.com/careers/${calls}-${i}` },
}));
return { hits: { hits } };
},
};
const fetched = await ibm.fetch({ name: 'IBM', ibm: { country: 'Germany' } }, mockCtx);
if (calls === 2 && fetched.length === 32) pass('ibm.fetch() paginates and stops on the first short page');
else fail(`ibm.fetch() made ${calls} calls, returned ${fetched.length} jobs`);
} catch (e) {
fail(`ibm provider tests crashed: ${e.message}`);
}