Skip to content

test: make the companion talk to a real API, and fix what that found - #2

Merged
FriskyyDev merged 1 commit into
mainfrom
feature/live-integration-test
Aug 4, 2026
Merged

test: make the companion talk to a real API, and fix what that found#2
FriskyyDev merged 1 commit into
mainfrom
feature/live-integration-test

Conversation

@FriskyyDev

Copy link
Copy Markdown
Owner

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.ts does

Against a local API, using the real client and the real PKCE implementation:

  1. Checks this build is one the server still accepts
  2. Builds a PKCE pair, plays the part of the consent page to get a code, exchanges it for a real token
  3. Confirms a code presented with the wrong verifier is refused
  4. Lists the guilds that token may write to
  5. Uploads a night
  6. Signs out and confirms the token is actually revoked, not merely forgotten

Skipped unless RAIDIFY_E2E_API_URL is set. A suite that needs a database running to pass is a suite people stop running. It can't point at production either — test-login 404s there, so step 1 fails loudly rather than quietly touching real data.

# raidify repo
docker compose up -d
dotnet run --project apps/api/src/Raidify.Api --urls http://localhost:5001

# here
RAIDIFY_E2E_API_URL=http://localhost:5001 npm test

It found a bug on its first run

Sign-out returns 204, and request<T> parsed every successful response as JSON. The empty body threw Unexpected end of JSON inputafter 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.ts against 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 what buildAuthorizeUrl sends and what LoopbackReceiver expects on the way back.

schema:pull now keeps only our 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. 567KB → 17KB, 14 schemas, resolved transitively through $ref and sorted so an addition doesn't reshuffle the diff.

Verification

  • npm test → 68 passed, 6 skipped
  • RAIDIFY_E2E_API_URL=... npm test → 74 passed, 9 files
  • SMOKE OK: bridge exposes 18 calls, appInfo returned v0.1.0, and the window rendered.
  • Typecheck clean

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

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>
@FriskyyDev
FriskyyDev merged commit 4c23f59 into main Aug 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant