import { describe, it, expect } from 'vitest'; import { computeNetScore, clamp01, clampRange, luminanceFromNetScore, addEntryToDay, updateEntryInDay, removeEntryFromDay, replaceEntriesForDay, recomputeDayNetScore } from '../../src/compute'; import type { DayTile, HabitEntry } from '../../src/models'; function day(entries: HabitEntry[] = []): DayTile { return { date: '2025-01-01', mood: { hue: 180, intensity: 0.5 }, entries, netScore: computeNetScore(entries) }; } describe('compute helpers', () => { it('computes netScore correctly', () => { const entries: HabitEntry[] = [ { id: '1', type: 'positive', habitId: 'h1', label: 'A', weight: 1, timestamp: new Date().toISOString() }, { id: '2', type: 'positive', habitId: 'h2', label: 'B', weight: 2, timestamp: new Date().toISOString() }, { id: '3', type: 'negative', habitId: 'h3', label: 'C', weight: 1, timestamp: new Date().toISOString() } ]; expect(computeNetScore(entries)).toBe(1 + 2 - 1); }); it('clamps values', () => { expect(clampRange(5, 0, 3)).toBe(3); expect(clampRange(-2, 0, 3)).toBe(0); expect(clamp01(2)).toBe(1); expect(clamp01(-1)).toBe(0); }); it('maps luminance smoothly', () => { const lo = luminanceFromNetScore(-100); const hi = luminanceFromNetScore(100); const mid = luminanceFromNetScore(0); expect(lo).toBeGreaterThanOrEqual(0); expect(hi).toBeLessThanOrEqual(1); expect(hi).toBeGreaterThan(lo); expect(Math.abs(mid - 0.5)).toBeLessThan(1e-6); }); it('recomputes on CRUD operations', () => { let d = day(); d = addEntryToDay(d, { id: 'e1', type: 'positive', habitId: 'h1', label: 'A', weight: 2, timestamp: new Date().toISOString() }); expect(d.netScore).toBe(2); d = addEntryToDay(d, { id: 'e2', type: 'negative', habitId: 'h2', label: 'B', weight: 1, timestamp: new Date().toISOString() }); expect(d.netScore).toBe(1); d = updateEntryInDay(d, { id: 'e2', type: 'negative', habitId: 'h2', label: 'B', weight: 2, timestamp: new Date().toISOString() }); expect(d.netScore).toBe(0); d = removeEntryFromDay(d, 'e1'); expect(d.netScore).toBe(-2); d = replaceEntriesForDay(d, []); expect(d.netScore).toBe(0); expect(recomputeDayNetScore(d)).toBe(0); }); });