Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions packages/wrangler/e2e/remote-binding/remote-bindings-api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
import assert from "node:assert";
import { resolve } from "node:path";
import { afterAll, beforeAll, describe, expect, test } from "vitest";
import { CLOUDFLARE_ACCOUNT_ID } from "../helpers/account-id";
import { WranglerE2ETestHelper } from "../helpers/e2e-wrangler-test";
import { generateResourceName } from "../helpers/generate-resource-name";
import type {
Miniflare,
MiniflareOptions,
RemoteProxyConnectionString,
Response,
} from "miniflare";

// Note: the tests in this file are simple ones that check basic functionalities of the remote bindings programmatic APIs
// various other aspects of these APIs (e.g. different bindings, reloading capabilities) are indirectly tested when
// generally testing remote bindings

describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)(
"wrangler dev - remote bindings - programmatic API",
async () => {
const remoteWorkerName = generateResourceName();
const helper = new WranglerE2ETestHelper();

const { Miniflare } = await helper.importMiniflare();

const {
experimental_startRemoteProxySession: startRemoteProxySession,
experimental_maybeStartOrUpdateRemoteProxySession:
maybeStartOrUpdateRemoteProxySession,
} = await helper.importWrangler();

beforeAll(async () => {
await helper.seed(resolve(__dirname, "./workers"));
await helper.run(
`wrangler deploy remote-worker.js --name ${remoteWorkerName} --compatibility-date 2025-01-01`
);
}, 35_000);

afterAll(async () => {
await helper.run(`wrangler delete --name ${remoteWorkerName}`);
});

function getMfOptions(
remoteProxyConnectionString: RemoteProxyConnectionString
): MiniflareOptions {
return {
modules: true,
script: `
export default {
async fetch(req, env) {
const myServiceMsg = !env.MY_SERVICE ? null : await (await env.MY_SERVICE.fetch(req)).text();
return new Response("worker response: " + (myServiceMsg ?? ""));
}
}`,
serviceBindings: {
MY_SERVICE: {
name: remoteWorkerName,
remoteProxyConnectionString,
},
},
};
}

describe("startRemoteProxySession", () => {
test("base usage", async () => {
const remoteProxySession = await startRemoteProxySession({
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
},
});
await remoteProxySession.ready;

const mf = new Miniflare(
getMfOptions(remoteProxySession.remoteProxyConnectionString)
);

const response = await timedDispatchFetch(mf);
const responseText = await response?.text();

expect(responseText).toEqual(
"worker response: Hello from a remote worker"
);

await mf.dispose();
await remoteProxySession.dispose();
});

test("user provided (incorrect but then corrected) auth data", async () => {
const remoteProxySession = await startRemoteProxySession(
{
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
},
},
{
auth: {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: "This is an incorrect API TOKEN!",
},
},
}
);
await remoteProxySession.ready;

const mf = new Miniflare(
getMfOptions(remoteProxySession.remoteProxyConnectionString)
);

const noResponse = await timedDispatchFetch(mf);
// We are unable to fetch from the worker since the remote connection is not correctly established
expect(noResponse).toBe(null);

assert(process.env.CLOUDFLARE_API_TOKEN);

const amendedRemoteProxySession = await startRemoteProxySession(
{
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
},
},
{
auth: {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: process.env.CLOUDFLARE_API_TOKEN,
},
},
}
);

await amendedRemoteProxySession.ready;

await mf.setOptions(
getMfOptions(amendedRemoteProxySession.remoteProxyConnectionString)
);

const response = await timedDispatchFetch(mf);
const responseText = await response?.text();

expect(responseText).toEqual(
"worker response: Hello from a remote worker"
);

await mf.dispose();
await remoteProxySession.dispose();
await amendedRemoteProxySession.dispose();
});
});

describe("maybeStartOrUpdateRemoteProxySession", () => {
test("base usage", async () => {
const proxySessionData = await maybeStartOrUpdateRemoteProxySession({
bindings: {
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
experimental_remote: true,
},
},
});

assert(proxySessionData);

await proxySessionData.session.ready;

const mf = new Miniflare(
getMfOptions(proxySessionData.session.remoteProxyConnectionString)
);

const response = await timedDispatchFetch(mf);
const responseText = await response?.text();

expect(responseText).toEqual(
"worker response: Hello from a remote worker"
);

await mf.dispose();
await proxySessionData.session.dispose();
});

test("user provided (incorrect but then corrected) auth data", async () => {
let proxySessionData = await maybeStartOrUpdateRemoteProxySession(
{
bindings: {
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
experimental_remote: true,
},
},
},
undefined,
{
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: "This is an incorrect API TOKEN!",
},
}
);

assert(proxySessionData);

await proxySessionData.session.ready;

const mf = new Miniflare(
getMfOptions(proxySessionData.session.remoteProxyConnectionString)
);

const noResponse = await timedDispatchFetch(mf);
// We are unable to fetch from the worker since the remote connection is not correctly established
expect(noResponse).toBe(null);

assert(process.env.CLOUDFLARE_API_TOKEN);

proxySessionData = await maybeStartOrUpdateRemoteProxySession(
{
bindings: {
MY_SERVICE: {
type: "service",
service: remoteWorkerName,
experimental_remote: true,
},
},
},
proxySessionData,
{
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: process.env.CLOUDFLARE_API_TOKEN,
},
}
);

assert(proxySessionData);

await mf.setOptions(
getMfOptions(proxySessionData.session.remoteProxyConnectionString)
);

const response = await timedDispatchFetch(mf);
const responseText = await response?.text();

expect(responseText).toEqual(
"worker response: Hello from a remote worker"
);

await mf.dispose();
await proxySessionData.session.dispose();
});
});
}
);

async function timedDispatchFetch(mf: Miniflare): Promise<Response | null> {
try {
return await mf.dispatchFetch("http://localhost/", {
signal: AbortSignal.timeout(5000),
});
} catch {
return null;
}
}
3 changes: 0 additions & 3 deletions packages/wrangler/src/api/remoteBindings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ type WorkerConfigObject = {
/**
* Utility for potentially starting or updating a remote proxy session.
*
* It uses an internal map for storing existing remote proxy session indexed by worker names. If no worker name is provided
* the remote proxy session won't be retrieved nor saved to/from the internal map.
*
* @param wranglerOrWorkerConfigObject either a file path to a wrangler configuration file or an object containing the name of
* the target worker alongside its bindings.
* @param preExistingRemoteProxySessionData the optional data of a pre-existing remote proxy session if there was one, this
Expand Down
Loading