@@ -2,9 +2,12 @@ import { execSync } from "child_process";
22import { randomUUID } from "crypto" ;
33import assert from "node:assert" ;
44import { mkdirSync , rmSync , writeFileSync } from "node:fs" ;
5- import { Fetcher , KVNamespace } from "@cloudflare/workers-types/experimental" ;
65import { afterAll , beforeAll , describe , expect , test , vi } from "vitest" ;
76import { getPlatformProxy } from "wrangler" ;
7+ import type { KVNamespace } from "@cloudflare/workers-types/experimental" ;
8+ import type { DispatchFetch , Response } from "miniflare" ;
9+
10+ type Fetcher = { fetch : DispatchFetch } ;
811
912const auth = getAuthenticatedEnv ( ) ;
1013const execOptions = {
@@ -15,123 +18,158 @@ const remoteWorkerName = `tmp-e2e-worker-test-remote-bindings-${randomUUID().spl
1518const remoteKvName = `tmp-e2e-remote-kv-test-remote-bindings-${ randomUUID ( ) . split ( "-" ) [ 0 ] } ` ;
1619
1720if ( auth ) {
18- describe ( "getPlatformProxy - remote bindings" , ( ) => {
19- let remoteKvId : string ;
20- beforeAll ( async ( ) => {
21- const deployOut = execSync (
22- `pnpm wrangler deploy remote-worker.js --name ${ remoteWorkerName } --compatibility-date 2025-06-19` ,
23- execOptions
24- ) ;
25-
26- if ( ! new RegExp ( `Deployed\\s+${ remoteWorkerName } \\b` ) . test ( deployOut ) ) {
27- throw new Error ( `Failed to deploy ${ remoteWorkerName } ` ) ;
28- }
29-
30- const kvAddOut = execSync (
31- `pnpm wrangler kv namespace create ${ remoteKvName } ` ,
32- execOptions
33- ) ;
34-
35- const createdKvRegexMatch = kvAddOut . match ( / " i d " : " (?< id > [ ^ " ] * ?) " / ) ;
36- const maybeRemoteKvId = createdKvRegexMatch ?. groups ?. [ "id" ] ;
37- assert ( maybeRemoteKvId , `Failed to create remote kv ${ remoteKvName } ` ) ;
38- remoteKvId = maybeRemoteKvId ;
39-
40- execSync (
41- `pnpm wrangler kv key put test-key remote-kv-value --namespace-id=${ remoteKvId } --remote` ,
42- execOptions
43- ) ;
44-
45- rmSync ( "./.tmp" , { recursive : true , force : true } ) ;
46-
47- mkdirSync ( "./.tmp" ) ;
48-
49- writeFileSync (
50- "./.tmp/wrangler.json" ,
51- JSON . stringify (
52- {
53- name : "get-platform-proxy-fixture-test" ,
54- compatibility_date : "2025-06-01" ,
55- services : [
56- {
57- binding : "MY_WORKER" ,
58- service : remoteWorkerName ,
59- experimental_remote : true ,
60- } ,
61- ] ,
62- kv_namespaces : [
63- {
64- binding : "MY_KV" ,
65- id : remoteKvId ,
66- experimental_remote : true ,
67- } ,
68- ] ,
69- } ,
70- undefined ,
71- 2
72- ) ,
73- "utf8"
74- ) ;
75- } , 25_000 ) ;
76-
77- afterAll ( ( ) => {
78- execSync ( `pnpm wrangler delete --name ${ remoteWorkerName } ` , execOptions ) ;
79- execSync (
80- `pnpm wrangler kv namespace delete --namespace-id=${ remoteKvId } ` ,
81- execOptions
82- ) ;
83- rmSync ( "./.tmp" , { recursive : true , force : true } ) ;
84- } , 25_000 ) ;
85-
86- test ( "getPlatformProxy works with remote bindings" , async ( ) => {
87- vi . stubEnv ( "CLOUDFLARE_ACCOUNT_ID" , auth . CLOUDFLARE_ACCOUNT_ID ) ;
88- vi . stubEnv ( "CLOUDFLARE_API_TOKEN" , auth . CLOUDFLARE_API_TOKEN ) ;
89- const { env, dispose } = await getPlatformProxy < {
90- MY_WORKER : Fetcher ;
91- MY_KV : KVNamespace ;
92- } > ( {
93- configPath : "./.tmp/wrangler.json" ,
94- experimental : { remoteBindings : true } ,
21+ describe (
22+ "getPlatformProxy - remote bindings" ,
23+ ( ) => {
24+ let remoteKvId : string ;
25+ beforeAll ( async ( ) => {
26+ const deployOut = execSync (
27+ `pnpm wrangler deploy remote-worker.js --name ${ remoteWorkerName } --compatibility-date 2025-06-19` ,
28+ execOptions
29+ ) ;
30+
31+ if ( ! new RegExp ( `Deployed\\s+${ remoteWorkerName } \\b` ) . test ( deployOut ) ) {
32+ throw new Error ( `Failed to deploy ${ remoteWorkerName } ` ) ;
33+ }
34+
35+ const kvAddOut = execSync (
36+ `pnpm wrangler kv namespace create ${ remoteKvName } ` ,
37+ execOptions
38+ ) ;
39+
40+ const createdKvRegexMatch = kvAddOut . match ( / " i d " : " (?< id > [ ^ " ] * ?) " / ) ;
41+ const maybeRemoteKvId = createdKvRegexMatch ?. groups ?. [ "id" ] ;
42+ assert ( maybeRemoteKvId , `Failed to create remote kv ${ remoteKvName } ` ) ;
43+ remoteKvId = maybeRemoteKvId ;
44+
45+ execSync (
46+ `pnpm wrangler kv key put test-key remote-kv-value --namespace-id=${ remoteKvId } --remote` ,
47+ execOptions
48+ ) ;
49+
50+ rmSync ( "./.tmp" , { recursive : true , force : true } ) ;
51+
52+ mkdirSync ( "./.tmp" ) ;
53+
54+ writeFileSync (
55+ "./.tmp/wrangler.json" ,
56+ JSON . stringify (
57+ {
58+ name : "get-platform-proxy-fixture-test" ,
59+ compatibility_date : "2025-06-01" ,
60+ services : [
61+ {
62+ binding : "MY_WORKER" ,
63+ service : remoteWorkerName ,
64+ experimental_remote : true ,
65+ } ,
66+ ] ,
67+ kv_namespaces : [
68+ {
69+ binding : "MY_KV" ,
70+ id : remoteKvId ,
71+ experimental_remote : true ,
72+ } ,
73+ ] ,
74+ } ,
75+ undefined ,
76+ 2
77+ ) ,
78+ "utf8"
79+ ) ;
80+ } , 25_000 ) ;
81+
82+ afterAll ( ( ) => {
83+ execSync (
84+ `pnpm wrangler delete --name ${ remoteWorkerName } ` ,
85+ execOptions
86+ ) ;
87+ execSync (
88+ `pnpm wrangler kv namespace delete --namespace-id=${ remoteKvId } ` ,
89+ execOptions
90+ ) ;
91+ rmSync ( "./.tmp" , { recursive : true , force : true } ) ;
92+ } , 25_000 ) ;
93+
94+ test ( "getPlatformProxy works with remote bindings" , async ( ) => {
95+ vi . stubEnv ( "CLOUDFLARE_ACCOUNT_ID" , auth . CLOUDFLARE_ACCOUNT_ID ) ;
96+ vi . stubEnv ( "CLOUDFLARE_API_TOKEN" , auth . CLOUDFLARE_API_TOKEN ) ;
97+
98+ const { env, dispose } = await getPlatformProxy < {
99+ MY_WORKER : Fetcher ;
100+ MY_KV : KVNamespace ;
101+ } > ( {
102+ configPath : "./.tmp/wrangler.json" ,
103+ experimental : { remoteBindings : true } ,
104+ } ) ;
105+
106+ const response = await fetchFromWorker ( env . MY_WORKER , "OK" ) ;
107+ const workerText = await response ?. text ( ) ;
108+ expect ( workerText ) . toEqual (
109+ "Hello from a remote Worker part of the getPlatformProxy remote bindings fixture!"
110+ ) ;
111+
112+ const kvValue = await env . MY_KV . get ( "test-key" ) ;
113+ expect ( kvValue ) . toEqual ( "remote-kv-value" ) ;
114+
115+ await dispose ( ) ;
95116 } ) ;
96117
97- const workerText = await (
98- await env . MY_WORKER . fetch ( "http://example.com" )
99- ) . text ( ) ;
100- expect ( workerText ) . toEqual (
101- "Hello from a remote Worker part of the getPlatformProxy remote bindings fixture!"
102- ) ;
103-
104- const kvValue = await env . MY_KV . get ( "test-key" ) ;
105- expect ( kvValue ) . toEqual ( "remote-kv-value" ) ;
106-
107- await dispose ( ) ;
108- } ) ;
109-
110- test ( "getPlatformProxy does not work with remote bindings if the experimental remoteBindings flag is not turned on" , async ( ) => {
111- const { env, dispose } = await getPlatformProxy < {
112- MY_WORKER : Fetcher ;
113- MY_KV : KVNamespace ;
114- } > ( {
115- configPath : "./.tmp/wrangler.json" ,
118+ test ( "getPlatformProxy does not work with remote bindings if the experimental remoteBindings flag is not turned on" , async ( ) => {
119+ const { env, dispose } = await getPlatformProxy < {
120+ MY_WORKER : Fetcher ;
121+ MY_KV : KVNamespace ;
122+ } > ( {
123+ configPath : "./.tmp/wrangler.json" ,
124+ } ) ;
125+
126+ const response = await fetchFromWorker (
127+ env . MY_WORKER ,
128+ "Service Unavailable"
129+ ) ;
130+ const workerText = await response ?. text ( ) ;
131+ expect ( workerText ) . toEqual (
132+ `[wrangler] Couldn\'t find \`wrangler dev\` session for service "${ remoteWorkerName } " to proxy to`
133+ ) ;
134+
135+ const kvValue = await env . MY_KV . get ( "test-key" ) ;
136+ expect ( kvValue ) . toEqual ( null ) ;
137+
138+ await dispose ( ) ;
116139 } ) ;
117-
118- const workerText = await (
119- await env . MY_WORKER . fetch ( "http://example.com" )
120- ) . text ( ) ;
121- expect ( workerText ) . toEqual (
122- `[wrangler] Couldn\'t find \`wrangler dev\` session for service "${ remoteWorkerName } " to proxy to`
123- ) ;
124-
125- const kvValue = await env . MY_KV . get ( "test-key" ) ;
126- expect ( kvValue ) . toEqual ( null ) ;
127-
128- await dispose ( ) ;
129- } ) ;
130- } ) ;
140+ } ,
141+ { timeout : 50_000 }
142+ ) ;
131143} else {
132144 test . skip ( "getPlatformProxy - remote bindings (no auth credentials)" ) ;
133145}
134146
147+ /**
148+ * Tries to fetch from a worker multiple times until a response is returned which matches a specified
149+ * statusText. Each fetch has a timeout signal making sure that it can't simply get stuck.
150+ *
151+ * This utility is used, instead of directly fetching from the Worker in order to prevent flakiness.
152+ *
153+ * @param worker The Worker to fetch from.
154+ * @param expectedStatusText The response's expected statusText.
155+ * @returns The successful Worker's response or null if no such response was obtained.
156+ */
157+ async function fetchFromWorker (
158+ worker : Fetcher ,
159+ expectedStatusText : string
160+ ) : Promise < Response > {
161+ return vi . waitFor (
162+ async ( ) => {
163+ const response = await worker . fetch ( "http://example.com" , {
164+ signal : AbortSignal . timeout ( 5_000 ) ,
165+ } ) ;
166+ expect ( response . statusText ) . toEqual ( expectedStatusText ) ;
167+ return response ;
168+ } ,
169+ { timeout : 30_000 , interval : 500 }
170+ ) ;
171+ }
172+
135173/**
136174 * Gets an env object containing Cloudflare credentials or undefined if not authenticated.
137175 *
0 commit comments