Commit Graph
9 Commits
Author SHA1 Message Date
Marc-André Bombeck d252940381 Give the just-in spec a record that expects sleep
The scenario purges every sleep row so last night can be the arrival that
flips the day. That left an account with no sleep source at all, which now
reads `final` on purpose, so the provisional assertion could not be reached.

Seeding a night from three days ago restores what the scenario means: a
record that does track sleep, waiting for last night's. Written through the
same ingest route as the fresh night and swept by the same purge.
2026-08-16 09:56:12 +02:00
Marc-André Bombeck d3d32ff8e0 Reset the today-just-in fixture through the app, not behind its back
The mobile project read the day as final on a table with no sleep in it.
The rows were gone; what was left was the dashboard snapshot the desktop
run built from them. It carries the sleep-freshness the digest reads, it
lives in the analytics SWR bucket at sixty seconds fresh and an hour of
stale-serve, and a DELETE straight into Postgres does not touch it. So
the second project asked whether last night's sleep was in, and a cached
answer from the first project said yes.

The purge now goes through the bulk-delete route. That tombstones the
rows the way the management list does, recomputes the rollup buckets the
snapshot reads through, and hard-evicts the bucket, so the next read
rebuilds from the table rather than from the run before it. The
delete-by-external-id route names the rows the way this spec does, but it
invalidates without evicting, and a marked-stale cell is served while it
revalidates - the next read would still have been the stale one. The ids
come from SQL because the measurements list collapses overlapping sources
to one row per day and can hide a row that still counts.

The purge stays where it is rather than moving into the cleanup block:
the eviction only happens when the delete removes something, so tidying
up at the end of each run would leave the next one with nothing to delete
and the same stale snapshot.

The starting state is asserted, not assumed, and it stays that way.
2026-08-01 19:33:27 +02:00
Marc-André Bombeck 96c10f07e6 Give the today-just-in spec an account that earns a hero
The spec watched the day flip from provisional to final on a hero that
only its own subject was keeping on screen: the arrival marker was the
last thing in the calm-degrade guard holding the hero up for the bare
fixture account, so removing the arrival chip took the hero with it and
left the spec nothing to observe.

Putting the arrival back into the guard would bring back a worse
problem. An arrival-only digest lands in the compact all-clear
composition, which would tell someone with no readings at all that
everything is clear - a claim about their health made from nothing, and
made only on the days something happened to sync. The guard stays as it
is; the fixture was what was wrong.

So the spec now seeds its own precondition: one integration needing a
reconnect, which is an ordinary Today for anyone whose readings come
from a device. It is read fresh on every request rather than through the
dashboard snapshot cache, so unlike a seeded score or briefing it cannot
lose a race with a cached snapshot, and the rail item admits no module
gate and no dismiss path. The row is removed again afterwards.

While the spec was open: it now pins the state it starts from. It only
ever asserted that the pending-sleep note was absent at the end, which a
note that never rendered also satisfies. It asserts the note is up and
the day reads provisional first, so the two halves are one assertion
with the number flipped.

The component side is pinned in unit tests, since the spec needs a built
app and a database to run.
2026-08-01 13:21:04 +02:00
Marc-André Bombeck 4e934a191d Drop the just-in line from the Today hero
Under a number, 'Just in' did not say what had arrived - the score? a
reading? - and the clock face beside it changed nothing a person would
then do. The line is gone, and with it the only wall clock this surface
rendered.

What it was there for survives: the day still flips in place when last
night's sleep lands, and the pending-night note still says the number is
reading an incomplete day. An arrival on its own is no longer a reason
to paint a hero at all, so an account whose only news was the arrival
now gets nothing rather than a bordered empty card.

The digest still carries the arrival, which the morning line and the
wide event both read.
2026-08-01 11:09:37 +02:00
Marc-André Bombeck 92218ccc00 test(dashboard): isolate arrival reaction browser coverage 2026-07-29 16:34:58 +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 4591899657 fix(release): harden v1.31 arrival contracts 2026-07-20 00:40:20 +02:00
Marc-André Bombeck c97ad3ed55 fix(release): harden Today arrival rendering 2026-07-19 19:55:40 +02:00
Marc-André Bombeck 663b3530dd test(daily): pin the just-in window, the degrade floor, and the hydration edge
Three properties, each with a mutation-verified guard:

The chip and the line run on DIFFERENT clocks and must keep doing so. The chip
expires after three hours; the line stands for the rest of the local day.
Collapsing them onto one clock reads like a simplification and silently deletes
the day's read at lunchtime.

Degradation is the headline. For every reason a line can fail to exist — no
provider, no consent, an exhausted budget, a dead provider, an unusable output
— the row stays line-less, the reservation is reconciled, and the chip and the
provisional-to-final flip still work. The degraded digest is built through the
real composer rather than a hand-shaped fixture, so the assertions cannot agree
with themselves.

The server pass carries no wall-clock time. The hero paints on the SSR pass
because it is the LCP element, so a locale-formatted time inside it would differ
from the browser's — React #418. The tests render through the server snapshot,
which is exactly the pass that must stay clock-free.

The seeded end-to-end spec lands a sleep batch through the real ingest route,
writes the arrival marker, and asserts the chip and the phase flip in place on
a refetch trigger the digest hook already declares — no reload, no new cadence.
2026-07-19 14:13:50 +02:00