End-to-end tests for automatic approval, against a stubbed AWS SSO - #89
Merged
Merged
Conversation
It is an end-to-end test: it boots the app and drives a real BrowserWindow through a WebAuthn wait. Everything naming it said "check" — the script, the file, the text it printed — and it ran as a step inside the job called "🧹 Lint app", so a pull request's check list never said whether it had run at all. `npm run test:overlay`, `tools/test-login-overlay.js`, and a job named 🧪 End-to-end tests that `build` waits on. It costs a second npm ci and build, which run in parallel with lint, and buys a check line that goes red on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XK1mi3rSPLzxu7XUhyC6a
Automatic approval is a script clicking buttons on pages Frost does not
own, in a window the user cannot see. 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 what actually makes it work — the whole path.
So `npm run test: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. Four 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, and the token only becomes redeemable when the
stub's approval page is actually fetched.
- session.protocol.handle("https", ...) serves the 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.
- Notification.prototype.show and shell.openExternal are recorded rather
than performed: what the user was told and where they were sent are the
assertions, and a CI runner has neither a notification daemon nor a
browser.
- powerMonitor.getSystemIdleTime() answers whatever the scenario says.
Frost only shows a login page to somebody who is there, so real idle
time would make these tests depend on whether anyone had touched the
keyboard.
Nine scenarios, one per outcome, including the two that decide whether
anybody is interrupted: an identity provider hop that the session carries
through stays silent, and one that asks for a password brings the window
up. HOME and the electron-store move to a temp directory, so a run touches
nothing of the developer's.
Each mutation fails exactly one scenario: the host rule returning false
fails both approval scenarios and returning true fails the
identity-provider one; dropping the refusal rule fails the unrecognised
page; isUserPresent() returning true unconditionally fails the unattended
one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018XK1mi3rSPLzxu7XUhyC6a
popen2
force-pushed
the
claude/frost-auto-approve-aws-o8yleo
branch
from
September 14, 2026 10:10
52bba75 to
ce9c29c
Compare
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.
This is the testing PR. It changes no behaviour — it tests what
maindoes today. The behaviour change that came out of writing it is #90, stacked on this.Rebased onto
mainat 7522d2e (after #92).Why
Automatic approval is a script clicking buttons on pages Frost does not own, in a window the user cannot see. 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). Testing the matching rules alone misses what actually makes it work: the whole path.
So
npm run test:auto-approvedrives the realrefresh()— the entry point the tray, the hotkey and the timer use — and asserts on what the user would have seen.Ten… nine scenarios
triggerPendingAuth(), even when the approval needs nobodyopenExternalgets the verification URL, no window showncli_login_button's idWhat makes it possible
Four interceptions, none 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 itsAuthorizationPendingExceptionare the real client speaking a real protocol. The token only becomes redeemable when the stub's approval page is actually fetched, so nothing passes without a real click.session.protocol.handle("https", …)— pages served at their real names, so the renderer getshttps://d-1234567890.awsapps.com, a secure context, and a genuine cross-origin 302 tohttps://idp.example.test. Served from localhost this would prove nothing; the host rule is the point.Notification.prototype.showandshell.openExternalrecorded, not performed — a CI runner has neither a notification daemon nor a browser.Notificationis a non-configurable export, hence the prototype.powerMonitor.getSystemIdleTime()answers what the scenario says — Keep trying an overnight sign-in instead of giving up on it #92 made Frost show a login page only to somebody who is there, so real idle time would make these depend on whether anyone touched the keyboard: green on a fresh CI runner, red on a desktop five minutes after you start them and walk away.HOMEand the electron-store move to a temp directory, so a run touches nothing of the developer's.It fails when the feature breaks
Each mutation fails exactly one scenario:
isApprovalHost→falseisApprovalHost→trueisDenial→falseisUserPresent→trueAlso here: the tests report as tests
The first commit renames
check:overlay→test:overlay(and its file), and moves it out of the job called 🧹 Lint app into 🧪 End-to-end tests, whichbuildwaits on. Both real-app tests now report under a name that answers "did the tests run" from the check list.Still not covered
A real desktop — no security key, no keychain, no dock — and the real AWS pages, so the id and label matching against what AWS serves today still wants a smoke test on a real machine.
🤖 Generated with Claude Code
https://claude.ai/code/session_018XK1mi3rSPLzxu7XUhyC6a