Repository navigation
[obvious-fleet] Backend: end-to-end capture journey test over the committed mini-arkit packet fixture - #254
Conversation
…-arkit packet An integration test copies the committed data/fixtures/packets/mini-arkit packet, starts create_app() with memory state, volume storage, static auth and a test HMAC secret, and sends the app's own sequence: create capture, register files, PUT bytes to the signed volume URLs, commit, finalize packet.json, and read events. It asserts the persisted bytes under captures/, the stored sha256 hashes, the processing status, and the awaiting_files behavior when one file never uploads. Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Independent review (Obvious, Autobuild + Max) Verdict: ready — the PR does what it claims, nothing regresses, and the tests bite on real-path mutations. Two low/non-blocking notes below. Findings1. [low] The finalized-bytes assertions cannot detect a change to the 2. [nit] Body count off by one — the body reports 3. [context, pre-existing] What I verified (fresh clone at head
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Note Review skipped Codemetrics is installed on this repository, but it hasn't been added to your team yet. To enable AI code reviews for this repo, go to your dashboard and add BaseScanning to your team's repositories. |
[Low impact] Adds an end-to-end test for the capture flow. The PR appears safe to merge, with one non-blocking gap in the event assertion.
|
| assert any( | ||
| (event.get("data") or {}).get("stage") == "validate" | ||
| and (event.get("data") or {}).get("runId") == payload["runId"] | ||
| for event in stage_events | ||
| ), stage_events |
There was a problem hiding this comment.
The event assertion checks stage and runId but never checks status. A validate event with a missing or wrong status would pass, so the test does not prove that the phone receives the expected running event. Add a check for status == "running" to the same predicate.
| assert any( | |
| (event.get("data") or {}).get("stage") == "validate" | |
| and (event.get("data") or {}).get("runId") == payload["runId"] | |
| for event in stage_events | |
| ), stage_events | |
| assert any( | |
| (event.get("data") or {}).get("stage") == "validate" | |
| and (event.get("data") or {}).get("status") == "running" | |
| and (event.get("data") or {}).get("runId") == payload["runId"] | |
| for event in stage_events | |
| ), stage_events |
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_capture_fixture_flow.py
Line: 188-192
Comment:
**Wrong progress status passes**
The event assertion checks `stage` and `runId` but never checks `status`. A `validate` event with a missing or wrong `status` would pass, so the test does not prove that the phone receives the expected running event. Add a check for `status == "running"` to the same predicate.
```suggestion
assert any(
(event.get("data") or {}).get("stage") == "validate"
and (event.get("data") or {}).get("status") == "running"
and (event.get("data") or {}).get("runId") == payload["runId"]
for event in stage_events
), stage_events
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
fleet-packet: burst5-20261015-bs-capture-fixture
What changed and why
The ported backend (
backend/server/api) had unit-style route tests and ane2e uploader test that drives the flow through
scripts/e2e_dev, but no testthat performs the device's own journey end to end over a committed packet
fixture. This adds
backend/tests/test_capture_fixture_flow.py:data/fixtures/packets/mini-arkitpacket (thesmallest committed fixture that exercises real packet validation: 11 files
with real bytes and hashes, arkit tier, streams/features/keyframe roles,
conventions block) into a private per-test copy, so the tracked fixture is
never mutated.
create_app()with memory state, local volume storage, staticauth (
HSS_AUTH_MODE=staticwith one device token) and a testUPLOAD_URL_HMAC_SECRET, matching the README's local-volume recipe.ios/HouseScanKit/Sources/HouseScanKit/Upload/CaptureAPI.swift):POST /v1/captureswithIdempotency-Key,POST .../filesregistration,PUTbytes to the signed
/v1/uploads/{token}URLs (no bearer, content type andMD5 from the signed headers),
POST .../files:commit,POST .../finalizewith raw
packet.jsonplusX-Packet-Sha256, andGET .../events.captures/{captureId}/for every file andcaptures/{captureId}/packet.jsonfor the manifest; stored sha256 hashesequal the fixture hashes; capture status
processingwithfilesCommitted == 11; events arrive in seq order with afiles_committedevent and astage=validate, status=runningeventcarrying the finalize
runId. A second test skips one upload: commitreports the path in
notFound, finalize returns 202 withstatus="awaiting_files"andmissing=[path], the capture staysawaiting_files, and the skipped path is absent from storage.In-process ASGI client vs launching uvicorn
The tests use Starlette's
TestClient(in-process ASGI), not a spawneduvicornsubprocess. The differences that matter here:transfer and concurrency behavior, and process-level env wiring. None of
those are in question at this stage — the port's risk is the app's route,
state, storage and auth logic, not ASGI server plumbing, and the README's
uvicorn recipe is already exercised manually per its local-volume section.
app.state.deps(
drain_finalize, the storage backend, the state backend) so persistedbytes, hashes and status can be asserted directly, plus speed (the whole
file runs in about one second) and no port/CI flakiness. It also covers the
same middleware path a real request takes —
create_app's_Accessmiddleware buffers bodies, checks auth and caps sizes inside the ASGI app,
not inside uvicorn.
For this stage (verifying the ingest contract and the fixture journey) the
in-process client is sufficient; the multipart 501 path and Modal deployment
remain outside its reach either way.
How I verified it
Worked in a fresh clone (
~/work/burst5-20261015-bs-capture-fixture) onobv/burst5-20261015-bs-capture-fixturefromfeat/port-house-scanning-server.uv run --extra api pytest tests/test_capture_fixture_flow.py -q→ 2 passeduv run --extra api pytest tests/test_e2e_upload.py tests/test_capture_fixture_flow.py -q→ 6 passed (existing upload suite green alongside the new test)
uv run --extra api pytest server/api/tests -q→ 123 passeduv run ruff check tests/test_capture_fixture_flow.py→ All checks passeduv run ruff format --check .(backend) → 461 files already formatted(one reformat of the new file was applied first)
(inventory, finalize state, orchestrator review, security, review page) —
none adds a capture-fixture integration test, so this does not duplicate them.
What is not verified
uvicornsubprocess was launched; the in-process path is assertedabove. Socket-level behavior (chunked uploads, timeouts) is unverified.
HSS_INGEST_HOOKS=null(the README's default — theserver.orchestratorstubs still raise), so after finalize the run is created and the capture
reaches exactly
processingwith avalidatestage event; noreconstruction/scene/criteria work runs and no
/resultoutcome isproduced. Multipart upload targets still answer
501 multipart_not_implemented.backend/tests/test_ssr_overlap_check.py::test_repo_has_no_ssr_shingle_overlapfails on this branch, but it fails on the unmodified base branch in a fresh
clone too: it needs
data/hygiene/ssr_shingles.sha256, which lives under thegit-ignored
data/tree and is generated locally. Pre-existing, unrelated.CaptureAPI.swiftitself was only read as therequest-shape contract, not compiled.
Assumptions
mini-arkitis the intended fixture: the other committed packets underdata/fixtures/packets/are larger (320K–900K), andexamples/holds onlypacket JSON with no bytes on disk, so it cannot drive a real upload.
ApiClienthelper and token fromserver/api/tests/conftest.pyfor consistency with the suite's conventions.tmp_pathper test; the tracked fixture isread-only input, per the task's "limited to the fixture flow test and its
private fixture copy".
device:principal, the smallest identity theapp's journey needs.