test(loop): cover db/csv-import - #30
Merged
Merged
Conversation
Add characterization tests for importFromCSV (local Oura App-Data CSV import into sqlite). Tests only; no production code changed. Covers directory-missing throw, empty/missing files, header->column mapping, num()/str() null-guards, header-only and blank-line handling, short-row trailing NULLs, INSERT OR REPLACE upsert, spo2 JSON average extraction plus its malformed/missing-key NULL branches, and heartrate day derivation with INSERT OR IGNORE dedup on (timestamp, source). Two behaviours pinned as findings (not fixed): CRLF line endings drop the last column, and an empty workout label stores NULL here vs '' in the API-sync path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TZnRjDeAggmffDp1dVP2an
drakulavich
marked this pull request as ready for review
August 1, 2026 13:57
Greptile SummaryThis tests-only PR adds dedicated coverage for CSV parsing and database import behavior, while documenting two pre-existing characterization findings.
Confidence Score: 5/5The PR appears safe to merge because it changes only tests and loop documentation, with no actionable regression in the added coverage. The new tests exercise existing CSV-import behavior using fixed local fixtures, restore the modified environment after the suite, and do not alter production behavior.
|
| Filename | Overview |
|---|---|
| src/db/csv-import.test.ts | Adds focused integration-style tests around importFromCSV using isolated temporary CSV fixtures and in-memory SQLite databases; no actionable defect was found. |
| docs/loops/test-gap-filler-state.md | Records the new coverage, advances the queue, and clearly documents the two intentionally pinned pre-existing behaviors. |
Reviews (1): Last reviewed commit: "test(loop): cover db/csv-import" | Re-trigger Greptile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this module
Weekly test-gap-filler run.
src/db/csv-import.tswas the top-priority uncovered module in the loop queue: it has real parsing/mapping logic (semicolon-delimited CSV → sqlite for 10 Oura tables), several error branches, and no dedicated test file. Tests only — no production code was changed.How it's tested
importFromCSVreads from a directory hard-coded at module-load time fromprocess.env.HOME:The test points
HOMEat a temp sandbox before importing the module (theCSV_DIRconst is captured once, on first import), seeds real semicolon-delimited CSV files per test, and drives them into a freshbun:sqlitein-memory database seeded with the real schema.HOMEis restored inafterAllso nothing leaks into other test files. Boundaries are mocked, module internals are not.Branches covered
CSV directory not found.daily_sleep: 0 rows…CSV import complete.still logged.< 2lines) → 0 rows; blank/whitespace-only lines skipped; a row with fewer values than headers fills trailing columns with NULL (vals[i] ?? '').num()null-guard: empty and non-numeric cells →NULL(not0/NaN); valid numbers pass through.str(): empty cell →NULL.INSERT OR REPLACEkeeps the last row, while the per-table log still reports the raw parsed row count.averageextracted from{"average": …}; swallowedJSON.parseon malformed blob →NULL; missingaveragekey →NULL.dayderived fromtimestamp.slice(0,10);INSERT OR IGNOREdedups on the(timestamp, source)unique index.Findings (characterization — pinned, not fixed)
Per loop discipline, suspected issues are pinned against current behaviour with marker comments rather than fixed:
parseCSVsplits on'\n'only and never strips'\r', so with a CRLF file the last header becomes"timestamp\r"and the insert (which reads key"timestamp") storesNULL. Non-terminal columns are fine. A real Oura export saved with Windows line endings would lose whatever column is last per file. Suggested fix for a separate PR:text.split(/\r?\n/).workout.label→NULLhere, but''in the API path.csv-import.tsruns the label throughstr()(→NULL), whereasimport.tscoerces a null label to''(asserted inimport.test.ts). Same logically-empty label, two different stored values depending on the importer. Inconsistency, not clearly a bug.Verification
bun test— whole suite green: 148 pass / 0 fail (was 133; +15 new).bunx tsc --noEmit— clean.bun install --frozen-lockfile— in sync (exit 0).State file
docs/loops/test-gap-filler-state.mdupdated in this PR (covered list, queue promotescommands/sync.tsto next, findings recorded).Do not merge — a human merges.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TZnRjDeAggmffDp1dVP2an
Generated by Claude Code