Commit Graph
14 Commits
Author SHA1 Message Date
Marc-André Bombeck a3561b4600 Tell a first-run what Apple Health is actually doing
Refs #778. Two honesty gaps from the first-run report: the web onboarding
presented Apple Health like a web connector although the connection only
completes in the iPhone app, and the initial backfill gave no sign of
progress, so a working first sync looked broken.

The onboarding source step now says plainly that Apple Health is set up in
the iPhone app after onboarding, without dead-ending the flow. The
integration card gains a server-authoritative progress summary — rows
accepted, oldest reading reached, and an honest flowing/waiting line
derived from the last accepted batch. Nothing the server does not know is
shown: no percentage, no ETA, no invented throttle state; a quiet stretch
reads as waiting for the phone, not as an error.
2026-08-14 09:46:24 +02:00
Marc-André Bombeck ff61aec51f fix(e2e): repair the two integration-card assertions the sweep left behind
Both ends of the divider removal were green and the pipe between them was
never run. The `<hr>` came out of every integration card, all three unit
tests were updated to expect none, and this browser spec kept asserting nine.
It is the same shape as the failure this project keeps rediscovering: two
correct halves and nothing exercising the assembly.

The cost was not just a red spec. The Pixel-5 horizontal-overflow assertion
sits AFTER it in the same test, so `/settings/integrations` had no proven
mobile-overflow check for the whole sweep — the test never reached it.

The second assertion read a sentence's bytes and broke on the text diet while
the thing it was about — the card rendering its explainer at all — never
changed. `SettingsCardHeader` now names its description slot
(`data-slot="settings-card-description"`) and the spec targets that: the slot
is present, non-empty, and not a raw i18n key. The product name stays
assertable because a proper noun is not prose. The wording itself is pinned
where it belongs, in the unit test that reads the same bundle it renders from.
2026-08-07 23:19:24 +02:00
Marc-André Bombeck a23ee61184 test(e2e): route cleanup happens by construction, not per spec
A spec that arms `page.route(...)` in a `beforeEach` and never drops the
handler races its own teardown. The context closes, an async handler is
still awaiting, its next call rejects with "Test ended.", and Playwright
attributes that to the test that had just passed. With `retries: 2` plus
`failOnFlakyTests`, which spec bites is a matter of timing.

`e2e/setup/test.ts` re-exports Playwright's `test` extended with an auto
fixture that unroutes every page and the context after each test, and
clears the page event listeners a spec subscribes to; `expect` rides
along so a spec needs one import. Auto means a spec author cannot forget
it and does not have to know it exists. Every spec now takes `test` from
there, and `e2e-route-cleanup-guard.test.ts` keeps the import boundary
closed with an allowlist that carries its reason.

The per-file `afterEach` on the cycle spec goes away — leaving both would
read as not trusting the fixture.

Not covered: a page inside a context a spec builds itself. Only
`share-documents.spec.ts` does that, and it owns those contexts.
2026-07-25 23:02:03 +02:00
Marc-André Bombeck d0b45e36b3 feat(integrations): remove the moodLog integration
Mood has been a first-class module here for a long time, with entries,
structured tags, rated factors and notes. The bridge to the external moodLog
service had nothing left to add, and its Settings card was withdrawn in
a56e6fe61 on the promise of a dedicated removal. This delivers it.

Removed: the hourly `moodlog-sync` queue, cron and handler binding; the
inbound webhook and its shared-secret handling; the reverse push and its four
call sites; the pull-sync module; the per-user credential routes and the
instance-wide toggle; the entry on the `/api/integrations/status` envelope,
the `IntegrationKey` union, the cadence map and the display-name table; the
boot-time secret rotation pass; 22 message keys per locale.

Migration 0271 drops the five `users` credential columns and
`app_settings.mood_log_global`, clears the ledger row for the provider, and
deletes the retired pg-boss schedule so no cron keeps enqueuing work no
handler drains.

Mood data is untouched. `mood_entries` is not named anywhere in the migration
and cannot be reached from it: the dropped columns carry no index, constraint
or foreign key, and `mood_entries.user_id` references `users(id)`, not any of
them. Rows the bridge imported keep `source = 'MOODLOG'`, and every read path
- list filter, source badge, exports, backups - still resolves that value.

`src/lib/validations/moodlog.ts` held the native mood-entry schemas alongside
the bridge's own; it is now `validations/mood.ts` and carries only the former.
2026-07-25 11:36:18 +02:00
Marc-André Bombeck 10593a11a8 test(integrations): cover the verdict ladder and the coverage guarantee
The load-bearing one is the panel-coverage test: render the panel with an
envelope carrying every integration key and require each configured one to
surface as a card or as a fallback row. Drop the fallback row and it goes red,
which is the whole point — the previous shape of this bug was a provider on
the wire that no screen rendered, and no card-shaped test could have caught it.

Alongside it: the ladder table with the month-silent fixture that resolves to
stalled, the 24-hour and 7-day boundaries asserted against literals rather than
the constants they check, the per-metric stale flag firing only under a healthy
provider, and a structural guard that the verdict-to-pill mapping is defined in
exactly one module.

The card suites migrate with the semantics: a transient failure now expects the
warning pill everywhere, and the Apple Health suite trades its bespoke badge
states for pill assertions. The e2e mocks gain the verdict shape, the
nightscout entry, and an assertion that the card-less row is on the page.
2026-07-25 09:41:53 +02:00
Marc-André Bombeck 62eb431d66 release: v1.32.0 — hardening and scalability (#580)
* release: prepare v1.32.0

* test: stabilize water capture re-entry
2026-07-22 04:14:15 +02:00
Marc-André Bombeck 75eb0637e6 feat(strava): add per-user BYO-OAuth workout source + Garmin indirect-path docs
Add Strava as a per-user BYO-OAuth workout source, mirroring the shipped
WHOOP/Oura/Polar pattern. Strava caps every new app at single-player mode, so
each self-hoster registers their own app and pastes the client id/secret into
Settings (falling back to a shared env app). Strava is a workout source only —
it exposes no daily metrics and no training-depth analytics are derived.

- Schema (migration 0233): per-user encrypted client id/secret + access/refresh
  tokens, plaintext athlete id, incremental cursor, and backfill marker on
  `User`; `STRAVA` added to `MeasurementSource`.
- Token model follows Oura's reactive-refresh-on-401 with a compare-and-set on
  the stored refresh ciphertext, because Strava rotates its refresh token on
  every refresh.
- Client over safeFetch: authorization-code + refresh, activity list + budget-
  aware per-activity detail (for calories/HR), honouring the 200/15min cap; each
  activity maps to one `Workout` row. Activity name/description stay in metadata
  as data, never surfaced or interpolated.
- Dedup reuses the existing source-agnostic two-layer picker: `STRAVA` slots
  into the workout ladder below the device-native wearables and above
  MANUAL/IMPORT, so an Apple-Watch + Strava twin collapses to one canonical row.
- Poll-cohort hourly sync (:17) + self-converging boot backfill, wired into the
  integration-sync registrar. Webhook deferred.
- OAuth routes (connect/callback/credentials/disconnect/status/test) with signed
  state, cross-account re-auth rejection, and a surgical `www.strava.com`
  connect-src gated to the Strava surfaces.
- Settings card + consolidated status envelope entry; env + compose whitelist;
  i18n across all six locales.
- Garmin: no direct connector is possible (business-partner-only). Add
  docs/integrations/garmin.md documenting the Apple Health / Google Health
  Connect path and Garmin's withheld proprietary scores, cross-linked from both
  platform docs, plus a non-connector info note in the Connections panel.

Regenerate docs/api/openapi.yaml for the new `STRAVA` MeasurementSource value.
2026-07-09 21:25:25 +02:00
Marc-André Bombeck 951a8cf9ea test(e2e): count the Google Health status pill on the integrations page 2026-07-04 14:26:36 +02:00
Marc-André Bombeck 1d3ee1d93c test(integrations): expect six integration pills and drop unused field maps
The mobile settings test asserted three integration pills; Polar, Oura and
Nightscout bring the list to six. Remove the unused Oura and Polar field-map
documentation constants and the unused advanced-indices interface the dead-code
gate flagged.
2026-06-14 21:46:20 +02:00
Marc-André Bombeck ab1eea7f23 test(e2e): align export and integrations specs with removed surfaces
Drop the obsolete doctor-report dialog spec (the interactive export-page
dialog was removed; the report now lives under the health-record export),
remove the doctor-report testid from the export smoke, and update the
integrations layout spec to three cards after the moodLog removal.
2026-06-06 23:40:10 +02:00
Marc-André Bombeck 2d6603cd54 test(e2e): align integration fixture with the consolidated status envelope
The settings cards now read connection state from the consolidated
/api/integrations/status envelope (the per-provider status routes were dropped
from the section), so the mobile-layout fixture must supply the per-integration
connected/configured fields the pills key on.
2026-06-05 04:08:01 +02:00
Marc-André Bombeck 3186ecbe67 fix(settings): raise the Google Health tag contrast and update the integration-count assertion
The Fitbit & Pixel tag chip paired muted text on a muted background (below
WCAG AA); use foreground text. Update the mobile integrations e2e to expect
the fourth integration card (Google Health).
2026-06-05 01:33:42 +02:00
Marc-André Bombeck 98693cda6a test(e2e): expect three integration pills on mobile after WHOOP card
The WHOOP integration card adds a third status pill on
/settings/integrations. Update the Pixel-5 layout spec to expect three
pills and assert the two connected ones (Withings + moodLog) order-
independently, since WHOOP stays a dormant BYO-keys card until an
operator adds credentials.
2026-06-04 05:00:13 +02:00
Marc-André BombeckandClaude Opus 4.7 47a8fc7311 test(integrations): Pixel-5 e2e for consolidated status pill
Pins three contracts at the Pixel-5 viewport that the Vitest specs
can't see: no horizontal page scroll, exactly one pill per card with
the connected `data-state` marker, and the v1.4.18 redundant banner
is gone from the rendered DOM. Stubs all three integration endpoints
so no real Withings/Mood Log credentials are needed in CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 13:05:42 +02:00