import { describe, expect, it } from 'vitest' import type { Curve96 } from '@vpp/domain' import { MemoryTimeSeriesStore } from '../src/timeseries.js' const curve = (date: string, fill = '1.0'): Curve96 => ({ interval_minutes: 15, date, values: Array.from({ length: 96 }, () => fill), }) let tick = 0 const clock = () => `2026-03-14T08:00:${String(tick++).padStart(2, '0')}Z` describe('time-series store', () => { it('writes a revision and reads it back as latest', () => { const store = new MemoryTimeSeriesStore(clock) const rec = store.write('load:agg-01', curve('2026-03-14'), 'meter-file') expect(rec.version).toBe(1) expect(store.latest('load:agg-01', '2026-03-14')).toEqual(rec) expect(store.latest('load:agg-01', '2026-03-15')).toBeUndefined() }) it('keeps revision history instead of overwriting (late corrections)', () => { const store = new MemoryTimeSeriesStore(clock) store.write('load:agg-01', curve('2026-03-14', '1.0'), 'edge') const corrected = store.write('load:agg-01', curve('2026-03-14', '1.1'), 'settlement-meter') expect(corrected.version).toBe(2) const history = store.revisions('load:agg-01', '2026-03-14') expect(history.map((r) => r.version)).toEqual([1, 2]) expect(history[0]!.curve.values[0]).toBe('1.0') expect(store.latest('load:agg-01', '2026-03-14')!.curve.values[0]).toBe('1.1') }) it('is idempotent for identical content', () => { const store = new MemoryTimeSeriesStore(clock) const a = store.write('pv:site-7', curve('2026-03-14'), 'edge') const b = store.write('pv:site-7', curve('2026-03-14'), 'edge-retry') expect(b).toBe(a) expect(store.revisions('pv:site-7', '2026-03-14')).toHaveLength(1) }) it('returns an inclusive date range with latest revision per date, sorted', () => { const store = new MemoryTimeSeriesStore(clock) store.write('load:agg-01', curve('2026-03-16'), 'edge') store.write('load:agg-01', curve('2026-03-14', '1.0'), 'edge') store.write('load:agg-01', curve('2026-03-14', '2.0'), 'edge') store.write('load:agg-01', curve('2026-03-13'), 'edge') store.write('load:agg-01', curve('2026-03-17'), 'edge') store.write('pv:site-7', curve('2026-03-15'), 'edge') const out = store.range('load:agg-01', '2026-03-14', '2026-03-16') expect(out.map((r) => r.date)).toEqual(['2026-03-14', '2026-03-16']) expect(out[0]!.version).toBe(2) }) it('rejects malformed curves and empty series ids', () => { const store = new MemoryTimeSeriesStore(clock) expect(() => store.write('', curve('2026-03-14'), 'edge')).toThrow(/series_id/) expect(() => store.write('load:x', { ...curve('2026-03-14'), values: ['1.0'] }, 'edge'), ).toThrow() expect(() => store.range('load:x', '2026/03/14', '2026-03-15')).toThrow() }) it('stored curves are isolated from caller mutation', () => { const store = new MemoryTimeSeriesStore(clock) const input = curve('2026-03-14') store.write('load:x', input, 'edge') input.values[0] = '999' expect(store.latest('load:x', '2026-03-14')!.curve.values[0]).toBe('1.0') }) it('lists series ids', () => { const store = new MemoryTimeSeriesStore(clock) store.write('pv:site-7', curve('2026-03-14'), 'edge') store.write('load:agg-01', curve('2026-03-14'), 'edge') expect(store.seriesIds()).toEqual(['load:agg-01', 'pv:site-7']) }) })