Files
ihaleist/tests/feed-deadlines.test.mjs

92 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, after } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { tenderSnapshot } from '../src/services/tenderState.js';
const dir = mkdtempSync(join(tmpdir(), 'ihaleist-feed-'));
process.env.IHALEIST_USER_DATA_DIR = dir;
const store = await import('../server/user-store.mjs');
const { ihaleFiltrele, yaklasanIhaleler, trTarih } = await import('../server/feeds-mcp.mjs');
const { briefMetni, briefHtml, briefScheduleKey } = await import('../server/brief.mjs');
after(() => rmSync(dir, { recursive: true, force: true }));
const email = 'dates@example.test';
const rows = [
{ id:'past', ikn:'2026/1', tarih:'06.09.2026', saat:'00:00' },
{ id:'future', ikn:'2026/2', tarih:'06.09.2026', saat:'10:00' },
{ id:'unknown', ikn:'2026/3', tarih:'06.09.2026', saat:'' },
{ id:'invalid', ikn:'2026/4', tarih:'31.09.2026', saat:'10:00' },
{ id:'unfollowed', ikn:'2026/5', tarih:'07.09.2026', saat:'10:00' },
];
const now = new Date('2026-09-05T21:30:00Z'); // Istanbul Sep 6, 00:30; UTC still Sep 5.
test('RSS and MCP use Istanbul deadlines regardless of server timezone', () => {
const previous = process.env.TZ;
try {
for (const zone of ['UTC', 'America/Los_Angeles', 'Europe/Istanbul']) {
process.env.TZ = zone;
assert.deepEqual(ihaleFiltrele(rows, {}, now).map(t => t.id), ['future','unknown','invalid','unfollowed']);
assert.equal(trTarih('6/9/2026').toISOString(), '2026-09-05T21:00:00.000Z');
assert.equal(trTarih('31.09.2026'), null);
assert.equal(trTarih('06.09.2026 garbage'), null);
}
} finally {
if (previous === undefined) delete process.env.TZ;
else process.env.TZ = previous;
}
});
test('upcoming API reads saved follows, rejects stale unfollows and includes out-of-pool snapshots', () => {
store.setData(email, 'takip', ['2026/5']);
for (const t of rows) store.saveTenderState(email, t.ikn, { takipte: t.id !== 'unfollowed' }, t, 0);
const outside = { id:'outside', ikn:'2026/6', tarih:'07.09.2026', saat:'09:00' };
store.saveTenderState(email, outside.ikn, { takipte:true }, outside, 0);
const result = yaklasanIhaleler(email, rows, 14, now);
assert.deepEqual(result.map(t => t.ikn), ['2026/2','2026/3','2026/6']);
assert.equal(result[0].kalan_gun, 0);
assert.equal(result[0].son_teklif_zamani, '2026-09-06T07:00:00.000Z');
assert.equal(result[1].saat_dogrulanmali, true);
assert.equal(result[1].son_teklif_zamani, null);
assert.equal(result[2].kalan_gun, 1);
assert.deepEqual(yaklasanIhaleler('other@example.test', rows, 14, now), []);
assert.ok(!store.getTrackedTenderKeys(email).includes('2026/5'));
const brief = { hafta:'Test haftası', yaklasan:result, sonuclar:[] };
for (const output of [briefMetni(brief), briefHtml(brief)]) {
assert.match(output, /10:00 \(Türkiye saati\)/);
assert.match(output, /saat doğrulanmalı/);
}
});
test('weekly notification schedule starts at Monday 08:00 Istanbul', () => {
assert.equal(briefScheduleKey(new Date('2026-09-07T04:59:59Z')), null);
assert.equal(briefScheduleKey(new Date('2026-09-07T05:00:00Z')), '2026-8-7');
assert.equal(briefScheduleKey(new Date('2026-09-07T20:59:59Z')), '2026-8-7');
assert.equal(briefScheduleKey(new Date('2026-09-07T21:00:00Z')), null);
});
test('closed tenders stay out of open feeds and weekly upcoming items, including saved snapshots', () => {
const owner = 'closed-feed@example.test';
const base = { tarih:'07.09.2026', saat:'10:00' };
const closed = ['İptal', 'Sonuçlandı', 'Sözleşme İmzalandı'].map((durum, i) =>
({ ...base, id:`closed-${i}`, ikn:`2026/closed-${i}`, durum }));
closed.push({ ...base, id:'winner', ikn:'2026/winner', kazananFirma:'Test kazananı' });
const open = { ...base, id:'open', ikn:'2026/open', durum:'Açık' };
const unknownTime = { ...open, id:'unknown-time', ikn:'2026/unknown-time', saat:'' };
const all = [...closed, open, unknownTime];
for (const tender of all) store.saveTenderState(owner, tender.ikn, { takipte:true }, tenderSnapshot(tender), 0);
assert.deepEqual(ihaleFiltrele(all, {}, now).map(t => t.id), ['open', 'unknown-time']);
assert.equal(ihaleFiltrele(all, { sadeceAcik:false }, now).length, all.length);
// Status updates in the current source must override a previously open saved snapshot.
const updated = { ...open, durum:'İptal' };
const upcoming = yaklasanIhaleler(owner, [updated, unknownTime], 14, now);
assert.deepEqual(upcoming.map(t => t.ikn), ['2026/unknown-time']);
assert.equal(upcoming[0].saat_dogrulanmali, true);
for (const output of [briefMetni, briefHtml]) {
const body = output({ hafta:'Test haftası', yaklasan:upcoming, sonuclar:[] });
for (const tender of [...closed, open]) assert.ok(!body.includes(tender.ikn));
assert.match(body, /saat doğrulanmalı/);
}
});