# Firmware upload backend host tests Run from the repository root: ```sh python3 tests/web_firmware_update/run.py python3 tests/web_cookie_auth/run.py ``` The first compiles the unchanged production upload source, production auth admission helpers/parser, and production server upload reservation/typed reboot functions with `-Wall -Wextra -Werror` and trapping undefined-behavior checks. It uses installed ESP-IDF image-layout headers (`IDF_PATH`, otherwise the normal PlatformIO framework directory). It performs no firmware build or device action. HTTP IO, private header-adapter admission, database, OTA/flash and FreeRTOS scheduling are doubles. The existing cookie-auth suite separately exercises production private-adapter framing/duplicate-header handling and session storage. The 61 cases cover: - Cookie, Origin, CSRF, admin, query/method and readiness rejection before reads. - Raw content type, nonempty body, actual partition capacity and inactive OTA app selection; bytewise partial image prefix and bounded/exact streaming writes. - Wrong magic/chip/segment/descriptor/hash-header rejection before OTA erase. - Service/identity busy, allocation/task creation and OTA begin/write/end/metadata/ boot-selection failures; handle consumption versus abort cleanup. - Parsed SDK image length mismatch, final session revocation/currentness failure. - EOF before/after OTA begin, stalled receive and total slow-drip deadlines. - Reservation rejection of competing upload/typed reboot, including during receive and boot selection; response failure at every response stage. - Successful commit followed by delayed reboot-owner action; failed response retains selection but does not reboot and releases reservations/resources. These are deterministic failure-injection tests, not real SDK image-integrity, flash, concurrent scheduler, TLS, network, power-loss or hardware test evidence. Stop/start/rotation guard use is source-checked, not dynamically executed here. ## Parent integration contract `POST /api/firmware`, raw `application/octet-stream`, known `Content-Length`, existing cookie + same-origin `Origin` + `X-CSRF-Token`, admin only. Use the existing browser CORS-mode/same-origin-credentials request pattern. No multipart, chunked transfer, `Expect`, query parameters, signature/version policy or filename validation. Standard ESP32-S3 application image with appended SDK SHA-256 digest is required. A filename alone is never trusted. `200 {"ok":true,"rebooting":true}` means SDK validation and boot selection succeeded. After synchronous send success, the preallocated owner task waits 500 ms and calls `esp_restart()` without stopping HTTPD or retaining a request or socket. It keeps service/identity reservations until reset. Send success is not peer receipt. Send failure after selection schedules **no reboot**, releases reservations, and leaves the accepted image selected for a later reset. A lost acknowledgement is therefore uncertain; never automatically retry. All errors use `{"error":"code"}`: | HTTP | Codes | | --- | --- | | 400 | `invalid_request`, `invalid_firmware`, `firmware_incomplete` | | 401 | `authentication_required` | | 403 | `origin`, `csrf`, `admin_required` | | 408 | `firmware_timeout` | | 413 | `firmware_too_large` | | 415 | `firmware_content_type` | | 500 | `firmware_write_failed`, `firmware_commit_failed` | | 503 | `unavailable`, `busy`, `firmware_unavailable`, `firmware_resources` | Rejections with unread bodies close rather than asking HTTPD to discard the remaining upload. Responses are JSON, no-store, nosniff and no-referrer. Errors contain no SDK diagnostic text or secret material. A boot-selection API failure is not retried or followed by reboot; flash/power failures at metadata commit cannot offer transactional certainty beyond SDK guarantees. The synchronous handler may block other HTTPD work for the upload duration. A 4 KiB heap buffer and a 2 KiB reboot-owner stack are prepared before flashing; no whole-image allocation. Receive checks enforce 10 seconds without progress and 120 seconds total, using the server's existing one-second receive timeout. These checks do not preempt SDK flash/validation calls or scheduler delays. Only the inactive app and SDK OTA metadata are written, not NVS/data partitions. ### Integration work outside backend ownership - Server URI capacity is now **40**; `server_lifecycle.py` currently fails its hardcoded 36-initializer extraction check (now 37). Its old 39-handler capacity, required-registration positions 1–16 and optional positions 17–18 also need adjustment to 40, 1–17 and 18–19, respectively, plus a firmware-handler fake. That existing fixture is intentionally not edited here. - HTTPS stop/start/rotation and typed reboot share the upload reservation; direct identity mutation is separately reserved. **Direct `esp_restart()` callers in `system_console.c`, `ssh_transport.c`, `web_admin_transport.c`, and `local_status_ui.c` do not share this fence.** Those files were outside the backend's allowed ownership. The parent must coordinate those reboot paths for global software-reboot exclusion; do not claim that property yet.