Skip to content

Commit

Permalink
Use unstable_dev and add e2e tests for service bindings (#4170)
Browse files Browse the repository at this point in the history
* Use `unstable_dev` and add e2e tests for service bindings

Co-authored-by: MrBBot <bcoll@cloudflare.com>
  • Loading branch information
penalosa and mrbbot committed Oct 18, 2023
1 parent 485ccf0 commit de25f9a
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 98 deletions.
103 changes: 18 additions & 85 deletions fixtures/service-bindings-app/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,107 +1,40 @@
import { fork } from "child_process";
import * as path from "path";
import { fetch } from "undici";
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import type { ChildProcess } from "child_process";

import { UnstableDevWorker, unstable_dev } from "wrangler";
describe("Service Bindings", () => {
let aProcess: ChildProcess;
let aIP: string;
let aPort: number;
let aResolveReadyPromise: (value: unknown) => void;
const aReadyPromise = new Promise((resolve) => {
aResolveReadyPromise = resolve;
});

let bProcess: ChildProcess;
let bIP: string;
let bPort: number;
let bResolveReadyPromise: (value: unknown) => void;
const bReadyPromise = new Promise((resolve) => {
bResolveReadyPromise = resolve;
});
let aWorker: UnstableDevWorker;

beforeAll(() => {
aProcess = fork(
path.join("..", "..", "..", "packages", "wrangler", "bin", "wrangler.js"),
["dev", "index.ts", "--local", "--port=0"],
{
stdio: ["ignore", "ignore", "ignore", "ipc"],
cwd: path.resolve(__dirname, "..", "a"),
}
).on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
aIP = parsedMessage.ip;
aPort = parsedMessage.port;
aResolveReadyPromise(undefined);
});
let bWorker: UnstableDevWorker;

bProcess = fork(
path.join("..", "..", "..", "packages", "wrangler", "bin", "wrangler.js"),
["dev", "index.ts", "--local", "--port=0"],
{
stdio: ["ignore", "ignore", "ignore", "ipc"],
cwd: path.resolve(__dirname, "..", "b"),
}
).on("message", (message) => {
const parsedMessage = JSON.parse(message.toString());
bIP = parsedMessage.ip;
bPort = parsedMessage.port;
bResolveReadyPromise(undefined);
beforeAll(async () => {
bWorker = await unstable_dev("./b/index.ts", {
config: "./b/wrangler.toml",
});
});

afterAll(async () => {
await aReadyPromise;
await bReadyPromise;
await new Promise((resolve, reject) => {
aProcess.once("exit", (code) => {
if (!code) {
resolve(code);
} else {
reject(code);
}
});
aProcess.kill("SIGTERM");

bProcess.once("exit", (code) => {
if (!code) {
resolve(code);
} else {
reject(code);
}
});
bProcess.kill("SIGTERM");
aWorker = await unstable_dev("./a/index.ts", {
config: "./a/wrangler.toml",
});
});

it("connects up Durable Objects and keeps state across wrangler instances", async () => {
await aReadyPromise;
await bReadyPromise;

// Service registry is polled every 300ms,
// so let's give worker A some time to find B

await new Promise((resolve) => setTimeout(resolve, 700));
});

const responseA = await fetch(`http://${aIP}:${aPort}/`);
it("connects up Durable Objects and keeps state across wrangler instances", async () => {
const responseA = await aWorker.fetch(`https://example.com/`);
const textA = await responseA.text();
expect(textA).toEqual("hello world");

const responseB = await fetch(`http://${bIP}:${bPort}/`);
const responseB = await bWorker.fetch(`/`);
const textB = await responseB.text();
expect(textB).toEqual("hello world");
});

it("gives facade service workers a constructor name of Fetcher", async () => {
await aReadyPromise;
await bReadyPromise;

// Service registry is polled every 300ms,
// so let's give worker A some time to find B
await new Promise((resolve) => setTimeout(resolve, 700));

const responseA = await fetch(`http://${aIP}:${aPort}/constructor`);
const responseA = await aWorker.fetch(`/constructor`);
const textA = await responseA.text();
expect(textA).toEqual("Fetcher");
});
afterAll(async () => {
await aWorker?.stop();
await bWorker?.stop();
});
});
152 changes: 139 additions & 13 deletions packages/wrangler/e2e/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,43 @@ async function runDevSession(
}
}

type DevWorker = {
workerName: string;
workerPath: string;
runDevSession: (
flags: string,
session: (port: number) => Promise<void>
) => ReturnType<typeof runDevSession>;
seed: (
seeder: ((name: string) => Record<string, string>) | Record<string, string>
) => ReturnType<typeof seed>;
};
async function makeWorker(): Promise<DevWorker> {
const root = await makeRoot();
const workerName = `smoke-test-worker-${crypto
.randomBytes(4)
.toString("hex")}`;
const workerPath = path.join(root, workerName);

return {
workerName,
workerPath,
runDevSession: (flags: string, session: (port: number) => Promise<void>) =>
runDevSession(workerPath, flags, session),
seed: (seeder) =>
seed(
workerPath,
typeof seeder === "function" ? seeder(workerName) : seeder
),
};
}

describe("basic dev tests", () => {
let workerName: string;
let workerPath: string;
let worker: DevWorker;

beforeEach(async () => {
const root = await makeRoot();
workerName = `smoke-test-worker-${crypto.randomBytes(4).toString("hex")}`;
workerPath = path.join(root, workerName);
await seed(workerPath, {
worker = await makeWorker();
await worker.seed((workerName) => ({
"wrangler.toml": dedent`
name = "${workerName}"
main = "src/index.ts"
Expand All @@ -64,11 +92,11 @@ describe("basic dev tests", () => {
"private": true
}
`,
});
}));
});

it("can modify worker during dev session (local)", async () => {
await runDevSession(workerPath, "", async (port) => {
await worker.runDevSession("", async (port) => {
const { text } = await retry(
(s) => s.status !== 200,
async () => {
Expand All @@ -78,7 +106,7 @@ describe("basic dev tests", () => {
);
expect(text).toMatchInlineSnapshot('"Hello World!"');

await seed(workerPath, {
await worker.seed({
"src/index.ts": dedent`
export default {
fetch(request, env) {
Expand All @@ -96,7 +124,7 @@ describe("basic dev tests", () => {
);
expect(text2).toMatchInlineSnapshot('"Updated Worker! value"');

await seed(workerPath, {
await worker.seed((workerName) => ({
"wrangler.toml": dedent`
name = "${workerName}"
main = "src/index.ts"
Expand All @@ -105,7 +133,7 @@ describe("basic dev tests", () => {
[vars]
KEY = "updated"
`,
});
}));
const { text: text3 } = await retry(
(s) => s.status !== 200 || s.text === "Updated Worker! value",
async () => {
Expand All @@ -118,7 +146,7 @@ describe("basic dev tests", () => {
});

it("can modify worker during dev session (remote)", async () => {
await runDevSession(workerPath, "--remote --ip 127.0.0.1", async (port) => {
await worker.runDevSession("--remote --ip 127.0.0.1", async (port) => {
const { text } = await retry(
(s) => s.status !== 200 || s.text === "",
async () => {
Expand All @@ -128,7 +156,7 @@ describe("basic dev tests", () => {
);
expect(text).toMatchInlineSnapshot('"Hello World!"');

await seed(workerPath, {
await worker.seed({
"src/index.ts": dedent`
export default {
fetch(request) {
Expand All @@ -152,3 +180,101 @@ describe("basic dev tests", () => {
});
});
});

describe("dev registry", () => {
let a: DevWorker;
let b: DevWorker;

beforeEach(async () => {
a = await makeWorker();
await a.seed({
"wrangler.toml": dedent`
name = "a"
main = "src/index.ts"
[[services]]
binding = "BEE"
service = 'b'
`,
"src/index.ts": dedent/* javascript */ `
export default {
fetch(req, env) {
return env.BEE.fetch(req);
},
};
`,
"package.json": dedent`
{
"name": "a",
"version": "0.0.0",
"private": true
}
`,
});

b = await makeWorker();
await b.seed({
"wrangler.toml": dedent`
name = "b"
main = "src/index.ts"
compatibility_date = "2023-01-01"
`,
"src/index.ts": dedent/* javascript */ `
export default{
fetch() {
return new Response("hello world");
},
};
`,
"package.json": dedent`
{
"name": "b",
"version": "0.0.0",
"private": true
}
`,
});
});

it("can fetch b", async () => {
await b.runDevSession("", async (bPort) => {
const { text } = await retry(
(s) => s.status !== 200,
async () => {
const r = await fetch(`http://127.0.0.1:${bPort}`);
return { text: await r.text(), status: r.status };
}
);
expect(text).toMatchInlineSnapshot('"hello world"');
});
});

it("can fetch b through a (start b, start a)", async () => {
await b.runDevSession("", async () => {
await a.runDevSession("", async (aPort) => {
const { text } = await retry(
(s) => s.status !== 200,
async () => {
const r = await fetch(`http://127.0.0.1:${aPort}`);
return { text: await r.text(), status: r.status };
}
);
expect(text).toMatchInlineSnapshot('"hello world"');
});
});
});
it("can fetch b through a (start a, start b)", async () => {
await a.runDevSession("", async (aPort) => {
await b.runDevSession("", async () => {
const { text } = await retry(
(s) => s.status !== 200,
async () => {
const r = await fetch(`http://127.0.0.1:${aPort}`);
return { text: await r.text(), status: r.status };
}
);
expect(text).toMatchInlineSnapshot('"hello world"');
});
});
});
});

0 comments on commit de25f9a

Please sign in to comment.