Skip to content

Check automatic approval end to end, against a stubbed AWS SSO - #89

Open
popen2 wants to merge 1 commit into
mainfrom
claude/frost-auto-approve-aws-o8yleo
Open

Check automatic approval end to end, against a stubbed AWS SSO#89
popen2 wants to merge 1 commit into
mainfrom
claude/frost-auto-approve-aws-o8yleo

Conversation

@popen2

@popen2 popen2 commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Follow-up to #85, which merged with only its matching rules covered. This adds the regression test that was missing: npm run check:auto-approve, in CI next to check:overlay.

Why

Automatic approval is a script clicking buttons on pages Frost does not own, in a window the user cannot see, and every way it can be wrong is quiet — a button never found (the refresh hangs until the device code expires), a button that should not have been clicked (the request is denied), a hand-over that never happens (the user waits in front of nothing). None of that is visible to a type-check, and testing the matching rules alone misses the thing that actually makes it work: the whole path.

So this drives the real refresh() — the same entry point the tray, the hotkey and the timer use — and asserts on what the user would have seen.

How it stubs AWS SSO

Two interceptions, neither of which asks the app to know it is under test:

  • AWS_ENDPOINT_URL_SSO_OIDC / AWS_ENDPOINT_URL_SSO — an AWS SDK feature, so the device authorization, the polling and its AuthorizationPendingException are the real client speaking a real protocol to a stub HTTP service. The token only becomes redeemable when the stub's /approved page is actually fetched, so nothing passes without a real click.
  • session.protocol.handle("https", …) — serves the verification pages at their real names. The renderer gets https://d-1234567890.awsapps.com, a secure context, and a genuine cross-origin 302 to the identity provider. Served from localhost this would prove nothing; the host rule is the point.

HOME and the electron-store move to a temp directory, so a run touches nothing of the developer's.

The three scenarios

Asserts
Live IdP session The token is collected and no window is ever shown — the feature's whole point
Page asks for a password The window comes up; the check signs in on the page, and the driver finishes the remaining approval step
Nothing recognisable Nothing is clicked — including a refusal wearing cli_login_button's id — and the window comes up

It fails when the feature breaks

Worth stating, since a green test that cannot go red is worse than none. Mutating the built driver:

  • isApprovalHostfalse: both approval scenarios fail.
  • isDenialfalse: the third fails by clicking the "Cancel request" trap.

16 seconds for a passing run. CI runs it under xvfb, the same way as check:overlay.

Still not covered

A real desktop: no security key, no keychain, no dock, and no real AWS pages — so the id and label matching against what AWS actually serves today still wants a smoke test on a real machine. Default-browser hand-over is also untested here, since asserting it would mean really opening a browser on the runner.


Generated by Claude Code

Automatic approval is a script clicking buttons on pages Frost does not
own, in a window the user cannot see, and every way it can be wrong is
quiet: a button that is never found (the refresh hangs until the device
code expires), a button that should not have been clicked (the request is
denied), a hand-over that never happens (the user waits in front of
nothing). None of that is visible to a type-check, and none of it is
really testable against the matching rules alone — what makes it work is
the whole path.

So `npm run check:auto-approve` drives the real `refresh()`, the same
entry point the tray, the hotkey and the timer use, and asserts on what
the user would have seen. Two interceptions make that possible without
the app knowing it is under test:

- `AWS_ENDPOINT_URL_SSO_OIDC` / `AWS_ENDPOINT_URL_SSO`, an AWS SDK
  feature, point the SDK at a stub HTTP service. The device
  authorization, the polling and its AuthorizationPendingException are
  the real client speaking a real protocol.
- `session.protocol.handle("https", ...)` serves the verification pages
  at their real names, so the renderer gets `https://d-….awsapps.com`, a
  secure context, and a genuine cross-origin redirect to the identity
  provider. Served from localhost it would prove nothing — the host rule
  is the point.

`HOME` and the electron-store move to a temp directory, so a run touches
nothing of the developer's. Three scenarios, one per outcome: a live
identity provider session finishes with no window ever shown; a page
asking for a password shows the window and finishes the approval once the
check signs in on the page; and a page with nothing recognisable —
including a refusal wearing `cli_login_button`'s id — is not clicked at
all, and the window comes up.

It fails when the feature breaks, which is the part worth stating:
disabling the host rule fails both approval scenarios, and disabling the
refusal rule fails the third by clicking the trap. 16 seconds, and CI
runs it under xvfb next to the overlay check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XK1mi3rSPLzxu7XUhyC6a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants