Skip to content
Draft
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
273 changes: 181 additions & 92 deletions fixtures/dev-registry/tests/dev-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as path from "node:path";
import { resolve } from "node:path";
/* eslint-disable workers-sdk/no-vitest-import-expect -- uses expect in module-scope helper functions */
import {
describe as baseDescribe,
describe,
expect,
onTestFailed,
onTestFinished,
Expand All @@ -18,11 +18,6 @@ import {
} from "../../../packages/vite-plugin-cloudflare/e2e/helpers";
import { runWranglerDev as baseRunWranglerDev } from "../../shared/src/run-wrangler-long-lived";

// TODO: These tests are consistently failing on Windows in CI and are blocking
// other work. Skipping them there as a temporary measure until the underlying
// issue is fixed. There's still value in running them on macOS and Linux.
const describe = baseDescribe.skipIf(process.platform === "win32");

const waitForTimeout = 20_000;
const cwd = resolve(__dirname, "..");
const tmpPathBase = path.join(os.tmpdir(), "wrangler-tests");
Expand All @@ -32,8 +27,18 @@ const it = test.extend<{
// Fixture for creating a temporary directory
async devRegistryPath({}, use) {
const tmpPath = await fs.realpath(await fs.mkdtemp(tmpPathBase));

// Fixture teardown runs *before* `onTestFinished` callbacks, so removing
// the directory here would pull the registry out from under dev sessions
// that are still running. Registering the cleanup as an
// `onTestFinished` callback during fixture setup instead makes it the
// first one registered, and therefore the last one to run under Vitest's
// LIFO ordering — after every dev session has exited.
onTestFinished(async () => {
await fs.rm(tmpPath, { recursive: true, maxRetries: 10 });
});

await use(tmpPath);
await fs.rm(tmpPath, { recursive: true, maxRetries: 10 });
},
});

Expand Down Expand Up @@ -94,6 +99,27 @@ async function runWranglerDev(
return url;
}

/**
* Starts a tail consumer, then its producer, returning both URLs.
*
* Vitest runs `onTestFinished` callbacks in LIFO order, so the session started
* last is torn down first. Starting the producer last therefore guarantees it
* is killed while its consumer is still running.
*
* The reverse order is not safe: killing a dev session that another running
* session is forwarding tail events to aborts workerd on the surviving side on
* Windows, which restarts the dev server mid-teardown and times the test out.
* The `tail_consumers` in this fixture are one-directional for the same reason
* — with a cycle there is no order that keeps every producer shorter-lived than
* its consumer.
*/
async function startTailPair(
startConsumer: () => Promise<string>,
startProducer: () => Promise<string>
): Promise<[consumer: string, producer: string]> {
return [await startConsumer(), await startProducer()];
}

async function setupPlatformProxy(config: string, devRegistryPath?: string) {
vi.stubEnv("WRANGLER_REGISTRY_PATH", devRegistryPath);

Expand Down Expand Up @@ -400,47 +426,33 @@ describe("Dev Registry: wrangler dev <-> wrangler dev", () => {
}, waitForTimeout);
});

it("supports tail handler", async ({ devRegistryPath }) => {
const exportedHandlerWithAssets = await runWranglerDev(
"wrangler.exported-handler-with-assets.jsonc",
devRegistryPath
);
const workerEntrypoint = await runWranglerDev(
[
"wrangler.worker-entrypoint.jsonc",
"wrangler.internal-durable-object.jsonc",
],
devRegistryPath
it("supports tail handler when the consumer has assets", async ({
devRegistryPath,
}) => {
// The producer runs alongside a second worker so that its logs are
// prefixed with the worker name, exercising multi-worker sessions too
const [exportedHandlerWithAssets, workerEntrypoint] = await startTailPair(
() =>
runWranglerDev(
"wrangler.exported-handler-with-assets.jsonc",
devRegistryPath
),
() =>
runWranglerDev(
[
"wrangler.worker-entrypoint.jsonc",
"wrangler.internal-durable-object.jsonc",
],
devRegistryPath
)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of worker-entrypoint via exported handler
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello world", "this is the 2nd log"]),
});
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["some other log"]),
});

const response = await fetch(`${workerEntrypoint}?${searchParams}`);

expect(await response.json()).toEqual({
worker: "Worker Entrypoint",
tailEvents: expect.arrayContaining([
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
]),
});
}, waitForTimeout);

await vi.waitFor(async () => {
// Trigger tail handler of exported-handler via worker-entrypoint
// Trigger tail handler of exported-handler-with-assets via worker-entrypoint
await fetch(`${workerEntrypoint}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello from test"]),
Expand Down Expand Up @@ -469,6 +481,45 @@ describe("Dev Registry: wrangler dev <-> wrangler dev", () => {
}, waitForTimeout);
});

it("supports tail handler when the producer has assets", async ({
devRegistryPath,
}) => {
const [exportedHandler, exportedHandlerWithAssets] = await startTailPair(
() => runWranglerDev("wrangler.exported-handler.jsonc", devRegistryPath),
() =>
runWranglerDev(
"wrangler.exported-handler-with-assets.jsonc",
devRegistryPath
)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of exported-handler via exported-handler-with-assets
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello world", "this is the 2nd log"]),
});
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["some other log"]),
});

const response = await fetch(`${exportedHandler}?${searchParams}`);

expect(await response.json()).toEqual({
worker: "exported-handler",
tailEvents: expect.arrayContaining([
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
]),
});
}, waitForTimeout);
});

it("supports queues across dev sessions", async ({ devRegistryPath }) => {
const exportedHandler = await runWranglerDev(
"wrangler.exported-handler.jsonc",
Expand Down Expand Up @@ -715,62 +766,81 @@ describe("Dev Registry: vite dev <-> vite dev", () => {
}, waitForTimeout);
});

it("supports tail handler", async ({ devRegistryPath }) => {
const exportedHandler = await runViteDev(
"vite.exported-handler.config.ts",
devRegistryPath
);
const workerEntrypointWithAssets = await runViteDev(
"vite.worker-entrypoint-with-assets.config.ts",
devRegistryPath
it("supports tail handler when the consumer has assets", async ({
devRegistryPath,
}) => {
const [exportedHandlerWithAssets, workerEntrypoint] = await startTailPair(
() =>
runViteDev(
"vite.exported-handler-with-assets.config.ts",
devRegistryPath
),
() => runViteDev("vite.worker-entrypoint.config.ts", devRegistryPath)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of worker-entrypoint via exported-handler
await fetch(`${exportedHandler}?${searchParams}`, {
// Trigger tail handler of exported-handler-with-assets via worker-entrypoint
await fetch(`${workerEntrypoint}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello world", "this is the 2nd log"]),
body: JSON.stringify(["hello from test"]),
});
await fetch(`${exportedHandler}?${searchParams}`, {
await fetch(`${workerEntrypoint}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["some other log"]),
body: JSON.stringify(["yet another log", "and another one"]),
});

const response = await fetch(
`${workerEntrypointWithAssets}?${searchParams}`
`${exportedHandlerWithAssets}?${searchParams}`
);

expect(await response.json()).toEqual({
worker: "Worker Entrypoint",
worker: "exported-handler",
tailEvents: expect.arrayContaining([
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
[["[Worker Entrypoint]"], ["hello from test"]],
[["[Worker Entrypoint]"], ["yet another log", "and another one"]],
]),
});
}, waitForTimeout);
});

it("supports tail handler when the producer has assets", async ({
devRegistryPath,
}) => {
const [exportedHandler, exportedHandlerWithAssets] = await startTailPair(
() => runViteDev("vite.exported-handler.config.ts", devRegistryPath),
() =>
runViteDev(
"vite.exported-handler-with-assets.config.ts",
devRegistryPath
)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of exported-handler via worker-entrypoint
await fetch(`${workerEntrypointWithAssets}?${searchParams}`, {
// Trigger tail handler of exported-handler via exported-handler-with-assets
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello from test"]),
body: JSON.stringify(["hello world", "this is the 2nd log"]),
});
await fetch(`${workerEntrypointWithAssets}?${searchParams}`, {
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["yet another log", "and another one"]),
body: JSON.stringify(["some other log"]),
});

const response = await fetch(`${exportedHandler}?${searchParams}`);

expect(await response.json()).toEqual({
worker: "exported-handler",
tailEvents: expect.arrayContaining([
[["[Worker Entrypoint]"], ["hello from test"]],
[["[Worker Entrypoint]"], ["yet another log", "and another one"]],
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
]),
});
}, waitForTimeout);
Expand Down Expand Up @@ -977,62 +1047,81 @@ describe("Dev Registry: vite dev <-> wrangler dev", () => {
}, waitForTimeout);
});

it("supports tail handler", async ({ devRegistryPath }) => {
const exportedHandlerWithStaticAssets = await runViteDev(
"vite.exported-handler-with-assets.config.ts",
devRegistryPath
);
const workerEntrypoint = await runWranglerDev(
"wrangler.worker-entrypoint.jsonc",
devRegistryPath
it("supports tail handler from wrangler dev to vite dev", async ({
devRegistryPath,
}) => {
const [exportedHandlerWithAssets, workerEntrypoint] = await startTailPair(
() =>
runViteDev(
"vite.exported-handler-with-assets.config.ts",
devRegistryPath
),
() => runWranglerDev("wrangler.worker-entrypoint.jsonc", devRegistryPath)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of worker-entrypoint via exported-handler
await fetch(`${exportedHandlerWithStaticAssets}?${searchParams}`, {
// Trigger tail handler of exported-handler-with-assets via worker-entrypoint
await fetch(`${workerEntrypoint}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello world", "this is the 2nd log"]),
body: JSON.stringify(["hello from test"]),
});
await fetch(`${exportedHandlerWithStaticAssets}?${searchParams}`, {
await fetch(`${workerEntrypoint}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["some other log"]),
body: JSON.stringify(["yet another log", "and another one"]),
});

const response = await fetch(`${workerEntrypoint}?${searchParams}`);
const response = await fetch(
`${exportedHandlerWithAssets}?${searchParams}`
);

expect(await response.json()).toEqual({
worker: "Worker Entrypoint",
worker: "exported-handler",
tailEvents: expect.arrayContaining([
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
[["[Worker Entrypoint]"], ["hello from test"]],
[["[Worker Entrypoint]"], ["yet another log", "and another one"]],
]),
});
}, waitForTimeout);
});

it("supports tail handler from vite dev to wrangler dev", async ({
devRegistryPath,
}) => {
const [exportedHandler, exportedHandlerWithAssets] = await startTailPair(
() => runWranglerDev("wrangler.exported-handler.jsonc", devRegistryPath),
() =>
runViteDev(
"vite.exported-handler-with-assets.config.ts",
devRegistryPath
)
);

const searchParams = new URLSearchParams({
"test-method": "tail",
});

await vi.waitFor(async () => {
// Trigger tail handler of exported-handler via worker-entrypoint
await fetch(`${workerEntrypoint}?${searchParams}`, {
// Trigger tail handler of exported-handler via exported-handler-with-assets
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["hello from test"]),
body: JSON.stringify(["hello world", "this is the 2nd log"]),
});
await fetch(`${workerEntrypoint}?${searchParams}`, {
await fetch(`${exportedHandlerWithAssets}?${searchParams}`, {
method: "POST",
body: JSON.stringify(["yet another log", "and another one"]),
body: JSON.stringify(["some other log"]),
});

const response = await fetch(
`${exportedHandlerWithStaticAssets}?${searchParams}`
);
const response = await fetch(`${exportedHandler}?${searchParams}`);

expect(await response.json()).toEqual({
worker: "exported-handler",
tailEvents: expect.arrayContaining([
[["[Worker Entrypoint]"], ["hello from test"]],
[["[Worker Entrypoint]"], ["yet another log", "and another one"]],
[["[exported-handler]"], ["hello world", "this is the 2nd log"]],
[["[exported-handler]"], ["some other log"]],
]),
});
}, waitForTimeout);
Expand Down
Loading
Loading