bidaangmamamayan/tests/date-test.mjs
Ryan 6b06e08c08 BidaAngMamayan: public supporter tribute news desk for Senator BAM Aquino
Forked from Forthena (PERSONAL/forthena) and retargeted:
- NO login, NO LLM: the edition is public and the ranking is deterministic
  (stories that name Sen. BAM lead; freshest first) — Daniel: no
  like/dislike or store profile for visitors.
- Crawler scoped to BAM: Bing news keyword queries + Inquirer/Rappler
  fallback wires, with a two-tier relevance gate (named-BAM always;
  Aquino+legislative-context only from fallback) so op-eds and non-Bam
  senators are dropped.
- Desks: BAM (lead), Gaming, Education, Politics, Energy, PH, Business.
- Community board /board: 'Thank you Senator BAM' — nickname-only posts,
  HTML-stripped, URL-only rejected, IP rate-limited (3/10min, 20/day).
- Yellow-gamer theme (Orbitron/Rajdhani/JetBrains Mono, dark + gold,
  HUD corners), supporter disclaimer in header and footer.
- Tests: category/date/relevance/text suites + Mongo board suite +
  full HTTP e2e (all passing).

Infra: 66.181.46.132 (bidaangmamamayan.com), local Mongo 7.0,
nginx + certbot TLS, PM2 bama (:3000) + 15-min crawl cron.
2026-09-15 08:37:03 +00:00

31 lines
1.9 KiB
JavaScript

// Tests the crawler's headline-date scanner (extracted from module source so we
// don't widen the module's public surface just for tests).
import { readFileSync } from "fs";
const src = readFileSync(new URL("../lib/crawl.js", import.meta.url), "utf8");
const monthsLine = /const MONTHS = .*;\n/.exec(src);
const fnBlock = /function headlineDateAge[\s\S]*?\n\}\n/.exec(src);
if (!monthsLine || !fnBlock) { console.log("FAIL could not locate headlineDateAge in source"); process.exit(1); }
const fn = new Function(monthsLine[0] + fnBlock[0] + "; return headlineDateAge;")();
const now = new Date("2026-08-30T12:00:00Z").getTime();
let fails = 0;
const ok = (c, msg) => { if (!c) { fails++; console.log(" FAIL " + msg); } else console.log(" ok " + msg); };
console.log("headlineDateAge:");
const march = fn("Peso weakens to 59.735 on March 13, 2026", now);
ok(march !== null && march > 160, `"March 13, 2026" is ~5 months old (got ${march?.toFixed(0)}d)`);
const yest = fn("On Aug 29, the monetary board decided", now);
ok(yest !== null && yest >= 0 && yest < 2, `"Aug 29" reads as ~1 day (got ${yest?.toFixed(1)}d)`);
const july = fn("Inflation quickens in July 2026", now);
ok(july !== null && july > 25 && july < 90, `"July 2026" is a month+ old (got ${july?.toFixed(0)}d)`);
ok(fn("Diesel prices climb for third straight week", now) === null, "no month-date in text -> null (pubDate governs)");
ok(fn("2025 inflation sinks to nine-year low", now) === null, "bare year (no month) -> null, conservative");
const dayfirst = fn("recorded 13 March 2026 by the BSP", now);
ok(dayfirst !== null && dayfirst > 160, `"13 March 2026" day-first form also caught (got ${dayfirst?.toFixed(0)}d)`);
const future = fn("Summit begins September 15, 2026", now);
ok(future === null, "a date in the future is ignored, not treated as ancient");
console.log(fails ? `\n${fails} FAILED` : "\nall passed");
process.exit(fails ? 1 : 0);