feat/api-dx2
needs attentionviewing older commit4ea2976 · incrementalPR #262reviewed 2026-07-07 20:20 UTC0H · 1M · 4L · 3I- Purpose
- Public-API DX improvements — close gaps surfaced by a real Tiendas Neto customer request for multi-RPU bill PDFs via the API
- Goal
- Four additive improvements to the public v1 API: force_refresh/period_count docs, zip filename collision fix, collection health on monitoring endpoint, latest_only file filter
- Sub-goals
- SG-1: Document force_refresh + period_count on POST /v1/jobs
- SG-2: Fix zip entry filename collisions for multi-RPU bundles
- SG-3: Add last_collection + latest_period_end to GET /v1/monitoring
- SG-4: Add latest_only filter to GET /v1/files
- What
- This increment (4ea2976e) adds unit tests for toCuratedErrorCode and dedupeZipEntryNames, and extracts the zip-dedup logic from inline in generatePublicFilesZip to an exported testable function. Previous commits carried all wire-level changes.
- Why
- Tests were absent for two security/correctness-critical utilities: the error-code curation boundary (toCuratedErrorCode) and the zip dedup function. Extraction + tests close that gap.
- Areas
- apps/platform/src/api/utils/public-v1-file-read.ts+49−17apps/platform/src/api/utils/public-v1-monitoring-read.ts+30−22apps/platform/src/api/utils/__tests__/public-v1-file-read.test.ts+96−0apps/platform/src/api/utils/__tests__/public-v1-monitoring-read.test.ts+80−0
- Blast
- 4 files, +255/−39 in this increment; all test or utility-extraction changes — no wire schema, no DB, no handler logic touched
Findings · 8
correctness1
Algorithm is input-order-dependent — first-seen wins, documented only in tests
apps/platform/src/api/utils/public-v1-file-read.ts
The dedup is intentionally order-preserving (first occurrence keeps the base name, later ones get suffixes). This is correct for the caller but is not stated in the function's JSDoc. The test for the _2 regression implicitly relies on this order; adding a comment (`// first occurrence keeps the base name; later duplicates get _2, _3, …`) would make the invariant explicit.
tests4
5 real CfeErrorCode values missing from toCuratedErrorCode test coverage
apps/platform/src/api/utils/__tests__/public-v1-monitoring-read.test.ts:55
NAVIGATION_ERROR, EXTRACTION_ERROR, BILL_EXTRACTION_FAILED, DOWNLOAD_ERROR, and UPLOAD_ERROR all fall to `default → collection_failed` but are absent from the explicit test group and the fail-closed invariant sample. The test uses SOME_FUTURE_CODE as a placeholder for unknown codes — fine as a general sentinel, but real codes should also be anchored. A developer accidentally promoting one of these to an explicit mapping would see zero test failures. Add them to the `it.each` on line 55.
dedupeZipEntryNames: storagePath passthrough not asserted
apps/platform/src/api/utils/__tests__/public-v1-file-read.test.ts:17
The `names()` helper strips storagePath from every assertion. The load-bearing contract is that each entry's storagePath survives unchanged — a wrong path causes the zip generator to fetch the wrong S3 object under the deduplicated name. Add at least one test asserting `result[i].storagePath === input[i].storagePath` over a duplicate-bearing input.
dedupeZipEntryNames: empty-array input not tested
apps/platform/src/api/utils/__tests__/public-v1-file-read.test.ts
No test exercises `dedupeZipEntryNames([])`. The function's caller guards against this today, but a boundary test documents the contract and protects against future refactors that move the guard.
Collision regression test is order-sensitive but test name does not say so
apps/platform/src/api/utils/__tests__/public-v1-file-read.test.ts:55
The _2 clobber test verifies a specific input order. If the algorithm ever sorted inputs the test would still pass but the anti-clobber property could silently break. Minor improvement: note the order-dependency in the test name or add a comment.
improvement3
Hoist `dot` computation outside the while-loop — it's invariant over `base`
apps/platform/src/api/utils/public-v1-file-read.ts:64
`const dot = base.lastIndexOf('.')` is recomputed on every loop iteration but `base` is a const captured before the loop. Hoisting it out (`const dot = base.lastIndexOf('.');` before `let name = base;`) eliminates redundant computation and makes the data-flow cleaner.
Counter starts at 1 but is immediately incremented — start at 2 for clarity
apps/platform/src/api/utils/public-v1-file-read.ts:63
`n` is initialised to 1 but the first line of the while body increments it to 2 before use — n=1 is a dead state. Starting `n = 2` and using it directly (`name = …_${n}…; n += 1;` after the name assignment) makes the intent clearer with identical output.
`dot > 0` silently excludes leading-dot files from extension splitting — add inline comment
apps/platform/src/api/utils/public-v1-file-read.ts:66
`dot > 0` is tested and intentional (`.gitignore` treated as no-extension), but it reads like a potential off-by-one. A short inline comment mirrors the test's rationale: `// dot===0 means leading dot (.gitignore) → no extension`.
History · 6 commits
- d2ff8e5needs attentionincremental1H · 2M · 4L2026-07-07 21:51
- 2ad6e01safeincremental0H · 1M · 1L2026-07-07 21:15
- 4ea2976needs attentionincremental0H · 1M · 4L2026-07-07 20:20current
- fe4cf9fneeds attentionincremental3H · 8M · 7L2026-07-07 14:53
- d82dbe8safeincremental0H · 0M · 0L2026-07-06 18:16
- 7113f2eneeds attentionfull5H · 6M · 7L2026-07-06 18:01