Harden bucket allocationValue: guard NaN/negative + server-side constraint + form cancel #70
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Fast-follow from #54's finish-phase (pr-reviewer) review. Three related, non-blocking gaps in the add-bucket flow — none reachable through the shipped UI today, but real defense-in-depth holes.
1.
allocationValueNaN/negative → silent zero-capacity bucket (main item)frontend/src/lib/bucketInput.tstoScaledInteger()has no guard:dollarsToCents('abc')→NaN, whichJSON.stringifycoerces tonullon the wire. BackendBucket::$allocationValueis nullable?intwith noNotNull/PositiveOrZeroconstraint, so it's accepted, andBucketRoomCalculatortreats null as0→ a silent zero-capacity bucket instead of a rejected request.Currently mitigated end-to-end only by the browser's native
<input type="number" min={0}>constraint validation (blocks non-numeric/negative beforehandleSubmit). The safety net is entirely a browser behavior the JS functions don't know about, and there's no server-side backstop.This is the missing counterpart to the existing
BufferOnlyForFixedLimitvalidator family —allocationValuehas no equivalent invariant.Fix:
Number.isFinite+>= 0guard intoScaledInteger(return/throw or surface a field error rather than emit NaN).allocationValueconstraint forfixed_limit/percentagetypes (required +PositiveOrZero), so a null/negative is a clean 422, not a silent zero bucket. Mirror the existing validator pattern (src/Validator/*).bucketInput.test.tscases fordollarsToCents('-50')/dollarsToCents('abc')documenting the guarded behavior; backend test asserting the 422.2. Add-bucket form cancel/close affordance
frontend/src/pages/AddBucketForm.tsxonly callssetOpen(false)on successful submit — a user who opens the form and changes their mind can't collapse it back to the "Add bucket" button without navigating away. Not in #54's stated scope (deferred, not a regression). Add a Cancel button that resets fields +setOpen(false).3. Characterization tests (nice-to-have)
Document the currently-unguarded
bucketInputbehavior with the negative/non-numeric cases above so a regression is caught if the guard lands or the functions get reused without atype="number"input.Notes