mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-11-11 12:57:40 +00:00
417c8dd00a243f29822f34e5c9aaadbd2e522213
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9534e1c25e |
fix(reach): retry a busy (429) reach report in the reach page (#2250)
Follow-up to #2127: @dborup's point 2 there, and @efiten's non-blocking note on the approval ("worth checking the node page's reach panel shows a retry hint on 429 rather than a generic error"). ## What changes - **The reach page retries a 429 itself.** It shows "Reach is busy computing other reports, retrying in N s", waits `Retry-After` seconds, and tries again. - When the header is missing or is not a number of seconds (an HTTP-date, 0, a negative number), it waits 2 s. It never waits more than 30 s. - After 5 retries it shows the old error ("Failed to load reach: API 429 …"). - Leaving the page stops the retries, both while it waits and while a request is still in flight. Picking another window while a request is in flight also drops that request's retries. - **`api()` does not retry 429.** Login, signup, activation, email change, companion linking, settings PUT and channel proposals also answer 429 from their own limits, and those must not auto-retry. `api()` now puts `status` and `retryAfter` on the errors it throws, so a caller can decide; the message text is unchanged. A unit test pins that `api()` makes exactly one request on a 429. - **Fix in `api()`:** the `_inflight` cleanup chain (`promise.finally(...)`) rejected unhandled on every failed call, so each API error also logged "Uncaught (in promise)". That would have added up to 5 more per busy reach load. The chain now catches; callers still get the rejection through the returned promise. This works with the server as it is on master (instant 429, `Retry-After: 5`), and with the bounded wait in #2249 (429 after 3 s, `Retry-After: 2`). Neither PR depends on the other. ## What I checked - **Suites.** `sh test-all.sh` passes, the XSS sink gate passes, and eslint has 0 errors and no new warnings. - **Unit tests** (`test-frontend-helpers.js`): - `reachRetryDelayMs`: Retry-After values; fallback on missing, zero, negative or HTTP-date values; the 30 s cap; giving up after 5 retries; no retry on 400, 404, 500, 503 or network errors. - `api()`: a 429 error carries `status` and `retryAfter`, makes exactly one request, and leaves no unhandled rejection. - The block sits above the file's `Promise.allSettled(pendingTests)` summary. Async tests registered below that line are never awaited, so they cannot fail the run; nothing below it is async today. - **E2E** (`test-node-reach-e2e.js`, already in CI suite 2): reach requests are answered through `page.route`, so the server does not have to be made busy. It checks: - two 429s, then the real report: the hint shows "retrying in 1 s", the retries wait (at least 1.8 s between the first and third request), and no error is shown; - always 429: 6 requests (1 + 5 retries), then the error; - leaving while it waits: no more requests; - leaving while the first request is still in flight: no hint written, no retry; - a 500 with Retry-After: one request, no retry. On master's frontend the new section fails at the first busy hint. Locally against the freshened `e2e-fixture.db`, the whole file takes about 17 s (2 runs). - **Mutations.** I wrote 15 from the code, and the tests caught all 15. They include: `api()` drops `status` or `retryAfter`, `api()` retries 429 itself, retry any error, retry limit off by one, fallback or cap changed, Retry-After 0 accepted, either "left the page" check removed, the wait not honoured, the hint showing ms, the retry count ignored, no error after giving up, and the cleanup chain unhandled again. Each mutation was checked against the file the server actually served before the tests ran. - **Real browser** (Chromium via Playwright, on a copy of a 10.5M-observation database): - Current master's server, 6 tabs opened at once on uncached 14-day reports: 4 got 429 with `Retry-After: 5` and showed the hint, and all 6 reports rendered within 11.5 s (one tab retried twice). Without this change those 4 tabs show "Failed to load reach: API 429". - With #2249's server: 4 of 6 got 429 with `Retry-After: 2`, and all rendered within 8.1 s. - The only console errors were Chrome's own "Failed to load resource" lines for the 429s; there was no "Uncaught (in promise)". ## Not checked - **Mobile layout.** The hint reuses the existing `.nq-load` style, so I did not take a screenshot at phone width. - **Other browsers.** Firefox and Safari were not tried; the code is plain `setTimeout` / `await`. - **Window switches.** While the hint is showing, the time-range buttons are not on screen; the same is true of the existing error and loading states. Picking a new window means waiting, or going back and opening reach again. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: nullrouten0 <201262769+nullrouten0@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
25f8426d32 |
test: make every suite in tests/e2e run, and tell the truth when it does (#2053)
Closes #2037. Step 1 (#2045) wired in the nine suites that already passed. This is steps 2 and 3: the four that ran and failed, and the five that could not run at all. After this, the count of suites in `tests/e2e` invoked by nothing goes from 18 to 0. ## Step 2 — the four that ran and failed Triaged against a fixture server in CI with full output kept, not the four-line tail the first probe saved. | suite | verdict | |---|---| | `test-packets-scope-column.js` | passes on master today. It failed on 2026-09-18, so something between the two fixed it. Wired in unchanged rather than investigated. | | `test-node-reach-e2e.js` | test wrong. It waited for the reach map whenever any *link* had GPS; `public/node-reach.js` builds the map only when the *node* has coordinates. Guaranteed 10s timeout on a node with positioned neighbours and no position of its own. | | `test-channel-modal-e2e.js` | both failures test-side. The Add button's visible label was shortened to "+ Add" with the accessible name moved to `aria-label` (`public/channels.js:746`); and `.ch-section-mychannels` is conditional on the visitor having added a channel, which has not happened at that point in the suite. | | `test-touch-targets.js` | three test-side, two a product finding. | The three test-side touch-target failures were the harness measuring controls that are not shown: `.compare-btn` (the CTA was removed in #1646, and `style.css` says so), `.ch-back-btn` (`display:none` outside the mobile channels layout), and `.filter-toggle-btn` (`display:none` on mobile since #1461; the control shown is the navbar mirror, which `mobile-page-actions.js:70` builds as a `.nav-btn`, so it was already measured). All three are dropped from the table with the reason recorded in the file. The remaining two are **not** a test problem: `.nav-btn` and `.ch-icon-btn` are each declared twice in `public/style.css`, 48px in the touch-target block and 44px in their own component rule, and the later one wins. Rather than lower the blanket or hide the failures, the suite now has `DEFAULT_MIN = 48` plus a `MIN_OVERRIDES` table holding those two at their effective 44, so a third selector dropping to 44 still fails the build. The contradiction is **#2052**, with both ways out costed; the override entries should go when it is settled. ## Step 3 — the five that could not run None is deleted. I checked each selector and seam against the product before deciding, and every one still targets a surface that exists and that nothing else covers. Four were written against `@playwright/test`, a runner the project neither installs nor uses anywhere else. Adopting a second runner for twelve tests costs more than porting them, and the precedent is already set: `test-path-inspector-coverage-e2e.js` exists, as its own header says, because `test-path-inspector-e2e.js` could not run. So they are ported to the plain-node Chromium pattern the other 109 suites use. - **`test-issue-1522-trace-url-sync-e2e.js`** — the trace hash in the URL, both directions. `test-e2e-playwright.js` covers that the page loads and searches; it never looks at the URL, which is the whole of #1522. - **`test-marker-outline-weight.js`** — the canvas pulse ring never thins below 2px. There is no CSS rule to read and axe cannot see inside a canvas, so sampling the seam is the only way. Added a guard that the ring was actually visible, so the weight check cannot pass vacuously on a pulse that never rendered. - **`test-pr-1490-live-map-gpu-animations-e2e.js`** — the queue drains, the engine sleeps again, the fading trails stay under the cap of 5, and the canvas sits on `animationsPane` rather than under the markers. - **`test-path-inspector-e2e.js`** — reduced to what nothing else covers: the map side pane, the `/#/traces/<hash>` redirect, the tools landing. Its standalone-page test duplicated the wired coverage suite and is dropped. Its "switching candidate clears prior polyline" case ended after the click with a comment and no assertion, which is the same green-but-empty problem this issue is about; it now compares path counts, and skips loudly when the fixture yields too few candidates. The fifth, **`test-table-sort.js`**, needed `jsdom`, which was declared nowhere. It is a unit test of `public/table-sort.js` filed under `tests/e2e`, so: `jsdom` is a devDependency (lockfile updated, `npm ci` stays consistent), the file moved to `tests/unit/`, and the `domIntegration` group in `scripts/non-unit-tests.json` is gone with its only member. It runs 22 tests. 20 passed immediately; 2 had rotted, because #1648 M2 replaced the up/down glyphs with Phosphor sprites and the direction moved out of `textContent` into the `<use href>`. Those two now read the sprite ref and the `aria-sort` value, so they also guard the accessible announcement. ## Verification `tests/unit/test-table-sort.js` 22/22 and `test-test-inventory.js` pass locally; the E2E suites need a fixture server, which I cannot build here (no cgo toolchain since #1992), so CI is their first run as committed. The triage above was measured in CI, not assumed. ## Not done The per-assertion skips named in the second comment on #2037 are untouched: the two flaky packet-detail cases, the fixture-data ones, and the two `clientRxCoverage` suites that skip wholesale while reporting success. Those need a fixture deployment with coverage enabled, which is its own change. I have not opened it. Option 2 from the issue, making `test-test-inventory.js` require a `deploy.yml` line for every `tests/e2e` file, is also not here. It is the right guard and it is now enforceable, since the list is finally at zero, but it belongs in its own change where a red build means what it says. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
893773338e |
chore(tests): move root test-*.js into tests/unit and tests/e2e (#2036)
Moves 290 root test-*.js into tests/unit (177, listed in test-all.sh) and tests/e2e (113, classified in scripts/non-unit-tests.json), per #1981 and PR-D of #1385. Root goes from 348 entries to 48. test-all.sh and test-fixtures/ stay put. The inventory guard now fails if a test reappears in the root or sits in the wrong folder.
Verified independently of the diff: the invoked sets are unchanged (test-all.sh 177 before and after, deploy.yml 96 before and after, both identical as sets), and a full local run of test-all.sh on master and on the branch produced 4702 output lines each whose only differences are absolute paths, stack-trace line numbers shifted by the REPO_ROOT line, the inventory wording and two perf ratios. The guard was mutation-checked: a test back in the root, a unit suite in tests/e2e, and a suite dropped from test-all.sh each make it exit 1. CI run 35246304316 ran 97 suites from tests/e2e and is green.
Follow-up
|