feat(db): add versioned habits and calendar domain contracts
This commit is contained in:
38
src/habits/calendar.test.ts
Normal file
38
src/habits/calendar.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { expect, test } from 'bun:test';
|
||||
import { addDays, endOfDay, localDate, scheduled, shade } from './calendar';
|
||||
import { calendarSettingsSchema, dateSchema, habitInput, scheduleSchema } from './contracts';
|
||||
test('fixed calendar recurrence uses anchors and Monday weeks independently of completions', () => {
|
||||
expect(scheduled({ type: 'daily' }, '2026-09-04')).toBe(true);
|
||||
expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-07')).toBe(true);
|
||||
expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-01')).toBe(false);
|
||||
expect(scheduled({ type: 'interval', every: 3, anchor: '2026-09-04' }, '2026-09-06')).toBe(false);
|
||||
expect(scheduled({ type: 'weekdays', days: [1] }, '2026-09-07')).toBe(true);
|
||||
expect(scheduled({ type: 'weekdays', days: [1] }, '2026-09-08')).toBe(false);
|
||||
const rule = { type: 'weekly' as const, every: 2, weekday: 3, anchor: '2026-08-31' };
|
||||
expect(scheduled(rule, '2026-09-02')).toBe(true);
|
||||
expect(scheduled(rule, '2026-09-09')).toBe(false);
|
||||
expect(scheduled(rule, '2026-09-16')).toBe(true);
|
||||
});
|
||||
test('real dates, strict settings, and exclusive methods are validated', () => {
|
||||
for (const d of ['2026-02-29', '2026-13-01', '2026-9-1', 'garbage']) expect(dateSchema.safeParse(d).success).toBe(false);
|
||||
expect(addDays('2024-02-28', 1)).toBe('2024-02-29');
|
||||
expect(habitInput.safeParse({ name: 'mixed', method: 'manual', target: 8 }).success).toBe(false);
|
||||
expect(scheduleSchema.safeParse({ type: 'weekdays', days: [1, 1] }).success).toBe(false);
|
||||
expect(calendarSettingsSchema.safeParse({ daySpacing: 4 }).success).toBe(false);
|
||||
});
|
||||
test('local midnight handles spring, autumn, and fractional timezone offsets', () => {
|
||||
expect(endOfDay('2026-03-29', 'Europe/Belgrade') - endOfDay('2026-03-28', 'Europe/Belgrade')).toBe(23 * 3600000);
|
||||
expect(endOfDay('2026-10-25', 'Europe/Belgrade') - endOfDay('2026-10-24', 'Europe/Belgrade')).toBe(25 * 3600000);
|
||||
expect(new Date(endOfDay('2026-09-04', 'Asia/Kathmandu')).toISOString()).toBe('2026-09-04T18:15:00.000Z');
|
||||
expect(localDate(Date.parse('2026-09-03T22:00Z'), 'Europe/Belgrade')).toBe('2026-09-04');
|
||||
});
|
||||
test('eight positive count shades, full completion, neutral and empty stay distinct', () => {
|
||||
const settings = calendarSettingsSchema.parse({});
|
||||
const values = Array.from({ length: 8 }, (_, i) => shade((i + 1) / 8, 8, settings, true));
|
||||
expect(new Set(values.map(v => v.color)).size).toBe(8);
|
||||
expect(values.map(v => v.level)).toEqual([1,2,3,4,5,6,7,8]);
|
||||
expect(shade(1, 1, settings, true).color).toBe(settings.mainColor);
|
||||
expect(shade(0, 8, settings, true).color).toBe(settings.emptyColor);
|
||||
expect(shade(null, 8, settings, true).color).toBe(settings.notDueColor);
|
||||
expect(shade(0.75, 4, settings, false).level).toBe(3);
|
||||
});
|
||||
Reference in New Issue
Block a user