Merge pull request #262 from claude/e2e-fresh-connections
E2E: a connection per request, so tests never reuse one wrangler dev closed ("fetch failed")cnbrkdmrci authored 2 parents 294f5fa1b49740commit bb4bd606e49045b3aa1a860b4c9784f4c42a9a19Browse files
- CI / Typecheck and bundlesuccess
- CI / Unit tests (1/2)success
- CI / Unit tests (2/2)success
- Deploy / Deploy to Cloudflaresuccess
- Deploy / Main's current tipsuccess
- End-to-end / End-to-end (agent reviews, tasks, Today)success
- End-to-end / End-to-end (git, PRs, Actions)success
- End-to-end / End-to-end (large pushes)success
- End-to-end / End-to-end (passkeys, registry, email)success
- End-to-end / End-to-end (real histories)success
- End-to-end / End-to-end (runner Docker)success
- End-to-end / End-to-end (security)success
- Live test / Containers, registry, sandboxes, webhooks, schedules, LFS and pull requestssuccess
21 files changed+47−0
Files (21)
- apps/diffinery/package.json+1−0
- apps/diffinery/test/e2e/accounts.test.mjs+1−0
- apps/diffinery/test/e2e/agent-setup.test.mjs+1−0
- apps/diffinery/test/e2e/agentic.test.mjs+1−0
- apps/diffinery/test/e2e/e2e.test.mjs+1−0
- apps/diffinery/test/e2e/early-access.test.mjs+1−0
- apps/diffinery/test/e2e/email.test.mjs+1−0
- apps/diffinery/test/e2e/fresh-connections.mjs+19−0
- apps/diffinery/test/e2e/graphql.test.mjs+1−0
- apps/diffinery/test/e2e/moderation.test.mjs+1−0
- apps/diffinery/test/e2e/oauth.test.mjs+1−0
- apps/diffinery/test/e2e/passkeys.test.mjs+1−0
- apps/diffinery/test/e2e/registry.test.mjs+1−0
- apps/diffinery/test/e2e/review.test.mjs+1−0
- apps/diffinery/test/e2e/security-limits.test.mjs+1−0
- apps/diffinery/test/e2e/security-txt.test.mjs+1−0
- apps/diffinery/test/e2e/security.test.mjs+1−0
- apps/diffinery/test/e2e/status.test.mjs+1−0
- apps/diffinery/test/e2e/tasks.test.mjs+1−0
- apps/diffinery/test/e2e/terminals.test.mjs+1−0
- pnpm-lock.yaml+9−0
modifiedapps/diffinery/package.json+1−0View file
| @@ -46,6 +46,7 @@ | ||
| 46 | 46 | "react": "19.2.0", |
| 47 | 47 | "react-dom": "19.2.0", |
| 48 | 48 | "typescript": "^5.9.3", |
| 49 | + "undici": "6.23.0", | |
| 49 | 50 | "vitest": "^4.1.0", |
| 50 | 51 | "wrangler": "^4.149.0" |
| 51 | 52 | } |
modifiedapps/diffinery/test/e2e/accounts.test.mjs+1−0View file
| @@ -7,6 +7,7 @@ | ||
| 7 | 7 | // username waits 90 days), a site administrator deleting someone's account, exporting an account (a tar |
| 8 | 8 | // archive with a clonable Git bundle and the issues you wrote), and granting and revoking site |
| 9 | 9 | // administration (signed in recently, audited, never the last site administrator). |
| 10 | +import "./fresh-connections.mjs"; | |
| 10 | 11 | import { spawn, execFileSync } from "node:child_process"; |
| 11 | 12 | import { createHash, randomBytes } from "node:crypto"; |
| 12 | 13 | import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/agent-setup.test.mjs+1−0View file
| @@ -12,6 +12,7 @@ | ||
| 12 | 12 | // only what a person approved and never a workflow change, turning merging off, and revoke, regenerate |
| 13 | 13 | // and renew. gh only talks HTTPS, so a stand-in gh (below) does what gh does with GH_ENTERPRISE_TOKEN and |
| 14 | 14 | // GH_HOST; the real gh runs against diffinery.com in the live check. |
| 15 | +import "./fresh-connections.mjs"; | |
| 15 | 16 | import { spawn, execFileSync } from "node:child_process"; |
| 16 | 17 | import { createHash, randomBytes } from "node:crypto"; |
| 17 | 18 | import { chmodSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/agentic.test.mjs+1−0View file
| @@ -6,6 +6,7 @@ | ||
| 6 | 6 | // request waiting for a person, an agent's failing pull request, approvals and |
| 7 | 7 | // merges moving items between sections, the REST API, the MCP tool, an agent's |
| 8 | 8 | // own view and the dashboard page. |
| 9 | +import "./fresh-connections.mjs"; | |
| 9 | 10 | import { spawn, execFileSync } from "node:child_process"; |
| 10 | 11 | import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from "node:fs"; |
| 11 | 12 | import { tmpdir } from "node:os"; |
modifiedapps/diffinery/test/e2e/e2e.test.mjs+1−0View file
| @@ -8,6 +8,7 @@ | ||
| 8 | 8 | // check runs, branch protection with required checks and human review for |
| 9 | 9 | // agent-authored PRs, merging, the REST contents API, the MCP server and |
| 10 | 10 | // profile pictures (WASM image processing in the Worker). |
| 11 | +import "./fresh-connections.mjs"; | |
| 11 | 12 | import { spawn, spawnSync, execFileSync } from "node:child_process"; |
| 12 | 13 | import { mkdtempSync, writeFileSync, mkdirSync, readFileSync, existsSync, rmSync, openSync } from "node:fs"; |
| 13 | 14 | import { tmpdir } from "node:os"; |
modifiedapps/diffinery/test/e2e/early-access.test.mjs+1−0View file
| @@ -10,6 +10,7 @@ | ||
| 10 | 10 | // identity only applies to requests addressed to this machine. |
| 11 | 11 | // |
| 12 | 12 | // pnpm --filter @diffinery/app exec node --test test/e2e/early-access.test.mjs |
| 13 | +import "./fresh-connections.mjs"; | |
| 13 | 14 | import { spawn, execFileSync } from "node:child_process"; |
| 14 | 15 | import { createHash, randomBytes } from "node:crypto"; |
| 15 | 16 | import { mkdtempSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/email.test.mjs+1−0View file
| @@ -11,6 +11,7 @@ | ||
| 11 | 11 | // through, failed workflow run emails, and the suppression list: signed bounce |
| 12 | 12 | // and complaint intake, no email to a suppressed address, lifting it again; the |
| 13 | 13 | // morning digest on its schedule (phase 5c) and Today at phone width. |
| 14 | +import "./fresh-connections.mjs"; | |
| 14 | 15 | import { spawn, execFileSync } from "node:child_process"; |
| 15 | 16 | import { mkdtempSync, writeFileSync, mkdirSync, existsSync, rmSync } from "node:fs"; |
| 16 | 17 | import { tmpdir } from "node:os"; |
addedapps/diffinery/test/e2e/fresh-connections.mjs+19−0View file
| @@ -1,0 +1,19 @@ | ||
| 1 | +// Every fetch in an end-to-end test process gets a connection of its own (imported for that alone). | |
| 2 | +// | |
| 3 | +// wrangler dev closes a keep-alive connection that sits idle for a few seconds, and tests block the | |
| 4 | +// event loop for seconds at a time (execFileSync: `wrangler d1 execute`, git, tar). The close then | |
| 5 | +// waits unread, the next fetch reuses the dead connection and fails with "fetch failed" (other side | |
| 6 | +// closed, UND_ERR_SOCKET), and so does the test. A new localhost connection per request costs well under | |
| 7 | +// a millisecond; Node's fetch and WebSocket use this dispatcher. | |
| 8 | +// | |
| 9 | +// Node's fetch is its own bundled undici, which only works with a dispatcher from the same major | |
| 10 | +// version (undici 7's Agent under Node 22's fetch fails request bodies), so the undici dev dependency | |
| 11 | +// follows Node's: Node 22 bundles undici 6. | |
| 12 | +import { Agent, setGlobalDispatcher } from "undici"; | |
| 13 | +import { createRequire } from "node:module"; | |
| 14 | + | |
| 15 | +const ours = createRequire(import.meta.url)("undici/package.json").version; | |
| 16 | +if (ours.split(".")[0] !== process.versions.undici.split(".")[0]) { | |
| 17 | + throw new Error(`apps/diffinery's undici dev dependency (${ours}) must have the major version of the undici in Node ${process.version} (${process.versions.undici})`); | |
| 18 | +} | |
| 19 | +setGlobalDispatcher(new Agent({ pipelining: 0 })); |
modifiedapps/diffinery/test/e2e/graphql.test.mjs+1−0View file
| @@ -6,6 +6,7 @@ | ||
| 6 | 6 | // GitHub's error shape. |
| 7 | 7 | // |
| 8 | 8 | // pnpm --filter @diffinery/app exec node --test test/e2e/graphql.test.mjs |
| 9 | +import "./fresh-connections.mjs"; | |
| 9 | 10 | import { spawn, execFileSync } from "node:child_process"; |
| 10 | 11 | import { createHash, randomBytes } from "node:crypto"; |
| 11 | 12 | import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/moderation.test.mjs+1−0View file
| @@ -8,6 +8,7 @@ | ||
| 8 | 8 | // (or the blocking organization's) repositories but still can everywhere else; a suspended person's |
| 9 | 9 | // token and session stop at once, their repositories, profile and comments disappear for everyone |
| 10 | 10 | // but site administrators, and unsuspending restores it all; a minimized comment shows collapsed. |
| 11 | +import "./fresh-connections.mjs"; | |
| 11 | 12 | import { spawn, execFileSync } from "node:child_process"; |
| 12 | 13 | import { createHash, generateKeyPairSync, randomBytes } from "node:crypto"; |
| 13 | 14 | import { mkdtempSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/oauth.test.mjs+1−0View file
| @@ -7,6 +7,7 @@ | ||
| 7 | 7 | // client metadata document as the client_id, the consent page (signed out, unknown app, wrong redirect, |
| 8 | 8 | // cancel, acting as yourself or an agent, admin never preselected), PKCE, codes working once, MCP calls |
| 9 | 9 | // with the token, refresh rotation and reuse, confidential clients, revocation, and Connected apps. |
| 10 | +import "./fresh-connections.mjs"; | |
| 10 | 11 | import { spawn, execFileSync } from "node:child_process"; |
| 11 | 12 | import { createHash, randomBytes } from "node:crypto"; |
| 12 | 13 | import { mkdtempSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/passkeys.test.mjs+1−0View file
| @@ -11,6 +11,7 @@ | ||
| 11 | 11 | // single-use invites and links, and refusing cross-site and token-driven calls. |
| 12 | 12 | // Open sign-up (SIGNUP=open) runs on a second Worker: an emailed code, then a |
| 13 | 13 | // passkey, and the sign-up pages answering 404 while it is off. |
| 14 | +import "./fresh-connections.mjs"; | |
| 14 | 15 | import { spawn, execFileSync } from "node:child_process"; |
| 15 | 16 | import { createHash, createHmac, randomBytes } from "node:crypto"; |
| 16 | 17 | import { mkdtempSync, readFileSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/registry.test.mjs+1−0View file
| @@ -8,6 +8,7 @@ | ||
| 8 | 8 | // manifests by tag and digest, indexes, referrers, tag listing, cross-repository mounts, |
| 9 | 9 | // token scopes, the packages REST API, the job token's `packages` permission, linking |
| 10 | 10 | // packages to repositories, and docker login/push/pull. |
| 11 | +import "./fresh-connections.mjs"; | |
| 11 | 12 | import { spawn, spawnSync, execFileSync } from "node:child_process"; |
| 12 | 13 | import { createHash, randomBytes } from "node:crypto"; |
| 13 | 14 | import { mkdtempSync, writeFileSync, mkdirSync, rmSync, existsSync, readFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/review.test.mjs+1−0View file
| @@ -9,6 +9,7 @@ | ||
| 9 | 9 | // read-only sandbox, autofix pushing to the pull request's branch, the |
| 10 | 10 | // re-review marking the finding fixed, Fix it, asking for a review, and the |
| 11 | 11 | // spend limits (model requests per review, the repository's daily limit). |
| 12 | +import "./fresh-connections.mjs"; | |
| 12 | 13 | import { spawn, execFileSync } from "node:child_process"; |
| 13 | 14 | import { mkdtempSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync, readFileSync, existsSync } from "node:fs"; |
| 14 | 15 | import { tmpdir } from "node:os"; |
modifiedapps/diffinery/test/e2e/security-limits.test.mjs+1−0View file
| @@ -3,6 +3,7 @@ | ||
| 3 | 3 | // invitations by username, per-account quotas, kill switches and the Markdown image proxy. |
| 4 | 4 | // |
| 5 | 5 | // pnpm --filter @diffinery/app e2e |
| 6 | +import "./fresh-connections.mjs"; | |
| 6 | 7 | import { spawnSync, execFileSync } from "node:child_process"; |
| 7 | 8 | import { createHash, randomBytes } from "node:crypto"; |
| 8 | 9 | import { writeFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/security-txt.test.mjs+1−0View file
| @@ -5,6 +5,7 @@ | ||
| 5 | 5 | // audited triage; the other has SECURITY_CONTACT set (every field, the form first). |
| 6 | 6 | // |
| 7 | 7 | // pnpm --filter @diffinery/app exec node --test test/e2e/security-txt.test.mjs |
| 8 | +import "./fresh-connections.mjs"; | |
| 8 | 9 | import { spawn, execFileSync } from "node:child_process"; |
| 9 | 10 | import { createHash, randomBytes } from "node:crypto"; |
| 10 | 11 | import { mkdtempSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/security.test.mjs+1−0View file
| @@ -22,6 +22,7 @@ | ||
| 22 | 22 | // merge queue) where a person allowed it and a required check gates it, never by bypass. |
| 23 | 23 | // Continued in security-limits.test.mjs (environment reviewers, quotas, kill switches, the image |
| 24 | 24 | // proxy), which runs beside this file on a server of its own; the setup is in security-harness.mjs. |
| 25 | +import "./fresh-connections.mjs"; | |
| 25 | 26 | import { spawnSync, execFileSync } from "node:child_process"; |
| 26 | 27 | import { createHash, randomBytes } from "node:crypto"; |
| 27 | 28 | import { mkdtempSync, writeFileSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/status.test.mjs+1−0View file
| @@ -4,6 +4,7 @@ | ||
| 4 | 4 | // 404 for everyone else, the audit log, and the hosted compute pause showing as maintenance. |
| 5 | 5 | // |
| 6 | 6 | // pnpm --filter @diffinery/app exec node --test test/e2e/status.test.mjs |
| 7 | +import "./fresh-connections.mjs"; | |
| 7 | 8 | import { spawn, execFileSync } from "node:child_process"; |
| 8 | 9 | import { createHash, randomBytes } from "node:crypto"; |
| 9 | 10 | import { mkdtempSync, rmSync } from "node:fs"; |
modifiedapps/diffinery/test/e2e/tasks.test.mjs+1−0View file
| @@ -10,6 +10,7 @@ | ||
| 10 | 10 | // security rules: no token, agent or sandbox can start or steer a task, task |
| 11 | 11 | // sandboxes are closed to the sandbox API, and a run's model token stops |
| 12 | 12 | // working when the run ends. |
| 13 | +import "./fresh-connections.mjs"; | |
| 13 | 14 | import { spawn, execFileSync } from "node:child_process"; |
| 14 | 15 | import { mkdtempSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync, readFileSync, existsSync } from "node:fs"; |
| 15 | 16 | import { tmpdir } from "node:os"; |
modifiedapps/diffinery/test/e2e/terminals.test.mjs+1−0View file
| @@ -2,6 +2,7 @@ | ||
| 2 | 2 | // (agents-harness.mjs). In a file of their own so each file stays well under the test runner's per-file limit. |
| 3 | 3 | // |
| 4 | 4 | // pnpm --filter @diffinery/app e2e |
| 5 | +import "./fresh-connections.mjs"; | |
| 5 | 6 | import { spawn, execFileSync } from "node:child_process"; |
| 6 | 7 | import { mkdtempSync, writeFileSync, mkdirSync, rmSync, readdirSync, statSync, readFileSync, existsSync } from "node:fs"; |
| 7 | 8 | import { tmpdir } from "node:os"; |
modifiedpnpm-lock.yaml+9−0View file
| @@ -121,6 +121,9 @@ | ||
| 121 | 121 | typescript: |
| 122 | 122 | specifier: ^5.9.3 |
| 123 | 123 | version: 5.9.3 |
| 124 | + undici: | |
| 125 | + specifier: 6.23.0 | |
| 126 | + version: 6.23.0 | |
| 124 | 127 | vitest: |
| 125 | 128 | specifier: ^4.1.0 |
| 126 | 129 | version: 4.1.11(@types/node@22.20.5)(vite@8.3.4(@types/node@22.20.5)(esbuild@0.25.11)(yaml@2.9.1)) |
| @@ -2184,6 +2187,10 @@ | ||
| 2184 | 2187 | undici-types@6.21.0: |
| 2185 | 2188 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} |
| 2186 | 2189 | |
| 2190 | + undici@6.23.0: | |
| 2191 | + resolution: {integrity: sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==} | |
| 2192 | + engines: {node: '>=18.17'} | |
| 2193 | + | |
| 2187 | 2194 | undici@7.29.0: |
| 2188 | 2195 | resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} |
| 2189 | 2196 | engines: {node: '>=20.18.1'} |
| @@ -4091,6 +4098,8 @@ | ||
| 4091 | 4098 | |
| 4092 | 4099 | undici-types@6.21.0: {} |
| 4093 | 4100 | |
| 4101 | + undici@6.23.0: {} | |
| 4102 | + | |
| 4094 | 4103 | undici@7.29.0: {} |
| 4095 | 4104 | |
| 4096 | 4105 | undici@7.29.1: {} |