readClaudeUsage() and readHostSnapshot() validated the stored receivedAt
with new Date(), which accepts "1" or a bare date. Both now go through
isIsoTimestamp(), exported by claude-usage.js and built on the sanitizer's
own check, so no regex is duplicated.
The existing 500 tests failed at mkdir, before the temp file existed, so
removing the unlinkSync() cleanup of either writer left the suite green. Two
new cases make the rename fail after the temp write (the target is a
non-empty directory) and require that no temp file is left.
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- __tests__/claude-usage.test.js (119 cases): sanitizer contract and
idempotence, dropped values (unknown fields, negative, past 2^53,
detail outside its regex, status outside the enum, bad dates, model
keys), list bounds, token isolation both ways, 503 naming each variable,
413 at 65 537 bytes and /hosts/<id> still 413 at 4 097, atomic mode-600
write, POST then GET round trip, exact staleness boundary under a frozen
clock, no_data degradation, rejection logs, COPY line.
- writes.test.js: POST /claude-usage may write only in the directory of
CLAUDE_USAGE_PATH, its rejections write nothing, GET /claude-usage joins
the read routes.
- README.md and CLAUDE.md: both routes, the three env vars, the
/data/claude-usage read-write mount owned by uid 1000, the token table.
Resolves#48
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>