T22/T24/T26: uploader, integration tests, and release readiness

T22 -- TelemetryUploader on package:http with MockClient standing in for
MockWebServer, tested against a real in-memory Drift database rather than a fake
DAO. Upload runs on its own timer, injected as a callback so the engine has no
opinion about HTTP and tests need no network. Config on shared_preferences with
a hand-rolled UUID v4. Still no UI for the endpoint, exactly as in the native
app. 7 tests.

T24 -- integration_test/app_test.dart, 4 tests passing on the iOS simulator.
These cover what widget tests cannot: Drift opening against real platform
storage, plugin registration, go_router driving a real Navigator, cold start.

T26 -- RELEASE-IOS.md. Usage strings are specific rather than generic, which is
the leading Guideline 5.1.1 rejection cause; privacy-label answers decided; a
pre-submission list covering the bundle-id switch back to com.rippr, the still
default app icon, a release build, and a demo video for review notes.

T25 written up as REAL-RIDE-CHECKLIST.md but outstanding by nature. Two items
decide real things: force-stopping mid-ride on Android confirms the 111 km
crash-gap bug is fixed, and parking 15 minutes mid-recording on iOS decides
whether geolocator is sufficient or the paid engine is needed.

Two prefer_initializing_formals lints suppressed with a reason: Dart forbids a
named parameter beginning with an underscore, so the suggested fix will not
compile.

178 tests passing, analyze clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 22:20:39 -05:00
parent d501528e69
commit a3bcd014c0
10 changed files with 732 additions and 4 deletions

View File

@@ -661,3 +661,79 @@ Phases 0–4 are complete. What is left is verification and cutover:
**Known parity gap so far:** notification actions (Pause/Resume in the shade), lost with
`flutter_foreground_task`.
---
## Phase 5 — Verification
### T22 — Uploader and config · **complete**
`lib/src/telemetry/telemetry_uploader.dart` and `lib/src/config/config.dart`. **7 tests.**
`package:http` with a `MockClient` replaces OkHttp with MockWebServer, and the tests run
against a real in-memory Drift database rather than a fake DAO — closer to production and
still no device. Batching, retry, the offline-safe backlog via `synced`, and per-point
trip/segment identity all carried over.
Upload runs on **its own timer** in the engine, injected as a callback so the engine has
no opinion about HTTP and tests need no network. Every failure path is swallowed: nothing
about uploading may disturb recording.
`Config` uses `shared_preferences`, with a hand-rolled UUID v4 rather than a package for
sixteen bytes. `mapEnabled` now comes from preferences instead of being hardcoded.
**Parity note:** there is still **no UI** for the endpoint, exactly as in the native app.
> Two `prefer_initializing_formals` lints are suppressed with a reason: Dart does not
> permit a named parameter whose name begins with an underscore, so the lint's suggested
> fix does not compile.
### T23 — Widget tests · **complete** (delivered in Phase 4)
### T24 — Integration tests · **complete**
`integration_test/app_test.dart`. **4 tests, passing on the iOS simulator.**
These cover what widget tests structurally cannot: Drift opening against real platform
storage, plugin registration resolving, `go_router` driving a real Navigator, and a cold
start surviving. The database test in particular is meaningful — an in-memory Drift
instance cannot prove the sqlite3 native library loaded on the device.
```
flutter test integration_test -d <device-id> → 00:50 +4: All tests passed!
```
**Not yet run on the Android emulator**, which needs 7.4 GiB free to boot; run
`flutter clean` first.
### T25 — Real ride · **outstanding, and it is the important one**
Written up as [REAL-RIDE-CHECKLIST.md](REAL-RIDE-CHECKLIST.md): eleven shared checks, three
Android-only, five iOS-only.
Three things in it are worth calling out:
- **A2 (Android):** force-stop mid-ride and confirm the dead time is *not* added to
distance — the native bug found in T11, measured at 111 km.
- **I3 (iOS):** park for fifteen minutes mid-recording and see whether recording resumes.
**This decides the platform strategy.** If `geolocator` loses rides to suspension, the
`LocationSource` seam exists so `flutter_background_geolocation` (~$500/yr) can replace
it as one new implementation.
- **Record the Android ride on both apps at once.** They have different application ids
precisely so they can coexist, and a direct numeric comparison is far stronger evidence
than either app alone.
### T26 — iOS release readiness · **configured, not submitted**
[RELEASE-IOS.md](RELEASE-IOS.md). Usage strings are written specifically rather than
generically (the leading 5.1.1 rejection cause), privacy-label answers are decided, and
the pre-submission list covers the bundle-id switch, the still-default app icon, a release
build, and a demo video for the review notes.
---
## Phase 5 status
**T22, T23, T24 complete. T25 and T26 need a real device and a real rider.**
`178 tests passing` (171 unit + widget, plus 4 integration on device), analyze clean.