Files
santifer--career-ops/tests/providers/hackernews.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

204 lines
8.3 KiB
JavaScript

// tests/providers/hackernews.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 — hackernews');
try {
const hackernewsModule = await import(pathToFileURL(join(ROOT, 'providers/hackernews.mjs')).href);
const hn = hackernewsModule.default;
const { parseHnComment, resolveLatestThreadId } = hackernewsModule;
// resolveLatestThreadId ─ happy path
const fakeSearch = {
hits: [
{ objectID: '99999999', title: 'Ask HN: Who is hiring? (June 2025)' },
{ objectID: '88888888', title: 'Ask HN: Who wants to be hired? (June 2025)' },
],
};
if (resolveLatestThreadId(fakeSearch) === '99999999') {
pass('resolveLatestThreadId picks the first matching "Who is hiring?" hit');
} else {
fail(`resolveLatestThreadId returned ${resolveLatestThreadId(fakeSearch)}`);
}
// resolveLatestThreadId ─ no matching hit
const noMatch = { hits: [{ objectID: '11111', title: 'Ask HN: Who wants to be hired?' }] };
if (resolveLatestThreadId(noMatch) === null) {
pass('resolveLatestThreadId returns null when no thread matches');
} else {
fail('resolveLatestThreadId should return null for non-hiring threads');
}
// resolveLatestThreadId ─ bad input
if (resolveLatestThreadId(null) === null && resolveLatestThreadId({}) === null) {
pass('resolveLatestThreadId handles null / empty input gracefully');
} else {
fail('resolveLatestThreadId should return null for bad input');
}
// parseHnComment ─ full pipe-delimited format
const fullFmt = 'Acme Corp | Senior Engineer | Remote | https://acme.com/jobs/123\nWe are hiring…';
const parsed = parseHnComment(fullFmt, 'https://news.ycombinator.com/item?id=1');
if (parsed && parsed.company === 'Acme Corp' && parsed.location === 'Remote') {
pass('parseHnComment extracts company and location from pipe-delimited header');
} else {
fail(`parseHnComment pipe format: ${JSON.stringify(parsed)}`);
}
if (parsed && parsed.url === 'https://acme.com/jobs/123') {
pass('parseHnComment extracts URL from comment text');
} else {
fail(`parseHnComment url: ${JSON.stringify(parsed?.url)}`);
}
// parseHnComment ─ URL in first line stripped from title
if (parsed && !parsed.title.includes('https://')) {
pass('parseHnComment strips URLs from the title field');
} else {
fail(`parseHnComment title still contains URL: ${JSON.stringify(parsed?.title)}`);
}
// parseHnComment ─ free-form (no pipes, no URL in text)
const freeFmt = 'Looking for a Rails dev, anywhere, part-time.';
const parsedFree = parseHnComment(freeFmt, 'https://news.ycombinator.com/item?id=1');
if (parsedFree && parsedFree.title === freeFmt && parsedFree.url === 'https://news.ycombinator.com/item?id=1') {
pass('parseHnComment handles free-form comment, falls back to thread URL');
} else {
fail(`parseHnComment free-form: ${JSON.stringify(parsedFree)}`);
}
if (parsedFree && parsedFree.company === '' && parsedFree.location === '') {
pass('parseHnComment leaves company/location empty for free-form comments');
} else {
fail(`parseHnComment free-form company/location: ${JSON.stringify(parsedFree)}`);
}
// parseHnComment ─ HTML entities and tags
const htmlFmt = '<p>Beta &amp; Co | Staff SWE | New York, NY | <a href="https://beta.io/jobs">https://beta.io/jobs</a></p>';
const parsedHtml = parseHnComment(htmlFmt, '');
if (parsedHtml && parsedHtml.company === 'Beta & Co') {
pass('parseHnComment decodes HTML entities (company name)');
} else {
fail(`parseHnComment HTML entity decode: ${JSON.stringify(parsedHtml?.company)}`);
}
if (parsedHtml && parsedHtml.url === 'https://beta.io/jobs') {
pass('parseHnComment extracts href URL from anchor tags');
} else {
fail(`parseHnComment anchor URL: ${JSON.stringify(parsedHtml?.url)}`);
}
// parseHnComment ─ <p> paragraph body must not bleed into location field
// Real HN posts often have "Company | Role | Location<p>Body text..." where the
// second <p> paragraph (job description) runs on without a 4th pipe segment.
// The parser must convert block tags to newlines so parts[2] stays clean.
const bleedFmt = '<p>Linear | Product Engineer | Remote<p>We are hiring at https://linear.app/careers/pe-2025';
const parsedBleed = parseHnComment(bleedFmt, 'https://news.ycombinator.com/item?id=1');
if (parsedBleed && parsedBleed.location === 'Remote') {
pass('parseHnComment location does not bleed body paragraph text (block tag newline fix)');
} else {
fail(`parseHnComment location bleed: ${JSON.stringify(parsedBleed?.location)}`);
}
if (parsedBleed && parsedBleed.url === 'https://linear.app/careers/pe-2025') {
pass('parseHnComment finds URL in body paragraph when absent from header line');
} else {
fail(`parseHnComment body-paragraph URL: ${JSON.stringify(parsedBleed?.url)}`);
}
// parseHnComment ─ deleted / empty comments return null
if (parseHnComment('', '') === null && parseHnComment(null, '') === null) {
pass('parseHnComment returns null for empty / null input');
} else {
fail('parseHnComment should return null for empty/null input');
}
// provider.fetch() — integration with mock ctx
const FAKE_THREAD_ID = '42424242';
const FAKE_THREAD_URL = `https://news.ycombinator.com/item?id=${FAKE_THREAD_ID}`;
const fakeSearchResp = {
hits: [{ objectID: FAKE_THREAD_ID, title: 'Ask HN: Who is hiring? (June 2025)' }],
};
const fakeItemResp = {
id: FAKE_THREAD_ID,
children: [
{
objectID: 'c1',
text: 'Startup XYZ | Backend Engineer | San Francisco | https://xyz.io/careers',
created_at: '2025-06-01T10:00:00Z',
},
{ objectID: 'c2', deleted: true, text: '' }, // deleted — should be skipped
{ objectID: 'c3', text: '' }, // empty — should be skipped
{
objectID: 'c4',
text: 'Freelance gig, no URL here, DM me.', // no URL — falls back to thread URL
created_at: '2025-06-01T11:00:00Z',
},
],
};
let searchFetched = false;
let itemFetched = false;
const mockCtx = {
async fetchJson(url, _opts) {
if (url.includes('search_by_date')) { searchFetched = true; return fakeSearchResp; }
if (url.includes(`/items/${FAKE_THREAD_ID}`)) { itemFetched = true; return fakeItemResp; }
throw new Error(`hackernews mock: unexpected fetch ${url}`);
},
};
const jobs = await hn.fetch({ name: 'HN Hiring', provider: 'hackernews' }, mockCtx);
if (searchFetched && itemFetched) {
pass('hackernews.fetch() calls search API then items API');
} else {
fail(`hackernews.fetch() API calls: search=${searchFetched} item=${itemFetched}`);
}
if (jobs.length === 2) {
pass('hackernews.fetch() returns 2 jobs (skips deleted + empty comments)');
} else {
fail(`hackernews.fetch() returned ${jobs.length} jobs (expected 2)`);
}
if (jobs[0]?.company === 'Startup XYZ' && jobs[0]?.location === 'San Francisco') {
pass('hackernews.fetch() maps pipe-delimited company and location');
} else {
fail(`hackernews.fetch() row 0: ${JSON.stringify(jobs[0])}`);
}
if (jobs[0]?.url === 'https://xyz.io/careers') {
pass('hackernews.fetch() maps job URL from comment text');
} else {
fail(`hackernews.fetch() row 0 url: ${JSON.stringify(jobs[0]?.url)}`);
}
if (jobs[0]?.postedAt === Date.parse('2025-06-01T10:00:00Z')) {
pass('hackernews.fetch() parses created_at to postedAt epoch ms');
} else {
fail(`hackernews.fetch() postedAt: ${JSON.stringify(jobs[0]?.postedAt)}`);
}
if (jobs[1]?.url === FAKE_THREAD_URL) {
pass('hackernews.fetch() falls back to thread URL for comments with no link');
} else {
fail(`hackernews.fetch() fallback url: ${JSON.stringify(jobs[1]?.url)}`);
}
// provider.fetch() — throws when no thread found
let threw = false;
try {
await hn.fetch({ name: 'HN' }, {
async fetchJson() { return { hits: [] }; },
});
} catch (e) {
threw = true;
}
if (threw) {
pass('hackernews.fetch() throws when "Who is hiring?" thread not found');
} else {
fail('hackernews.fetch() should throw when no matching thread found');
}
} catch (e) {
fail(`hackernews provider tests crashed: ${e.message}`);
}