Files
coder/site/e2e/tests
Jake Howell b341ce63fb fix(site/e2e): close mock external-auth servers in teardown (#26575)
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell.

Stack:
1. #26575 `fix(site/e2e): close mock external-auth servers in teardown`
← this PR
2. #26793 `fix(site/e2e): accept 404 from external auth reset hook`
3. #26795 `fix(site/src): refresh provider state after device-flow
exchange`
4. #26798 `fix(site/e2e): reset both providers in external auth hook`
5. #26648 `chore(site/e2e): re-enable externalAuth suite`

The externalAuth e2e suite has been skipped since #17235 because
`createServer` in `site/e2e/helpers.ts` started an express server but
never gave callers a way to close it. On retries or repeated runs, the
listener from the previous invocation was still bound to the hardcoded
port and the next `beforeAll` failed with `EADDRINUSE`, eventually
timing out in `waitForPort`.

`createServer` now returns a `{ app, close }` pair. The web flow closes
in `afterAll`; the device flow uses `try/finally`.
`closeAllConnections()` is called before `close()` so teardown stays
bounded if keep-alive connections linger.

The suite remains `test.describe.skip` here; #26648 flips the skip off
once the rest of the stack is in.

Refs https://linear.app/codercom/issue/DEVEX-413
Refs https://github.com/coder/internal/issues/356

<details>
<summary>Decision log</summary>

Discussed the full options list with @jakehwll before drafting. Picked
option A (minimal teardown) because:

- Two prior PRs (#15537, #16528) attacked symptoms (port probing, longer
timeout) without addressing the leaked listener.
- Kayla's diagnosis on coder/internal#356 pointed at exactly this case:
nothing else in CI is grabbing the port, the listener from the previous
run is still bound.
- A is mechanical and orthogonal: it adds a real teardown without
changing port allocation, fixture wiring, or what gets mocked. If the
flake persists after A, we know to escalate to a worker-scoped fixture
or dynamically allocated ports.

Returning `close` rather than the raw `http.Server` encapsulates the
`closeAllConnections` + `close` choreography so callers don't repeat it.
`closeAllConnections` is optional-chained because it landed in Node
18.2; coder/coder runs newer, but the chain costs nothing.

The device test uses `try/finally` rather than a shared `afterEach` to
keep per-test state local. The web flow's `afterAll` mirrors its
`beforeAll`.

</details>
2026-07-01 13:14:23 +10:00
..