Repository navigation
test: live interaction coverage gate for the chat surface #809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
fba4b46
test: live interaction coverage gate for the chat surface
sakibsadmanshajib 3cf62b2
test: prove chat controls live, and teach live-auth the chat OIDC hop
sakibsadmanshajib 2b9588d
test: record the live chat coverage run and prove the detector on a f…
sakibsadmanshajib ad3087c
fix: stop the chat coverage gate counting failures as proof, and floo…
sakibsadmanshajib 84dba92
test: record the corrected chat coverage run and stop recording empty…
sakibsadmanshajib 33a4832
docs: record the 504s and the unstable denominator in the chat covera…
sakibsadmanshajib dbab865
refactor: make distinct control identities the headline coverage figure
sakibsadmanshajib 3c88ff7
fix: stop the coverage gate manufacturing proof, and redact what it w…
sakibsadmanshajib 64265b6
test: run the coverage gate in CI, and stop it moving its own bar
sakibsadmanshajib 245249c
fix: close the leak paths the bot review found in the coverage prover
sakibsadmanshajib 15ee811
docs: name the earlier ledger by the day it was actually taken
sakibsadmanshajib cb71862
fix: require a server verdict for network proof, and stop laundering …
sakibsadmanshajib e33949a
fix: keep framework-generated text out of the coverage artifact, and …
sakibsadmanshajib 5f5c37c
docs: withdraw a coverage figure no shipped code path can produce
sakibsadmanshajib 3d23e41
fix: stop the chat list floor turning a groomed account into a red gate
sakibsadmanshajib 21eab6f
fix: keep a presence bar on the chat list instead of no floor at all
sakibsadmanshajib 09d3992
fix: match a response to the request the click made, and redact the l…
sakibsadmanshajib 4f712ff
refactor: narrow DOM nodes with instanceof instead of casting
sakibsadmanshajib 3a75aa8
fix: address the bot review on the rebased head
sakibsadmanshajib File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| name: Chat interaction coverage | ||
|
|
||
| # Two jobs, deliberately gated differently. | ||
| # | ||
| # self-check Runs everywhere, on every pull request that touches the gate. | ||
| # Needs no deployment, no session and no secrets: it serves its | ||
| # own fixture from an intercepted origin and asserts the prover | ||
| # still calls a dead button dead. This is the part that keeps | ||
| # the gate from rotting into something that cannot go red. | ||
| # | ||
| # live-sweep The real sweep, against the deployed chat surface. It needs a | ||
| # running deployment and a minted session, so it never fires on | ||
| # a plain pull request: run it by hand, or label a pull request | ||
| # run-chat-coverage. The same reason owui-nightly is not a | ||
| # per-pull-request gate. | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, labeled] | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
| inputs: | ||
| surfaces: | ||
| description: 'Surface filter (a regular expression, blank for a full sweep)' | ||
| required: false | ||
| default: '' | ||
|
|
||
| concurrency: | ||
| group: chat-coverage-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| # Read only, and narrowed to what is actually read: the self-check job asks the | ||
| # issues API whether a surface exclusion's blocking issue has closed. Nothing | ||
| # here writes to the repository, and a job that walks a live OAuth flow should | ||
| # not be holding a token that could. | ||
| permissions: | ||
| contents: read | ||
| issues: read | ||
|
|
||
| jobs: | ||
| self-check: | ||
| name: Chat coverage self-check (no deployment needed) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| defaults: | ||
| run: | ||
| working-directory: apps/web-console | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| # Leave no usable git credential in the runner's config for the rest | ||
| # of the job, which runs a browser against a live deployment. | ||
| persist-credentials: false | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
| cache: 'npm' | ||
| cache-dependency-path: apps/web-console/package-lock.json | ||
| - name: Install dependencies | ||
| run: npm ci | ||
| - name: Install Chromium | ||
| run: npx playwright install --with-deps chromium | ||
| - name: Prover break-proof, registry and exclusion expiry | ||
| # GITHUB_TOKEN is what the exclusion-expiry check reads issue state | ||
| # with. It fails rather than skips without one, on purpose: an | ||
| # exclusion whose blocking issue has closed has to become a failure | ||
| # the day it closes, and a check that skips itself cannot do that. | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| run: npm run e2e:chat-coverage:self-check | ||
|
|
||
| live-sweep: | ||
| name: Chat coverage live sweep | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 60 | ||
| if: >- | ||
| github.event_name == 'workflow_dispatch' || | ||
| (github.event_name == 'pull_request' && | ||
| contains(github.event.pull_request.labels.*.name, 'run-chat-coverage')) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| defaults: | ||
| run: | ||
| working-directory: apps/web-console | ||
| env: | ||
| CHAT_URL: ${{ vars.CHAT_URL || 'https://chat-hive.scubed.co' }} | ||
| CONSOLE_URL: ${{ vars.CONSOLE_URL || 'https://console-hive.scubed.co' }} | ||
| # An existing account to mint a session for. live-auth.mjs mints through | ||
| # the admin one-time-token flow, so no password is read and none is | ||
| # changed; HIVE_QA_TESTER_PASSWORD is deliberately not passed in here. | ||
| HIVE_QA_AGENT_EMAIL: ${{ secrets.HIVE_QA_AGENT_EMAIL || secrets.HIVE_QA_TESTER_EMAIL }} | ||
| SUPABASE_URL: ${{ secrets.SUPABASE_URL }} | ||
| SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }} | ||
| SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} | ||
| COV_SURFACES: ${{ inputs.surfaces }} | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| # Leave no usable git credential in the runner's config for the rest | ||
| # of the job, which runs a browser against a live deployment. | ||
| persist-credentials: false | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
| cache: 'npm' | ||
| cache-dependency-path: apps/web-console/package-lock.json | ||
| - name: Check the inputs the sweep cannot run without | ||
| # Loud, not quiet. A missing secret here used to leave the whole | ||
| # config matching zero files, which reports a green run of nothing. | ||
| run: | | ||
| set -euo pipefail | ||
| missing="" | ||
| for v in CHAT_URL CONSOLE_URL HIVE_QA_AGENT_EMAIL SUPABASE_URL SUPABASE_ANON_KEY SUPABASE_SERVICE_ROLE_KEY; do | ||
| [ -n "${!v:-}" ] || missing="$missing $v" | ||
| done | ||
| if [ -n "$missing" ]; then | ||
| echo "::error::the live sweep needs:$missing" | ||
| exit 1 | ||
| fi | ||
| - name: Install dependencies | ||
| run: npm ci | ||
| - name: Install Chromium | ||
| run: npx playwright install --with-deps chromium | ||
| - name: Sweep the chat surface | ||
| run: npm run e2e:chat-coverage | ||
| - name: Upload the coverage ledger | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: chat-coverage-ledger | ||
| # Two named files, never a directory. Everything this suite writes | ||
| # itself goes through redactUrl, but a directory upload also sweeps | ||
| # up whatever the framework wrote beside them: Playwright's JSON and | ||
| # HTML reports carry raw error text, and a sign-in that outruns its | ||
| # ninety second wait quotes the OAuth callback with a live code and | ||
| # state in the timeout message. Those reports are configured to land | ||
| # outside this directory, and naming the files here means a future | ||
| # reporter added to the config cannot quietly rejoin the artifact. | ||
| # Traces and videos are off for the same reason and cannot be | ||
| # inspected by the text lint at all. | ||
| path: | | ||
| apps/web-console/chat-coverage-report/coverage.run.json | ||
|
sakibsadmanshajib marked this conversation as resolved.
|
||
| apps/web-console/chat-coverage-report/coverage.md | ||
| if-no-files-found: error | ||
| retention-days: 14 | ||
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // Signs the coverage sweep in and saves storageState. | ||
| // | ||
| // signInToChat lives in tests/e2e/support/live-auth.ts, and is the only | ||
| // implementation of the chat hop: the mint itself stays in live-auth.mjs, | ||
| // which this shells out to, so the service-role key never enters this worker's | ||
| // module graph. It mints a session the way a magic link does, so no password | ||
| // is read, written or rotated, then carries it through the real "Continue with | ||
| // Hive" OIDC hop, which is the only thing that gets Open WebUI's own session | ||
| // cookie set. See docs/live-test-auth.md. | ||
| // | ||
| // This project records no trace, video or screenshot (see the config): the | ||
| // callback URL it walks through carries a live `code` and `state`, and no text | ||
| // lint can read inside a trace or a video to catch them. | ||
| import path from "node:path"; | ||
|
|
||
| import { test as setup } from "@playwright/test"; | ||
|
|
||
| import { signInToChat } from "../../tests/e2e/support/live-auth"; | ||
|
|
||
| const STATE = path.join(__dirname, ".auth", "state.json"); | ||
| const CHAT = process.env.CHAT_URL ?? process.env.OWUI_URL ?? ""; | ||
| const CONSOLE = process.env.CONSOLE_URL ?? ""; | ||
| const EMAIL = process.env.OWUI_E2E_EMAIL ?? process.env.HIVE_QA_AGENT_EMAIL ?? ""; | ||
|
|
||
| setup("authenticate against the chat surface", async ({ browser }) => { | ||
| setup.setTimeout(6 * 60_000); | ||
| if (!CHAT || !CONSOLE || !EMAIL) { | ||
| setup.skip(true, "CHAT_URL, CONSOLE_URL and an account are all required"); | ||
| return; | ||
| } | ||
|
|
||
| const context = await browser.newContext({ viewport: { width: 1440, height: 950 } }); | ||
| await signInToChat(context, { email: EMAIL, consoleUrl: CONSOLE, chatUrl: CHAT }); | ||
| await context.storageState({ path: STATE }); | ||
| await context.close(); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.