Organise the tests, split the app by concern, and lint the scripts - #45
Merged
Conversation
src was one 672-line file holding routing, capture, platform, asset and security concerns together, which left most of it reachable only through a listening socket. It is now one file per subject with its tests beside it, and newApplication builds the whole routing surface from arguments so tests drive real requests through it. Tests are grouped by the unit under test with behaviour-stating subtests, and the Playwright suite is grouped by feature area behind a shared harness. Go coverage moves from partial to 81% of src, and the suite covers endpoint-ID validation, client-IP resolution, header stripping, asset versioning, the CSP environment gate, request-ID handling and every route. ESLint now covers the page scripts and the Playwright suite, the latter with type information so an implicit any is an error. The Go commands are scoped to ./src because node_modules ships a stray Go package. Three fixes fall out of this: - Every response now carries its security headers. The static middleware resets the response when a path resolves to a directory, which silently dropped the CSP, X-Frame-Options, nosniff, Referrer-Policy and X-Request-Id from the landing page. They are stamped on the way out, where nothing downstream can discard them. - Controls that act on a whole endpoint report the endpoint. The listing is searched server-side and windowed, so its length is not the endpoint's; with a search active the destructive "Delete all (N)" understated what it would delete, and the filtered-empty panel claimed nothing had been captured. The listing now carries a total. - Counted copy reads correctly at one: "1 request", not "1 requests". Claude-Session: https://claude.ai/code/session_01XRYoAwHndAhq2w7RvxSGoo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A pass over tests, structure, linting and prose, plus three fixes the QA tour turned up.
Tests are organised, then filled in
srcwas one package withapplication_test.goholding several unrelatedconcerns. Tests now sit beside the file they cover, named for the unit under
test with subtests that state a behaviour:
endpoint_test.goplatform_test.gocapture_test.goassets_test.gosecurity_test.gorequestlog_test.gosockets_test.goroutes_test.gologging/logging_test.goThe gaps that mattered were the security-critical ones with no coverage at all:
validEndpointID,resolveClientIP,omitHeader, the CSP's environment gateand the inbound request-ID pattern. Go coverage of
srcgoes from partial to81%,
loggingfrom 0 to 88%.The Playwright suite grows from 26 to 59 tests, grouped by feature area
(Page / Capture stream / Body rendering / Filtering / Copying / Sending / Tab
indicator) behind a shared
tests/support/harness.ts. New ground: the renderwindow, the connection indicator, query-string display and search, escaped HTML
bodies, HAR omissions, contact-form semantics.
Structure
application.gowas 672 lines. It is now one file per subject —platform,endpoint,capture,api,pages,assets,security,sockets— andnewApplicationbuilds the whole routing surface fromarguments, so the route tests drive real requests without a listening socket.
requireValidEndpointreplaces the validation repeated in six handlers, andpageMetareplaces the head fields repeated per page.On the front end,
.paneland.panel-flatwere byte-identical while acomment claimed they differed; there is now one surface class.
.field-labeland
.region-labelshare one type token, and the three places that re-spelledthat token as a utility string use the class.
copyandcopyHarshare theirflash-and-announce logic.
Lint and types
There was no lint setup, so "zero warnings" was unverifiable. Added a flat
ESLint config covering
public/*.js(browser globals, no build step) ande2e/**/*.tswith type-aware rules, so an implicitanyreaching anassertion is an error. Wired into CI alongside
tsc --noEmitandgo vet.Current state, all verified rather than assumed:
eslint-disable: 0@ts-ignore/@ts-expect-error: 0any: 0. One type assertion remains, at theJSON.parseclipboardboundary, confined to a single helper and documented there.
Go commands are scoped to
./src/...:node_modulesships a stray Go packagethat
./...otherwise builds.Prose
No misspellings turned up across comments, markdown and UI copy. The comment
tour removed the bug war stories
AGENTS.mdprohibits (the flicker story, theclick-swallowing header, the select desync) in favour of the constraint each
one protects, corrected
har.js's claim that it loads on every page, andgeneralised two hyper-specific figures.
AGENTS.mdgains a code-layoutsection for the new file split.
Three fixes from the QA tour
Every response carries its security headers. The static middleware resets
the response when a path resolves to a directory, which silently dropped the
CSP,
X-Frame-Options,nosniff,Referrer-PolicyandX-Request-Idfromthe landing page. Confirmed against a production build of
master:They are now stamped on the way out, where nothing downstream can discard them.
Controls that act on a whole endpoint report the endpoint. The listing is
searched server-side and windowed at 128, so its length is not the endpoint's.
With a search active, the destructive
Delete all (N)understated what itwould delete, and the filtered-empty panel said "0 captured on this endpoint"
while three were. The listing now carries a
total.Counted copy reads correctly at one — "Deleted 1 request", not
"1 requests".
QA
Full tour in Chrome plus direct API calls, in development and against the
production container. Landing, contact, endpoint and 404 pages; endpoint
creation; ten capture shapes driven by curl (JSON, XML, text, multipart with a
file, HTML, query strings, every method, infrastructure headers, curl
spoofing); live arrival over WebSocket; search, method filter, the render
window, every copy control, single and bulk delete with confirmation, the send
panel; socket loss and reconnect with the gap-closing refetch. No console
errors. Backend: status codes for malformed endpoint IDs, request-ID echo and
minting, asset hashes tracking content in both modes, the 1 MiB body limit, and
the 125/min production rate limit firing.
Known issue, not fixed here
A request whose body exceeds 1 MiB returns 413 but still stores a capture with
an empty body, beside a
Content-Lengthheader saying otherwise — a cardthat reads "Body: None" for a payload the client did send. It is pre-existing
and unrelated to this change. I left it alone because a correct fix needs care
around fasthttp's body-read semantics across chunked encoding and
Expect: 100-continue, and getting it wrong would drop legitimate captures.Worth its own change.
🤖 Generated with Claude Code