From bdf5f647d4bd383142a7cfd7f171592ccbda0614 Mon Sep 17 00:00:00 2001 From: efiten Date: Fri, 19 Jun 2026 08:50:08 +0200 Subject: [PATCH] fix(ci): freshen all e2e-fixture observation timestamps (unblocks #1630 reach e2e) (#1747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem `test-issue-1630-reach-mobile-e2e.js` has been failing on `master` since ~2026-06-16, in its precondition `pickRepeaterWithReach` ("no repeater with reach links found in fixture") — not in any of its actual assertions. The same SHA passed on 06-15 and failed on 06-16 with no code change, i.e. it tracks the wall clock, not the tree. ## Root cause `tools/freshen-fixture.sh` shifts `nodes`, `transmissions`, `observers` and `neighbor_edges` timestamps to ~now, but for `observations.timestamp` it only rewrote rows where `timestamp = 0 OR timestamp IS NULL`. Real-timestamped observations stayed frozen at fixture-capture time. Per-node reach (`/api/nodes/{pk}/reach?days=N` → `scanReachRows`, `cmd/server/node_reach.go`) windows on `observations.timestamp >= sinceEpoch`. ~30 days after the fixture was captured, the newest observation aged out of the 30-day window, so reach returned no links and the test could find no repeater with reach. ## Fix Shift all non-zero `observations.timestamp` forward by the same offset (preserving relative order), mirroring the other tables in the script. The offset subquery is uncorrelated, so SQLite evaluates `MAX` once on the pre-update state (same idiom the existing blocks rely on). ## Verification Ran the updated script against `test-fixtures/e2e-fixture.db`: ``` BEFORE: newest observation 31 days old → outside the 30-day reach window AFTER : newest observation 0 days old, all 500 observations within 30 days ``` CI Playwright on this PR is the end-to-end confirmation. Scope is the CI fixture helper only — no application code, schema, or runtime behaviour changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Erwin Fiten Co-authored-by: Claude Opus 4.8 (1M context) --- tools/freshen-fixture.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/freshen-fixture.sh b/tools/freshen-fixture.sh index 5c1dcc21..c0574be3 100755 --- a/tools/freshen-fixture.sh +++ b/tools/freshen-fixture.sh @@ -23,7 +23,18 @@ UPDATE transmissions SET first_seen = strftime('%Y-%m-%dT%H:%M:%SZ', first_seen, (SELECT printf('+%d seconds', CAST((julianday('now') - julianday(MAX(first_seen))) * 86400 AS INTEGER)) FROM transmissions) ) WHERE first_seen IS NOT NULL; --- Sync observations.timestamp (Unix seconds) to match their transmission's freshened first_seen. +-- Shift all real (non-zero) observation timestamps (Unix seconds) forward so the +-- newest is ~now, preserving relative ordering — same approach as the columns +-- above. Per-node reach (/api/nodes/{pk}/reach?days=N) filters on +-- observations.timestamp >= sinceEpoch, so without this the fixture's observations +-- age out of the window ~N days after capture and reach returns no links (the +-- #1630 reach-mobile e2e then can't find a repeater with reach and fails). The +-- subquery is uncorrelated, so SQLite evaluates MAX once on the pre-update state. +UPDATE observations SET timestamp = timestamp + + (SELECT CAST(strftime('%s', 'now') AS INTEGER) - MAX(timestamp) FROM observations WHERE timestamp > 0) +WHERE timestamp > 0; + +-- Sync observations.timestamp to match their transmission's freshened first_seen. -- Observations with timestamp=0 break the SQL since-filter in buildTransmissionWhere. UPDATE observations SET timestamp = CAST(strftime('%s', (SELECT first_seen FROM transmissions WHERE id = transmission_id)