test(web): pin the active-server retention the callback recovery depends on

Review round 4 raised that the callback catch leaves `activeServerId` set: the
disconnect there emits only on a status change, and a rejected token exchange
throws inside `completeOAuthFlow` before the reconnect, so the client is still
at its initial "disconnected" and the listener that would clear it never fires.

That is accurate, and clearing it is nonetheless wrong. "Authorize again" on
the re-auth banner hands `clearServerOAuthState` the live client only when the
banner's server is the active one; releasing it here passes `null` instead and
the stale tokens are never cleared from the client holding them, which is the
one thing that recovery exists to do. Confirmed by trying it — the existing
lost-authorization-state test fails on `clearOAuthTokens` never being called.

So the retention is now asserted rather than incidental, with the reason at
both the code and the test, and the InspectorView double surfaces `activeServer`
so a later cleanup of the apparent leak fails loudly instead of quietly
breaking the recovery.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-24 21:23:25 -04:00
commit f0720e4ed6

View file

@ -2909,6 +2909,19 @@ function App() {
// active-server lock is never released. Before the EMA guard, since a
// stuck session is worth clearing whichever way the error classifies.
await client.disconnect().catch(() => {});
// `activeServerId` is deliberately NOT cleared here, even though the
// disconnect above often cannot announce itself: it emits only on a
// status *change*, and the commonest failure — a rejected token
// exchange — throws inside `completeOAuthFlow` before the reconnect
// runs, so this freshly built client is still at its initial
// `"disconnected"` and the listener that would clear it never fires.
//
// That looks like a leak and is not. The next step after a callback
// failure is the re-auth banner below, and its "Authorize again" hands
// `clearServerOAuthState` the live client only when the banner's server
// *is* the active one. Releasing it here would pass `null` instead, and
// the stale tokens would never be cleared from the client that holds
// them — the one thing that recovery exists to do.
if (isEmaClientNotConfiguredError(err)) {
notifications.show({
title: `Cannot connect to "${server.name}"`,