Skip to content

Commit b45cffe

Browse files
chore: improvements to remote-bindings tests to reduce flakes (#10380)
* chore: improvements to remote-bindings tests to reduce flakes * retry wrangler-remote-resources checks a few times * move the getPlatformProxy remote-bindings tests to the e2e CI job * move `startDev` into its own module so it is possible to mock it * move `startRemoteProxySession` into its own module so it is possible to mock it * add missing types to `Binding` type * add missing `remote` property and update jsdoc for Config type * Allow config watching in unit tests The tests were previously failing because the controller had not been been torn down before the end of the test, rather than the actual watching being a problem in itself. When the tests end, the current working directory gets reverted from the temporary directory that was created for it. Since the controller had not been torn down, the config file change triggered a config change event which then tries to read the config from the wrong current working directory, causing an exception. * Remove wrangler-remote-resources e2e test This is now covered by a combination of: - `packages/wrangler/e2e/remote-binding/miniflare-remote-resources.test.ts` - actually tests that we can create, deploy and connect to the remote dev session worker. - `packages/wrangler/src/__tests__/dev/remote-bindings.test.ts` - tests that we generate the correct config for the remote session proxy worker and local miniflare session. The removed e2e test was very flaky and slow as it required creating and deleting a lot of resources in Cloudflare. The new test runs entirely without any dependency on Cloudflare and is super fast (<20 secs). * add Wrangler remote bindings unit tests * ensure configcontroller is torn down before tmp directory is deleted * wait for config watching to be updated * ignore changes if the watcher is closed * Allow the "too large" upload test more time to pass Sometimes timesout on Windows * abort if this config controller is no longer viable * remove unnecessary `console.dir()` calls in tests * update ubuntu in CI tests * run wrangler tests before fixtures * Update packages/wrangler/e2e/helpers/e2e-wrangler-test.ts Co-authored-by: Dario Piotrowicz <[email protected]> * run all linux tests on ubuntu 24 --------- Co-authored-by: Dario Piotrowicz <[email protected]>
1 parent d7aa0ae commit b45cffe

35 files changed

+1628
-1338
lines changed

.github/workflows/e2e.yml

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
description: v20, Windows
2626
node: 20.19.1
2727
# we need to use an amd image to run the containers tests, since we build for linux/amd64
28-
- os: ubuntu-22.04
28+
- os: ubuntu-24.04
2929
description: v20, Linux
3030
node: 20.19.1
3131

@@ -61,6 +61,14 @@ jobs:
6161
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
6262
CI_OS: ${{ matrix.os }}
6363

64+
- name: Run getPlatformProxy() remote-bindings e2e tests
65+
run: pnpm run test:e2e -F @fixture/get-platform-proxy-remote-bindings
66+
env:
67+
TEST_CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }}
68+
TEST_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }}
69+
NODE_OPTIONS: "--max_old_space_size=8192"
70+
CI_OS: ${{ matrix.os }}
71+
6472
- name: Upload turbo logs
6573
if: always()
6674
uses: actions/upload-artifact@v4
@@ -83,7 +91,8 @@ jobs:
8391
- os: windows-2022
8492
description: v20, Windows
8593
node: 20.19.1
86-
- os: ubuntu-22.04-arm
94+
# we need to use an amd image to run the containers tests, since we build for linux/amd64
95+
- os: ubuntu-24.04
8796
description: v20, Linux
8897
node: 20.19.1
8998
runs-on: ${{ matrix.os }}
@@ -128,25 +137,3 @@ jobs:
128137
with:
129138
name: turbo-runs-${{ matrix.os }}-${{ matrix.node }}
130139
path: .turbo/runs
131-
132-
# E2E tests cannot run on pull requests created from forks due to security concerns.
133-
# In such cases we require a member of the `workers-sdk` maintainers group to manually
134-
# run these tests on behalf of the PR.
135-
#
136-
# If a PR from a fork specifically requests running the e2e tests, by applying the `e2e`
137-
# label, we want CI to fail with a descriptive message, instead of skipping the `e2e-tests`
138-
# job altogether, which gives the false optics that CI is green. Having CI intentionally
139-
# fail for such use cases will act as a reminder that tests need to be manually run, and
140-
# will prevent us from accidentally merging fork PRs that are green, but never ran the
141-
# e2e tests.
142-
e2e-test-forks:
143-
name: "E2E tests on forks"
144-
concurrency:
145-
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.node }}
146-
if: github.event_name == 'pull_request' && contains(github.event.*.labels.*.name, 'e2e' ) && github.event.pull_request.head.repo.full_name != github.repository
147-
runs-on: ${{ matrix.os }}
148-
steps:
149-
- name: Force Fail
150-
run: |
151-
echo: "E2E tests cannot run on pull requests created from forks due to security reasons. Please reach out to a workers-sdk maintainer to run the E2E tests on behalf of this PR."
152-
exit 1

.github/workflows/test-and-check.yml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ jobs:
112112
# Browser rendering is disabled here because of https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu
113113
filter: '--filter="./packages/*" --filter="./fixtures/*" --filter="./packages/vite-plugin-cloudflare/playground" --filter="!./packages/kv-asset-handler" --filter="!./fixtures/browser-rendering"'
114114
# Skipped until we upgrade to undici v7, because of https://github.com/nodejs/undici/issues/4285
115-
# - os: ubuntu-24.04-arm
115+
# - os: ubuntu-24.04
116116
# label: v24, Linux
117117
# node_version: 24
118118
# filter: '--filter="./packages/*" --filter="./fixtures/*" --filter="./packages/vite-plugin-cloudflare/playground" --filter="!./packages/kv-asset-handler" --filter="!./fixtures/interactive-dev-tests"'
@@ -147,12 +147,22 @@ jobs:
147147
env:
148148
GITHUB_TOKEN: ${{ github.token }}
149149

150-
- name: Run tests
150+
- name: Run tests (Wrangler only)
151+
# We are running Wrangler separately to be able to get early feedback on changes.
152+
# There is no point in running the fixtures if Wrangler is broken.
151153
if: steps.changes.outputs.everything_but_markdown == 'true'
152-
run: pnpm run test:ci --concurrency 1 ${{ matrix.filter }} --log-order=stream
154+
run: pnpm run test:ci --log-order=stream --filter=wrangler
155+
env:
156+
NODE_OPTIONS: "--max_old_space_size=8192"
157+
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
158+
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html
159+
CI_OS: ${{ matrix.description }}
160+
NODE_DEBUG: "@cloudflare:vite-plugin"
161+
162+
- name: Run tests (not Wrangler)
163+
if: steps.changes.outputs.everything_but_markdown == 'true'
164+
run: pnpm run test:ci --concurrency 1 ${{ matrix.filter }} --log-order=stream --filter=!wrangler
153165
env:
154-
TEST_CLOUDFLARE_API_TOKEN: ${{ secrets.TEST_CLOUDFLARE_API_TOKEN }}
155-
TEST_CLOUDFLARE_ACCOUNT_ID: ${{ secrets.TEST_CLOUDFLARE_ACCOUNT_ID }}
156166
NODE_OPTIONS: "--max_old_space_size=8192"
157167
WRANGLER_LOG_PATH: ${{ runner.temp }}/wrangler-debug-logs/
158168
TEST_REPORT_PATH: ${{ runner.temp }}/test-report/index.html

fixtures/get-platform-proxy-remote-bindings/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"description": "A test for the getPlatformProxy utility used with remote bindings",
55
"scripts": {
6-
"test:ci": "vitest run",
6+
"test:e2e": "vitest run",
77
"type:tests": "tsc --noEmit -p tests/tsconfig.json"
88
},
99
"devDependencies": {

fixtures/get-platform-proxy-remote-bindings/tests/index.test.ts

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,37 @@ if (auth) {
2727
`pnpm wrangler deploy remote-worker.js --name ${remoteWorkerName} --compatibility-date 2025-06-19`,
2828
execOptions
2929
);
30-
31-
if (!new RegExp(`Deployed\\s+${remoteWorkerName}\\b`).test(deployOut)) {
32-
throw new Error(`Failed to deploy ${remoteWorkerName}`);
33-
}
30+
const deployedUrl = deployOut.match(
31+
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
32+
)?.groups?.url;
33+
assert(deployedUrl, "Failed to find deployed worker URL");
3434

3535
const stagingDeployOut = execSync(
3636
`pnpm wrangler deploy remote-worker.staging.js --name ${remoteStagingWorkerName} --compatibility-date 2025-06-19`,
3737
execOptions
3838
);
39-
40-
if (
41-
!new RegExp(`Deployed\\s+${remoteStagingWorkerName}\\b`).test(
42-
stagingDeployOut
43-
)
44-
) {
45-
throw new Error(`Failed to deploy ${remoteStagingWorkerName}`);
46-
}
39+
const stagingDeployedUrl = stagingDeployOut.match(
40+
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
41+
)?.groups?.url;
42+
assert(stagingDeployedUrl, "Failed to find deployed staging worker URL");
43+
44+
// Wait for the deployed workers to be available
45+
await Promise.all([
46+
vi.waitFor(
47+
async () => {
48+
const response = await fetch(deployedUrl);
49+
expect(response.status).toBe(200);
50+
},
51+
{ timeout: 5000, interval: 500 }
52+
),
53+
vi.waitFor(
54+
async () => {
55+
const response = await fetch(stagingDeployedUrl);
56+
expect(response.status).toBe(200);
57+
},
58+
{ timeout: 5000, interval: 500 }
59+
),
60+
]);
4761

4862
const kvAddOut = execSync(
4963
`pnpm wrangler kv namespace create ${remoteKvName}`,
@@ -65,17 +79,31 @@ if (auth) {
6579
}, 35_000);
6680

6781
afterAll(() => {
68-
execSync(`pnpm wrangler delete --name ${remoteWorkerName}`, execOptions);
69-
execSync(
70-
`pnpm wrangler delete --name ${remoteStagingWorkerName}`,
71-
execOptions
72-
);
73-
execSync(
74-
`pnpm wrangler kv namespace delete --namespace-id=${remoteKvId}`,
75-
execOptions
76-
);
82+
try {
83+
execSync(
84+
`pnpm wrangler delete --name ${remoteWorkerName}`,
85+
execOptions
86+
);
87+
} catch {}
88+
try {
89+
execSync(
90+
`pnpm wrangler delete --name ${remoteStagingWorkerName}`,
91+
execOptions
92+
);
93+
} catch {}
94+
try {
95+
execSync(
96+
`pnpm wrangler kv namespace delete --namespace-id=${remoteKvId}`,
97+
execOptions
98+
);
99+
} catch {}
77100

78-
rmSync("./.tmp", { recursive: true, force: true });
101+
rmSync("./.tmp", {
102+
recursive: true,
103+
force: true,
104+
maxRetries: 10,
105+
retryDelay: 100,
106+
});
79107
}, 35_000);
80108

81109
describe("normal usage", () => {

fixtures/get-platform-proxy-remote-bindings/turbo.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "http://turbo.build/schema.json",
33
"extends": ["//"],
44
"tasks": {
5-
"test:ci": {
5+
"test:e2e": {
66
"env": [
77
"VITEST",
88
"NODE_DEBUG",

fixtures/vitest-pool-workers-remote-bindings/run-tests.mjs

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
* This script is used to deploy the remote workers and run the fixture using said workers
66
* with the appropriate vitest configurations.
77
*/
8-
import { execSync } from "child_process";
9-
import { randomUUID } from "crypto";
10-
import { cpSync, readFileSync, rmSync, writeFileSync } from "fs";
8+
import assert from "node:assert";
9+
import { execSync } from "node:child_process";
10+
import { randomUUID } from "node:crypto";
11+
import { cpSync, readFileSync, rmSync, writeFileSync } from "node:fs";
12+
import { setTimeout } from "node:timers/promises";
1113

1214
const env = getAuthenticatedEnv();
1315
if (!env) {
@@ -58,12 +60,21 @@ const deployOut = execSync(
5860
stdio: "pipe",
5961
cwd: "./.tmp",
6062
env,
63+
encoding: "utf8",
6164
}
6265
);
66+
6367
if (!new RegExp(`Deployed\\s+${remoteWorkerName}\\b`).test(`${deployOut}`)) {
6468
throw new Error(`Failed to deploy ${remoteWorkerName}`);
6569
}
6670

71+
const urlMatcher = new RegExp(
72+
`(?<url>https:\\/\\/${remoteWorkerName}\\..+?\\.workers\\.dev)`
73+
);
74+
75+
const deployedUrl = deployOut.match(urlMatcher)?.groups?.url;
76+
assert(deployedUrl, "Failed to find deployed worker URL");
77+
6778
writeFileSync(
6879
"./.tmp/remote-wrangler.staging.json",
6980
JSON.stringify(
@@ -84,6 +95,7 @@ const deployStagingOut = execSync(
8495
stdio: "pipe",
8596
cwd: "./.tmp",
8697
env,
98+
encoding: "utf8",
8799
}
88100
);
89101
if (
@@ -93,20 +105,50 @@ if (
93105
) {
94106
throw new Error(`Failed to deploy ${remoteStagingWorkerName}`);
95107
}
108+
const deployedStagingUrl = deployStagingOut.match(
109+
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
110+
)?.groups?.url;
111+
assert(deployedStagingUrl, "Failed to find deployed staging worker URL");
112+
113+
await setTimeout(2000);
114+
115+
// Wait for the workers to become available
116+
await Promise.all([
117+
waitFor(async () => {
118+
const response = await fetch(deployedUrl);
119+
assert.equal(response.status, 200);
120+
}),
121+
waitFor(async () => {
122+
const response = await fetch(deployedStagingUrl);
123+
assert.equal(response.status, 200);
124+
}),
125+
]);
96126

97127
try {
98-
execSync("pnpm test:vitest --config ./.tmp/vitest.workers.config.ts", {
99-
env,
100-
});
101-
execSync(
102-
"pnpm test:vitest --config ./.tmp/vitest.workers.config.staging.ts",
103-
{
104-
env,
128+
try {
129+
try {
130+
console.log("Running vitest-pool-workers remote bindings tests...");
131+
execSync("pnpm test:vitest --config ./.tmp/vitest.workers.config.ts", {
132+
env,
133+
stdio: "inherit",
134+
});
135+
console.log(
136+
"Running vitest-pool-workers remote bindings staging tests..."
137+
);
138+
execSync(
139+
"pnpm test:vitest --config ./.tmp/vitest.workers.config.staging.ts",
140+
{
141+
env,
142+
stdio: "inherit",
143+
}
144+
);
145+
} finally {
146+
execSync(`pnpm wrangler delete --name ${remoteWorkerName}`, { env });
105147
}
106-
);
148+
} finally {
149+
execSync(`pnpm wrangler delete --name ${remoteStagingWorkerName}`, { env });
150+
}
107151
} finally {
108-
execSync(`pnpm wrangler delete --name ${remoteWorkerName}`, { env });
109-
execSync(`pnpm wrangler delete --name ${remoteStagingWorkerName}`, { env });
110152
rmSync("./.tmp", { recursive: true, force: true });
111153
}
112154

@@ -133,3 +175,22 @@ function getAuthenticatedEnv() {
133175
"Skipping vitest-pool-workers remote bindings tests because the environment is not authenticated with Cloudflare."
134176
);
135177
}
178+
179+
/**
180+
* Wait for the callback to execute successfully.
181+
*
182+
* If the callback throws an error or returns a rejected promise it will continue to wait until it succeeds or times out.
183+
*/
184+
export async function waitFor(callback) {
185+
const start = Date.now();
186+
while (true) {
187+
try {
188+
return callback();
189+
} catch (error) {
190+
if (Date.now() < start + 15_000) {
191+
throw error;
192+
}
193+
}
194+
await setTimeout(1000);
195+
}
196+
}

packages/vite-plugin-cloudflare/e2e/helpers.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,17 @@ export function runCommand(
200200
while (attempts > 0) {
201201
debuglog("Running command:", command);
202202
try {
203-
childProcess.execSync(command, {
203+
const output = childProcess.execSync(command, {
204204
cwd,
205-
stdio: debuglog.enabled ? "inherit" : "ignore",
205+
stdio: "pipe",
206206
env: testEnv,
207207
timeout,
208+
encoding: "utf8",
208209
});
209-
break;
210+
if (debuglog.enabled) {
211+
process.stdout.write(output);
212+
}
213+
return output;
210214
} catch (e) {
211215
attempts--;
212216
if (attempts > 0) {

packages/wrangler/e2e/dev-env.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)("switching runtimes", () => {
5656
apiToken: process.env.CLOUDFLARE_API_TOKEN,
5757
}
5858
},
59+
server: {
60+
port: 0,
61+
},
5962
},
6063
});
6164

0 commit comments

Comments
 (0)