process.env.TZ = 'Europe/Berlin'; const { test } = require('node:test'); const assert = require('node:assert/strict'); const database = require('../src/config/database'); const repository = require('../src/repositories/WorklogRepository'); const service = require('../src/services/TimeEntryService'); const RealDate = Date; function freezeTime(t, time = '2026-10-27T14:00:00') { global.Date = class extends RealDate { constructor(...args) { super(...(args.length ? args : [time])); } static now() { return new RealDate(time).getTime(); } }; t.after(() => { global.Date = RealDate; }); } function mock(t, object, key, value) { const original = object[key]; object[key] = value; t.after(() => { object[key] = original; }); } for (const [name, model, method] of [ ['Krankheit', 'Sick', 'getSickByUserInDateRange'], ['Urlaub', 'Vacation', 'getVacationsByUserInDateRange'] ]) { test(`${name}: letzter Tag nach Zeitumstellung wird berücksichtigt`, async t => { mock(t, database, 'getModels', () => ({ [model]: { findAll: async () => [{ first_day: '2026-10-06', last_day: '2026-10-26', vacation_type: 2 }] } })); const days = await repository[method](1, new Date('2026-10-26T00:00:00'), new Date('2026-11-01T23:59:59')); assert.deepEqual(days.map(day => day.date.toISOString().slice(0, 10)), ['2026-10-26']); }); test(`${name}: eintägige Abwesenheit an der Wochengrenze`, async t => { mock(t, database, 'getModels', () => ({ [model]: { findAll: async () => [{ first_day: '2026-10-26', last_day: '2026-10-26', vacation_type: 1 }] } })); const days = await repository[method](1, new Date('2026-10-26T00:00:00'), new Date('2026-11-01T23:59:59')); assert.equal(days.length, 1); assert.equal(days[0].date.toISOString().slice(0, 10), '2026-10-26'); }); } function setupWeek(t, { pause = false, sick = true, halfVacation = false } = {}) { freezeTime(t); const entries = [ { id: 1, state: 'start work', tstamp: '2026-10-27 08:00:00' }, { id: 2, state: 'start pause', tstamp: '2026-10-27 10:00:00', relatedTo_id: 1 }, ...(!pause ? [{ id: 3, state: 'stop pause', tstamp: '2026-10-27 10:30:00', relatedTo_id: 2 }] : []) ]; mock(t, repository, 'findByDateRange', async () => entries); mock(t, repository, 'findByUser', async () => entries); mock(t, repository, 'getTimefixesByWorklogIds', async () => new Map()); mock(t, repository, 'getSickByUserInDateRange', async () => sick ? [{ date: new Date('2026-10-26'), sick_type: 'self' }] : []); mock(t, repository, 'getVacationsByUserInDateRange', async () => halfVacation ? [{ date: new Date('2026-10-27'), half_day: 1 }] : []); mock(t, repository, 'getHolidaysInDateRange', async () => []); } test('Wochenansicht: lokale Wochengrenzen, laufende Arbeit und Krankheit genau einmal', async t => { setupWeek(t); const week = await service.getWeekOverview(1); assert.equal(week.weekStart, '2026-10-26'); assert.equal(week.weekEnd, '2026-11-01'); assert.equal(week.days[0].sick.hours, 8); assert.equal(week.days[1].workBlocks[0].netWorkTime, '5:30'); assert.equal(week.weekTotal, '5:30'); assert.equal(week.nonWorkingTotal, '8:00'); assert.equal(week.totalAll, '13:30'); }); test('Wochenansicht: laufende Pause friert Nettozeit ein', async t => { setupWeek(t, { pause: true }); const week = await service.getWeekOverview(1); assert.equal(week.days[1].workBlocks[0].netWorkTime, '2:00'); assert.equal(week.weekTotal, '2:00'); }); test('Wochenansicht: Arbeit am halben Urlaubstag geht nicht verloren', async t => { setupWeek(t, { sick: false, halfVacation: true }); const week = await service.getWeekOverview(1); assert.equal(week.weekTotal, '5:30'); assert.equal(week.nonWorkingTotal, '4:00'); assert.equal(week.totalAll, '9:30'); }); test('Status: keine Fehlzeit für kranken Montag, Pausen angerechnet, laufende Wochenzeit', async t => { setupWeek(t); mock(t, database, 'getModels', () => ({ User: { findByPk: async () => ({ daily_hours: 8 }) }, Timewish: { findOne: async () => null, findAll: async () => [] } })); mock(t, service, '_calculateTotalOvertime', async () => ({ minutes: 349, formatted: '+5:49' })); const stats = await service.getStatistics(1); assert.equal(stats.currentlyWorked, '05:30:00'); assert.equal(stats.overtime, '+0:00'); assert.equal(stats.weekWorktime, '5:30'); assert.equal(stats.nonWorkingHours, '8:00'); assert.equal(stats.missingBreakMinutes, 0); assert.equal(stats.open, '02:30:00'); assert.equal(stats.openForWeek, '2:30'); assert.equal(stats.adjustedEndTodayWeek, '16:30'); assert.equal(stats.totalOvertime, '+5:49'); });