Files
brain/tools/cost-stop-hook.test.mjs
T
2026-06-15 19:21:13 +03:00

83 lines
3.0 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { runUpdate, currentMonthFile, todayISO } from './cost-stop-hook.mjs';
import { PRICING } from './cost-pricing.mjs';
describe('runUpdate (pure)', () => {
const ep = (date, cost) => ({
timestamps: { started_at: `${date}T12:00:00.000Z` },
task_cost: cost,
});
it('adds today entry to empty existing state', () => {
const r = runUpdate({
episodes: [ep('2026-05-28', { classifier_input_tokens: 1_000_000 })],
dateISO: '2026-05-28',
existing: {},
pricing: PRICING,
});
expect(r['2026-05-28']).toBeDefined();
expect(r['2026-05-28'].classifier_usd).toBeCloseTo(3, 6);
expect(r['2026-05-28'].episode_count).toBe(1);
});
it('overwrites today entry but preserves other-day entries', () => {
const r = runUpdate({
episodes: [ep('2026-05-28', { classifier_input_tokens: 1_000_000 })],
dateISO: '2026-05-28',
existing: { '2026-05-27': { total_usd: 9.99, episode_count: 5 } },
pricing: PRICING,
});
expect(r['2026-05-27']).toEqual({ total_usd: 9.99, episode_count: 5 });
expect(r['2026-05-28'].classifier_usd).toBeCloseTo(3, 6);
});
it('writes zero-episode-count day when no episodes match', () => {
const r = runUpdate({
episodes: [],
dateISO: '2026-05-28',
existing: {},
pricing: PRICING,
});
expect(r['2026-05-28'].episode_count).toBe(0);
expect(r['2026-05-28'].total_usd).toBe(0);
});
it('does not mutate the existing input object', () => {
const existing = { '2026-05-27': { total_usd: 1 } };
runUpdate({ episodes: [], dateISO: '2026-05-28', existing, pricing: PRICING });
expect(Object.keys(existing)).toEqual(['2026-05-27']);
expect(existing['2026-05-27']).toEqual({ total_usd: 1 });
});
});
describe('todayISO', () => {
it('returns YYYY-MM-DD string for a Date instance', () => {
expect(todayISO(new Date('2026-05-28T15:30:00.000Z'))).toBe('2026-05-28');
});
it('uses UTC date components', () => {
// Date that's late evening UTC but next day in local — ISO must reflect UTC.
expect(todayISO(new Date('2026-05-28T23:59:59.000Z'))).toBe('2026-05-28');
});
});
describe('currentMonthFile', () => {
it('returns docs/observer/episodes-YYYY-MM.jsonl', () => {
const p = currentMonthFile(new Date('2026-05-28T12:00:00.000Z'), '/repo');
expect(p.replace(/\\/g, '/')).toBe('/repo/docs/observer/episodes-2026-05.jsonl');
});
it('pads month with leading zero', () => {
const p = currentMonthFile(new Date('2026-01-05T12:00:00.000Z'), '/repo');
expect(p.replace(/\\/g, '/')).toBe('/repo/docs/observer/episodes-2026-01.jsonl');
});
it('uses stateDir param (config-seam); дефолт docs/observer', () => {
const now = new Date('2026-06-15T12:00:00.000Z');
expect(currentMonthFile(now, '/repo', '.claude/brain-state').replace(/\\/g, '/'))
.toBe('/repo/.claude/brain-state/episodes-2026-06.jsonl');
expect(currentMonthFile(now, '/repo').replace(/\\/g, '/'))
.toBe('/repo/docs/observer/episodes-2026-06.jsonl');
});
});