import assert from "node:assert/strict"; import test from "node:test"; import { computeDueCutoff, runDueSoonWecomNotifications, } from "../lib/wecom-notifier-service.ts"; test("computeDueCutoff advances by dueDays and crosses month/year", () => { assert.equal(computeDueCutoff("2026-08-18", 3), "2026-08-21"); assert.equal(computeDueCutoff("2026-08-30", 3), "2026-09-02"); assert.equal(computeDueCutoff("2026-12-30", 3), "2027-01-02"); assert.equal(computeDueCutoff("2026-08-18", 0), "2026-08-18"); assert.equal(computeDueCutoff("2026-08-18", 10), "2026-08-28"); }); test("computeDueCutoff returns input unchanged when malformed", () => { assert.equal(computeDueCutoff("not-a-date", 3), "not-a-date"); }); function captureDb() { const executed = []; const db = { prepare(sql) { const statement = (values) => ({ async all() { executed.push({ sql, values }); return { results: [] }; }, async first() { executed.push({ sql, values }); return null; }, }); return { bind(...values) { return statement(values); }, ...statement([]), }; }, }; return { db, executed }; } const NOTIFY_CONFIG = { corpId: "", agentId: "", secret: "", robotWebhook: "", dueDays: 3, }; test("runDueSoonWecomNotifications filters by taskId when given", async () => { const { db, executed } = captureDb(); const now = Date.UTC(2026, 7, 18, 2, 0, 0); const summary = await runDueSoonWecomNotifications( db, NOTIFY_CONFIG, undefined, now, "task-9", ); assert.equal(executed.length, 1); assert.match(executed[0].sql, /AND t\.id = \?/); assert.deepEqual(executed[0].values, ["2026-08-21", "task-9"]); assert.equal(summary.dueSoonAttempted, 0); assert.equal(summary.digestSent, false); }); test("runDueSoonWecomNotifications keeps global scan without taskId", async () => { const { db, executed } = captureDb(); const now = Date.UTC(2026, 7, 18, 2, 0, 0); await runDueSoonWecomNotifications(db, NOTIFY_CONFIG, undefined, now); assert.equal(executed.length, 1); assert.ok(!/AND t\.id = \?/.test(executed[0].sql)); assert.deepEqual(executed[0].values, ["2026-08-21"]); });