test: make the companion talk to a real API, and fix what that found - #2
Merged
Conversation
Every test in this repo tested a piece in isolation: the parser against fixtures, the watcher against a temp file, the compat rules against invented responses. All of it could pass while the app could not sign in, because nothing here had ever spoken to a server. That is the same gap that once shipped an app whose preload never loaded — every check green, the thing itself dead. So live.test.ts signs in for real against a local API. It builds a PKCE pair, plays the part of the consent page to get a code, exchanges it through the real client for a real token, confirms a code presented with the wrong verifier is refused, lists the guilds that token may write to, uploads a night, and checks that signing out actually revokes rather than merely forgetting. Skipped unless RAIDIFY_E2E_API_URL is set. A suite that needs a database running to pass is a suite people stop running. It cannot point at production either: test-login 404s there, so the first step fails loudly rather than quietly doing something to real data. It found a bug on its first run. Sign-out returns 204, and request<T> parsed every successful response as JSON, so an empty body threw "Unexpected end of JSON input" *after* the server had already revoked the token. The caller's catch block then reported a sign-out that worked perfectly as "offline, or already revoked" — the opposite of what happened. Any future no-content endpoint had the same problem waiting. Also checked the hand-written contract against the live schema, field by field: every name, type and nullability matches, and the enums really are integers on the wire. And schema:pull now keeps only the companion's corner. The full document is 248 paths and 328 schemas — every admin route and internal DTO, a complete map of the private API. This repo may be made public so anyone wary of an unsigned binary can read what it does, and that map must not travel with it. Narrowing costs nothing: the point of the file is noticing a change to the eight endpoints we speak to, and 567KB shrinks to 17KB. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Every test in this repo tested a piece in isolation — the parser against fixtures, the watcher against a temp file, the compat rules against invented responses. All of it could pass while the app could not sign in, because nothing here had ever spoken to a server.
That's the same gap that once shipped an app whose preload never loaded: every check green, the thing itself dead.
What
live.test.tsdoesAgainst a local API, using the real client and the real PKCE implementation:
Skipped unless
RAIDIFY_E2E_API_URLis set. A suite that needs a database running to pass is a suite people stop running. It can't point at production either —test-login404s there, so step 1 fails loudly rather than quietly touching real data.It found a bug on its first run
Sign-out returns 204, and
request<T>parsed every successful response as JSON. The empty body threwUnexpected end of JSON input— after the server had already revoked the token. The caller's catch block then reported a sign-out that worked perfectly as "offline, or already revoked", which is the opposite of what happened. Any future no-content endpoint had the same problem waiting.Contract mirror verified
Checked
src/shared/contract.tsagainst the live OpenAPI document field by field: every name, type and nullability matches, and the enums really are integers on the wire. The consent page's parameter names line up with whatbuildAuthorizeUrlsends and whatLoopbackReceiverexpects on the way back.schema:pullnow keeps only our cornerThe full document is 248 paths and 328 schemas — every admin route and internal DTO, a complete map of the private API. This repo may be made public so anyone wary of an unsigned binary can read what it does, and that map must not travel with it.
Narrowing costs nothing — the point of the file is noticing a change to the eight endpoints we speak to. 567KB → 17KB, 14 schemas, resolved transitively through
$refand sorted so an addition doesn't reshuffle the diff.Verification
npm test→ 68 passed, 6 skippedRAIDIFY_E2E_API_URL=... npm test→ 74 passed, 9 filesSMOKE OK: bridge exposes 18 calls, appInfo returned v0.1.0, and the window rendered.README's "Not built yet" was describing sign-in and the UI, both of which now exist; it now lists what's actually outstanding.
🤖 Generated with Claude Code