Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ jobs:
# required shape before the server boots.
run: ./corescope-migrate -db test-fixtures/e2e-fixture.db

- name: Seed deterministic Path Inspector routes (#2060)
run: sqlite3 test-fixtures/e2e-fixture.db < test-fixtures/path-inspector.sql

- name: Start Go server with fixture DB
run: |
fuser -k 13581/tcp 2>/dev/null || true
Expand Down
24 changes: 24 additions & 0 deletions docs/frontend-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,27 @@ classification records that limitation rather than installing them implicitly.
The existing E2E workflow retains its browser installation, local fixture
server, coverage collection, and repeat checks. Only use local servers for
browser tests; inventory membership alone does not mean a suite runs in CI.

## Path Inspector route fixture

`tests/e2e/test-path-inspector-e2e.js` requires the real fixture API. After
freshening and migrating a disposable copy of `test-fixtures/e2e-fixture.db`,
seed it **before starting the server**:

```sh
sqlite3 fixture-copy.db < test-fixtures/path-inspector.sql
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node tests/e2e/test-path-inspector-e2e.js
```

The SQL adds four synthetic GPS repeaters and four persisted neighbor edges.
Prefixes `f20601,f20602,f20603` produce two paths through different middle
repeaters. The suite requires trusted hop evidence, clicks both Show on Map
buttons, checks the real Leaflet segment coordinates, and verifies all previous
route objects leave the map when the candidate changes. Missing seeds fail;
there is no candidate-dependent skip. Rerunning the seed is safe, and it does
not add packets or alter their ordering. CI runs this setup after migration.

The historical claim that low scores remove all candidates is not how the
current inspector works: scores are floored, and path trust marks evidence.
An empty candidate list means at least one prefix has no relay candidate.
Deterministic prefixes avoid relying on whichever graph neighbors sort first.
21 changes: 14 additions & 7 deletions public/route-view.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
/* route-view.css — minimal route view layout + styling. */

body.mc-route-active #leaflet-map { left: 320px !important; width: calc(100% - 320px) !important; }
/* Reserve the timeline's real width in the map's flex row (#2081). Offsetting
the map visually covers the Path Inspector instead of making room for it. */
@media (min-width: 768px) {
#map-wrap > .mc-rt-sidebar {
position: relative;
top: 0;
bottom: auto;
flex-shrink: 1;
}
/* A saved/dragged wide panel must yield space when the viewport narrows.
Match the resize handle's 220px minimum, except for the 36px collapse. */
#map-wrap > .mc-rt-sidebar:not(.mc-rt-collapsed) { min-width: 220px; }
body.mc-route-active #leaflet-map { min-width: 160px; }
}

/* Auto-collapse Map Controls panel when route view opens. The toggle button
(.map-controls-toggle) stays visible — clicking it expands the panel.
Expand Down Expand Up @@ -579,12 +592,6 @@ body.mc-route-active .leaflet-overlay-pane svg path:not(.mc-rt-edge) { display:
transform: translateY(-50%);
}

/* When sidebar is collapsed, expand map to fill */
body.mc-route-active:has(.mc-rt-sidebar.mc-rt-collapsed) #leaflet-map {
left: 36px !important;
width: calc(100% - 36px) !important;
}

@media (max-width: 767px) {
/* Mobile: hide desktop collapse + resize affordances (mobile uses bottom-sheet) */
body.mc-route-active .mc-rt-collapse-btn,
Expand Down
26 changes: 26 additions & 0 deletions test-fixtures/path-inspector.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-- Synthetic Path Inspector topology (#2060). Apply AFTER fixture migration
-- and timestamp freshening, BEFORE starting the read-only server.
-- Three-byte prefixes meet path trust without changing production thresholds.
-- The shared middle prefix offers two distinct GPS routes: Start-North-End
-- and Start-South-End. No packets/observations are added or reordered.
BEGIN;
INSERT INTO nodes (public_key, name, role, lat, lon, first_seen, last_seen, advert_count) VALUES
('f206010000000000000000000000000000000000000000000000000000000000', 'Fixture Path Start', 'repeater', 1.00, 1.00, strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 day'), strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 hour'), 1),
('f206021111111111111111111111111111111111111111111111111111111111', 'Fixture Path North', 'repeater', 1.01, 1.01, strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 day'), strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 hour'), 1),
('f206022222222222222222222222222222222222222222222222222222222222', 'Fixture Path South', 'repeater', 0.99, 1.01, strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 day'), strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 hour'), 1),
('f206030000000000000000000000000000000000000000000000000000000000', 'Fixture Path End', 'repeater', 1.00, 1.02, strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 day'), strftime('%Y-%m-%dT%H:%M:%SZ','now','-1 hour'), 1)
ON CONFLICT(public_key) DO UPDATE SET name=excluded.name, role=excluded.role,
lat=excluded.lat, lon=excluded.lon, first_seen=excluded.first_seen,
last_seen=excluded.last_seen, advert_count=excluded.advert_count;

-- The server loads this persisted graph at startup and refreshes it from the
-- same table. Edge counts affect ranking, not whether candidates exist or
-- whether a prefix is trusted. Saturated counts keep both routes comfortably
-- above the speculative score cutoff without adding synthetic packet rows.
INSERT INTO neighbor_edges (node_a, node_b, count, last_seen) VALUES
('f206010000000000000000000000000000000000000000000000000000000000', 'f206021111111111111111111111111111111111111111111111111111111111', 100, strftime('%Y-%m-%dT%H:%M:%SZ','now')),
('f206010000000000000000000000000000000000000000000000000000000000', 'f206022222222222222222222222222222222222222222222222222222222222', 100, strftime('%Y-%m-%dT%H:%M:%SZ','now')),
('f206021111111111111111111111111111111111111111111111111111111111', 'f206030000000000000000000000000000000000000000000000000000000000', 100, strftime('%Y-%m-%dT%H:%M:%SZ','now')),
('f206022222222222222222222222222222222222222222222222222222222222', 'f206030000000000000000000000000000000000000000000000000000000000', 100, strftime('%Y-%m-%dT%H:%M:%SZ','now'))
ON CONFLICT(node_a, node_b) DO UPDATE SET count=excluded.count, last_seen=excluded.last_seen;
COMMIT;
Loading
Loading