import { test } from 'node:test' import assert from 'node:assert/strict' import { BUNDESLAENDER, istFeiertag, feiertagName, feiertageImZeitraum, } from '../server/services/termine/feiertage.js' const eintrag = (liste, tag) => liste.find((e) => e.tag === tag) test('Tag der Deutschen Einheit hat einen Namen', () => { assert.equal(feiertagName('2026-10-03', 'RP'), 'Tag der Deutschen Einheit') }) test('normaler Tag hat keinen Namen', () => { assert.equal(feiertagName('2026-10-05', 'RP'), null) }) test('ungültiges Land wirft nicht', () => { assert.equal(feiertagName('2026-10-03', 'XX'), null) assert.equal(feiertagName('2026-10-03', 'rp'), null, 'Kuerzel nur in Grossbuchstaben wie BUNDESLAENDER') assert.equal(feiertagName('2026-10-03', ''), null) assert.equal(feiertagName('2026-10-03', undefined), null) assert.deepEqual(feiertageImZeitraum('2026-10-01', '2026-10-31', 'XX'), []) assert.deepEqual(feiertageImZeitraum('2026-10-01', '2026-10-31', undefined), []) }) test('ungültiger Tag wirft nicht', () => { for (const tag of [undefined, null, 5, '', '2026-10-3', '03.10.2026', '2026-13-45', '0000-00-00']) { assert.equal(feiertagName(tag, 'RP'), null, String(tag)) } assert.deepEqual(feiertageImZeitraum('kaputt', '2026-10-31', 'RP'), []) assert.deepEqual(feiertageImZeitraum('2026-10-01', null, 'RP'), []) assert.deepEqual(feiertageImZeitraum(undefined, undefined, 'RP'), []) }) test('Heiligabend und Silvester sind „pruefen“', () => { const liste = feiertageImZeitraum('2026-12-20', '2027-01-02', 'RP') assert.deepEqual(eintrag(liste, '2026-12-24'), { tag: '2026-12-24', name: 'Heiligabend', art: 'pruefen' }) assert.deepEqual(eintrag(liste, '2026-12-31'), { tag: '2026-12-31', name: 'Silvester', art: 'pruefen' }) assert.deepEqual(eintrag(liste, '2027-01-01'), { tag: '2027-01-01', name: 'Neujahrstag', art: 'feiertag' }) }) test('Jahreswechsel: beide Jahre werden geliefert', () => { const liste = feiertageImZeitraum('2026-12-20', '2027-01-02', 'RP') const tage = liste.map((e) => e.tag) assert.deepEqual(tage, ['2026-12-24', '2026-12-25', '2026-12-26', '2026-12-31', '2027-01-01']) assert.equal(eintrag(liste, '2026-12-25').art, 'feiertag') assert.equal(eintrag(liste, '2026-12-26').art, 'feiertag') }) test('Zeitraum über mehrere Jahre: sortiert, ohne Doppelte', () => { const liste = feiertageImZeitraum('2026-10-01', '2028-02-01', 'BY') const tage = liste.map((e) => e.tag) assert.deepEqual(tage, [...tage].sort()) assert.equal(new Set(tage).size, tage.length) assert.ok(tage.includes('2027-08-15')) assert.ok(tage.includes('2027-01-06')) assert.ok(!tage.includes('2026-08-15'), 'vor dem Zeitraum') assert.ok(!tage.includes('2028-04-14'), 'nach dem Zeitraum') }) test('Grenzen sind eingeschlossen', () => { assert.deepEqual(feiertageImZeitraum('2026-10-03', '2026-10-03', 'RP'), [ { tag: '2026-10-03', name: 'Tag der Deutschen Einheit', art: 'feiertag' }, ]) const liste = feiertageImZeitraum('2026-12-25', '2026-12-26', 'RP') assert.deepEqual(liste.map((e) => e.tag), ['2026-12-25', '2026-12-26']) assert.deepEqual(feiertageImZeitraum('2026-10-04', '2026-10-30', 'RP'), []) }) test('Zeitraum mit bis vor von ist leer', () => { assert.deepEqual(feiertageImZeitraum('2026-10-31', '2026-10-01', 'RP'), []) }) test('Mariä Himmelfahrt in BY ist „pruefen“, im SL „feiertag“', () => { const by = eintrag(feiertageImZeitraum('2028-08-15', '2028-08-15', 'BY'), '2028-08-15') const sl = eintrag(feiertageImZeitraum('2028-08-15', '2028-08-15', 'SL'), '2028-08-15') assert.deepEqual(by, { tag: '2028-08-15', name: 'Mariä Himmelfahrt', art: 'pruefen' }) assert.deepEqual(sl, { tag: '2028-08-15', name: 'Mariä Himmelfahrt', art: 'feiertag' }) assert.equal(eintrag(feiertageImZeitraum('2028-08-15', '2028-08-15', 'RP'), '2028-08-15'), undefined) }) test('Fronleichnam in SN und TH ist „pruefen“ (Datum aus RP)', () => { for (const land of ['SN', 'TH']) { const e = eintrag(feiertageImZeitraum('2026-06-01', '2026-06-30', land), '2026-06-04') assert.deepEqual(e, { tag: '2026-06-04', name: 'Fronleichnam', art: 'pruefen' }, land) } for (const land of ['RP', 'BY', 'BW', 'HE', 'NW', 'SL']) { const e = eintrag(feiertageImZeitraum('2026-06-01', '2026-06-30', land), '2026-06-04') assert.deepEqual(e, { tag: '2026-06-04', name: 'Fronleichnam', art: 'feiertag' }, land) } for (const land of ['BE', 'HH', 'NI', 'SH']) { assert.equal(eintrag(feiertageImZeitraum('2026-06-01', '2026-06-30', land), '2026-06-04'), undefined, land) } }) test('Fronleichnam in SN folgt dem Osterdatum jedes Jahres', () => { // Ostersonntag + 60 Tage: 2027-05-27, 2028-06-15 assert.equal(eintrag(feiertageImZeitraum('2027-01-01', '2027-12-31', 'SN'), '2027-05-27').art, 'pruefen') assert.equal(eintrag(feiertageImZeitraum('2028-01-01', '2028-12-31', 'TH'), '2028-06-15').art, 'pruefen') }) test('in BB bleiben Oster- und Pfingstsonntag echte Feiertage', () => { const liste = feiertageImZeitraum('2028-04-01', '2028-06-30', 'BB') assert.deepEqual(eintrag(liste, '2028-04-16'), { tag: '2028-04-16', name: 'Ostersonntag', art: 'feiertag' }) assert.deepEqual(eintrag(liste, '2028-06-04'), { tag: '2028-06-04', name: 'Pfingstsonntag', art: 'feiertag' }) }) test('landesweite Feiertage anderer Länder bleiben „feiertag“', () => { assert.equal(eintrag(feiertageImZeitraum('2028-11-01', '2028-11-30', 'SN'), '2028-11-22').name, 'Buß- und Bettag') assert.equal(eintrag(feiertageImZeitraum('2028-11-01', '2028-11-30', 'SN'), '2028-11-22').art, 'feiertag') assert.equal(eintrag(feiertageImZeitraum('2028-01-01', '2028-01-31', 'BY'), '2028-01-06').name, 'Heilige Drei Könige') }) test('feiertagName liefert nur echte Feiertage, keine Prüftage', () => { assert.equal(feiertagName('2026-12-24', 'RP'), null, 'Heiligabend ist kein gesetzlicher Feiertag') assert.equal(feiertagName('2026-12-31', 'RP'), null) assert.equal(feiertagName('2028-08-15', 'BY'), null, 'in BY nur örtlich') assert.equal(feiertagName('2028-08-15', 'SL'), 'Mariä Himmelfahrt') assert.equal(feiertagName('2026-06-04', 'SN'), null) assert.equal(feiertagName('2026-06-04', 'RP'), 'Fronleichnam') assert.equal(feiertagName('2026-12-25', 'HH'), '1. Weihnachtstag') }) test('feiertagName stimmt mit istFeiertag überein (alle Länder, drei Jahre)', () => { for (const land of BUNDESLAENDER) { const liste = feiertageImZeitraum('2026-01-01', '2028-12-31', land).filter((e) => e.art === 'feiertag') assert.ok(liste.length >= 9, land) for (const e of liste) { assert.equal(istFeiertag(e.tag, land), true, `${land} ${e.tag}`) assert.equal(feiertagName(e.tag, land), e.name, `${land} ${e.tag}`) } } // Gegenrichtung: ein ganzes Jahr, jeder Tag ohne Eintrag ist auch für istFeiertag kein Feiertag const rp = new Set(feiertageImZeitraum('2027-01-01', '2027-12-31', 'RP').filter((e) => e.art === 'feiertag').map((e) => e.tag)) for (let m = 1; m <= 12; m += 1) { for (let d = 1; d <= 28; d += 1) { const tag = `2027-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}` assert.equal(feiertagName(tag, 'RP') !== null, rp.has(tag), tag) assert.equal(istFeiertag(tag, 'RP'), rp.has(tag), tag) } } }) test('Prüftage doppeln keinen Eintrag und sind sortiert', () => { for (const land of BUNDESLAENDER) { const liste = feiertageImZeitraum('2026-01-01', '2029-12-31', land) const tage = liste.map((e) => e.tag) assert.equal(new Set(tage).size, tage.length, `${land}: ein Eintrag je Tag`) assert.deepEqual(tage, [...tage].sort(), `${land}: sortiert`) for (const e of liste) { assert.match(e.name, /\S/) assert.ok(e.art === 'feiertag' || e.art === 'pruefen') } for (const e of liste.filter((x) => x.art === 'pruefen')) { assert.equal(feiertagName(e.tag, land), null, `${land} ${e.tag}`) // istFeiertag (Terminplaner) bleibt unveraendert: nur Mariae Himmelfahrt in BY fuehrt es als Feiertag if (!(land === 'BY' && e.name === 'Mariä Himmelfahrt')) { assert.equal(istFeiertag(e.tag, land), false, `${land} ${e.tag}`) } } } assert.equal(istFeiertag('2028-08-15', 'BY'), true, 'bisheriges Verhalten von istFeiertag bleibt') }) test('Ergebnis ist unabhängig von der Prozess-Zeitzone (UTC bis UTC+9, UTC-8)', async () => { const { execFileSync } = await import('node:child_process') const code = ` import { feiertageImZeitraum } from './server/services/termine/feiertage.js' process.stdout.write(JSON.stringify(feiertageImZeitraum('2026-12-20', '2027-01-02', 'SN'))) ` const lauf = (tz) => execFileSync(process.execPath, ['--input-type=module', '-e', code], { cwd: new URL('..', import.meta.url), env: { ...process.env, TZ: tz }, encoding: 'utf8', }) const erwartet = JSON.stringify(feiertageImZeitraum('2026-12-20', '2027-01-02', 'SN')) for (const tz of ['UTC', 'Europe/Berlin', 'America/Los_Angeles', 'Asia/Tokyo']) { assert.equal(lauf(tz), erwartet, tz) } })