docs(01-02): complete body-size-limits and test-hardening plan

- 01-02-SUMMARY.md: plan completion summary with deviations
- STATE.md: advanced plan position, added decisions, updated metrics
- ROADMAP.md: phase 01 marked complete (2/2 plans)
- REQUIREMENTS.md: DATA-03 and DATA-04 marked complete
This commit is contained in:
2026-03-23 21:26:02 +01:00
parent 7bdfc5ffec
commit 03c3d5d6d7
4 changed files with 142 additions and 12 deletions

View File

@@ -0,0 +1,127 @@
---
phase: 01-data-integrity
plan: 02
subsystem: api
tags: [go, http, security, testing, maxbytesreader, body-size-limit]
# Dependency graph
requires:
- phase: 01-data-integrity plan 01
provides: UPSERT fix and FK enforcement already applied; test file structure established
provides:
- HTTP 413 response for oversized request bodies (>1MB) on WebhookHandler, TagsHandler POST, TagAssignmentHandler PUT/DELETE
- maxBodyBytes constant (1 << 20) and MaxBytesReader + errors.As pattern
- t.Fatalf at all 6 test setup call sites (no more silent test pass-on-setup-failure)
- 3 new oversized-body tests proving DATA-03 fixed
affects:
- phase-02 (database refactor — handlers are now correct and hardened, test suite is reliable)
- any future handler additions that accept a body (pattern established)
# Tech tracking
tech-stack:
added: []
patterns:
- "MaxBytesReader + errors.As(*http.MaxBytesError) pattern for request body size limiting in handlers"
- "JSON-prefix oversized body test: use valid JSON opening so decoder reads past limit before MaxBytesReader triggers"
key-files:
created: []
modified:
- pkg/diunwebhook/diunwebhook.go
- pkg/diunwebhook/diunwebhook_test.go
key-decisions:
- "Use MaxBytesReader wrapping r.Body before each JSON decode; distinguish 413 from 400 via errors.As on *http.MaxBytesError"
- "Oversized body test bodies must use valid JSON prefix (e.g. {\"image\":\") + padding — all-x bodies trigger JSON parse error before MaxBytesReader limit is reached"
patterns-established:
- "MaxBytesReader body guard pattern: r.Body = http.MaxBytesReader(w, r.Body, maxBodyBytes) before decode, errors.As for 413 vs 400"
- "Test setup errors must use t.Fatalf, never silent return"
requirements-completed:
- DATA-03
- DATA-04
# Metrics
duration: 7min
completed: 2026-03-23
---
# Phase 01 Plan 02: Body Size Limits and Test Setup Hardening Summary
**Request body size limits (1MB cap, HTTP 413) added to four handler paths; six silent test-setup returns replaced with t.Fatalf to surface setup failures in CI**
## Performance
- **Duration:** 7 min
- **Started:** 2026-03-23T20:17:30Z
- **Completed:** 2026-03-23T20:24:37Z
- **Tasks:** 2
- **Files modified:** 2
## Accomplishments
- Added `maxBodyBytes` constant and `errors` import to `diunwebhook.go`; applied `http.MaxBytesReader` + `errors.As(*http.MaxBytesError)` guard before JSON decode in WebhookHandler, TagsHandler POST, TagAssignmentHandler PUT and DELETE — returns HTTP 413 on body > 1MB
- Replaced 6 silent `if err != nil { return }` test setup patterns with `t.Fatalf(...)` so failing setup always fails the test, not silently passes
- Added 3 new oversized-body tests (TestWebhookHandler_OversizedBody, TestTagsHandler_OversizedBody, TestTagAssignmentHandler_OversizedBody); all pass with 413
## Task Commits
Each task was committed atomically:
1. **RED: add failing 413 tests** - `311e91d` (test)
2. **Task 1: MaxBytesReader in handlers + GREEN test fix** - `98dfd76` (feat)
3. **Task 2: Replace silent returns with t.Fatalf** - `7bdfc5f` (fix)
**Plan metadata:** (docs commit — see below)
_Note: TDD task 1 has a RED commit followed by a combined feat commit covering the implementation and the test body correction._
## Files Created/Modified
- `pkg/diunwebhook/diunwebhook.go` — added `errors` import, `maxBodyBytes` constant, MaxBytesReader guards in 4 handler paths
- `pkg/diunwebhook/diunwebhook_test.go` — 3 new oversized-body tests; 6 t.Fatalf replacements
## Decisions Made
- `http.MaxBytesReader` is applied per-handler (not via middleware) to match the existing no-middleware architecture
- Body limit set at 1MB (`1 << 20`) matching the plan spec
- Oversized body test bodies use a valid JSON prefix (`{"image":"` + padding) rather than all-`x` bytes — the JSON decoder reads only 1 byte of invalid content before failing, so all-`x` never triggers MaxBytesReader; a JSON string value causes the decoder to read the full field before the limit fires
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] Oversized body tests used all-x bytes; fixed to use valid JSON prefix**
- **Found during:** Task 1 GREEN phase
- **Issue:** Test body `make([]byte, 1<<20+1)` filled with `'x'` causes JSON decoder to fail at byte 1 with "invalid character" — MaxBytesReader never triggers because the read count never reaches the limit
- **Fix:** Changed test bodies to `{"image":"` (or `{"name":"`) + `bytes.Repeat([]byte("x"), 1<<20+1)` so the decoder reads past 1MB before encountering an unterminated string
- **Files modified:** pkg/diunwebhook/diunwebhook_test.go
- **Verification:** All 3 oversized-body tests now pass with HTTP 413
- **Committed in:** 98dfd76 (Task 1 feat commit)
---
**Total deviations:** 1 auto-fixed (Rule 1 - test bug)
**Impact on plan:** The fix is necessary for tests to validate what they claim. No scope creep; the handler implementation is exactly as specified.
## Issues Encountered
None beyond the test body deviation documented above.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- DATA-03 and DATA-04 fixed; all Phase 01 plans complete
- Full test suite passes with 0 failures
- Handler hardening pattern (MaxBytesReader + errors.As) established for future handlers
- Ready to transition to Phase 02 (database refactor / PostgreSQL support)
---
*Phase: 01-data-integrity*
*Completed: 2026-03-23*