mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
fix(jobbank-search): degrade an unparseable pubDate to a null date instead of crashing the search (#416) (#417)
new Date(<unparseable>) yields an Invalid Date whose toISOString()
throws RangeError, and normalizeSearchItem runs inside an unguarded
items.map(), so one malformed RSS item killed the entire search with
{"error": "Invalid Date", "code": "API_ERROR"} and exit 1. The
un-CDATA'd fallback capture in parseRssItems can deliver exactly such a
value. An unparseable pubDate now degrades to the same shape as an
absent one (posted "", date null); every other item survives. Three
new cases pin the malformed shapes, each failing on the unfixed code.
This commit is contained in:
@@ -5,7 +5,13 @@ import { rssFetch, fetchWithUA, writeError, parseRssDescription, extractJobIdFro
|
||||
export function normalizeSearchItem(item: RssItem): Record<string, unknown> {
|
||||
const parsed = parseRssDescription(item.description)
|
||||
const id = extractJobIdFromUrl(item.link)
|
||||
const posted = item.pubDate ? new Date(item.pubDate).toISOString() : ""
|
||||
// Guard the parse: new Date(<unparseable>) is an Invalid Date whose
|
||||
// toISOString() throws RangeError, and this runs inside an unguarded
|
||||
// items.map() - one bad feed item would kill the whole search as
|
||||
// API_ERROR (#416). An unparseable pubDate degrades to the same shape
|
||||
// as an absent one: posted "", date null.
|
||||
const parsedDate = item.pubDate ? new Date(item.pubDate) : null
|
||||
const posted = parsedDate && !Number.isNaN(parsedDate.getTime()) ? parsedDate.toISOString() : ""
|
||||
return {
|
||||
id,
|
||||
title: item.title,
|
||||
|
||||
@@ -27,6 +27,21 @@ describe("Jobbank search normalization", () => {
|
||||
expect(result.date).toBeNull();
|
||||
});
|
||||
|
||||
// A present-but-unparseable pubDate must degrade to the same null-date shape
|
||||
// as an absent one, never throw: toISOString() on an Invalid Date raises
|
||||
// RangeError, and normalizeSearchItem runs inside an unguarded items.map(),
|
||||
// so one bad feed item killed the whole search as API_ERROR (#416). The
|
||||
// un-CDATA'd fallback capture in parseRssItems can deliver exactly such a
|
||||
// value.
|
||||
for (const bad of ["date unavailable", "2026-09-02T08:00:00+02:00x", "I går"]) {
|
||||
test(`emits a null date instead of throwing on unparseable pubDate ${JSON.stringify(bad)}`, () => {
|
||||
const result = normalizeSearchItem({ ...rssItem(), pubDate: bad });
|
||||
|
||||
expect(result.posted).toBe("");
|
||||
expect(result.date).toBeNull();
|
||||
});
|
||||
}
|
||||
|
||||
test("keeps the native fields alongside the contract date (additive)", () => {
|
||||
const result = normalizeSearchItem(rssItem());
|
||||
|
||||
|
||||
@@ -41,6 +41,16 @@ per-file diff commands.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`jobbank-search` no longer dies over one malformed feed date** (#416) - `new Date()`
|
||||
on a present-but-unparseable `pubDate` yields an Invalid Date whose `toISOString()`
|
||||
throws `RangeError`, and `normalizeSearchItem` runs inside an unguarded `items.map()`,
|
||||
so a single bad RSS item killed the entire search with `{"error": "Invalid Date",
|
||||
"code": "API_ERROR"}` and exit 1 - a whole default-ON portal lost to one item, with
|
||||
the error pointing at the API. The un-CDATA'd fallback capture in `parseRssItems` can
|
||||
deliver exactly such a value. An unparseable `pubDate` now degrades to the same shape
|
||||
as an absent one (`posted` empty, `date: null`, per the `seen_jobs.json` contract that
|
||||
#391 put this field on), and every other item survives. Pinned by three new cases in
|
||||
`search-normalization.test.ts`, each verified to fail on the unfixed code.
|
||||
- **`linkedin-search` rejects fractional numeric flags instead of silently changing
|
||||
the query** (#371) - bare `parseInt` truncated values before validation, so
|
||||
`--jobage 0.5` became `0` and silently omitted LinkedIn's `f_TPR` freshness filter
|
||||
|
||||
Reference in New Issue
Block a user