Files
wehub-resource-sync 70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:11 +08:00

319 lines
7.8 KiB
TypeScript

import path from "node:path";
import dedent from "ts-dedent";
import { afterEach, assert, beforeEach, describe, test, vi } from "vitest";
import { CLOUDFLARE_ACCOUNT_ID } from "./helpers/account-id";
import {
importWrangler,
WranglerE2ETestHelper,
} from "./helpers/e2e-wrangler-test";
import { waitForLong } from "./helpers/wait-for";
import type { Worker } from "../src/api/startDevWorker";
import type { ExpectStatic, MockInstance } from "vitest";
const { unstable_startWorker: startWorker } = await importWrangler();
describe("startWorker - auth options", { sequential: true }, () => {
let worker: Worker | undefined;
describe.skipIf(!CLOUDFLARE_ACCOUNT_ID)("with remote bindings", () => {
let helper: WranglerE2ETestHelper;
let consoleErrorMock: MockInstance<typeof console.error>;
beforeEach(async () => {
consoleErrorMock = vi.spyOn(console, "error").mockImplementation(() => {
// suppress error output during tests
});
helper = new WranglerE2ETestHelper();
const aiWorkerScript = dedent`
export default {
async fetch(_request, env) {
const messages = [
{
role: "user",
content:
"Respond with the exact text 'This is a response from Workers AI.'. Do not include any other text",
},
];
const content = await env.AI.run("@cf/meta/llama-3.1-8b-instruct-fast", {
messages,
});
return new Response(content.response);
},
}
`;
await helper.seed({
"src/index.js": aiWorkerScript,
});
});
afterEach(() => worker?.dispose());
test("starting a worker with startWorker with the valid auth information and updating it with invalid information", async ({
expect,
}) => {
const validAuth = vi.fn(() => {
assert(process.env.CLOUDFLARE_API_TOKEN);
return {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: process.env.CLOUDFLARE_API_TOKEN,
},
};
});
let emittedErrors: unknown[] = [];
worker = await startWorker({
entrypoint: path.resolve(helper.tmpPath, "src/index.js"),
bindings: {
AI: {
type: "ai",
remote: true,
},
},
dev: {
auth: validAuth,
inspector: false,
server: {
port: 0,
},
},
});
worker.raw.on("error", (e: unknown) => emittedErrors.push(e));
await assertValidWorkerAiResponse(expect, emittedErrors);
expect(validAuth).toHaveBeenCalled();
emittedErrors = [];
const incorrectAuth = vi.fn(() => {
return {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: "This is an incorrect API TOKEN!",
},
};
});
await worker.patchConfig({
dev: {
auth: incorrectAuth,
},
});
await assertAuthErrorForWorkerAiResponse(expect, emittedErrors);
expect(incorrectAuth).toHaveBeenCalled();
});
test("starting a worker with startWorker with invalid auth information and updating it with valid auth information", async ({
expect,
}) => {
const incorrectAuth = vi.fn(() => {
return {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: "This is an incorrect API TOKEN!",
},
};
});
let emittedErrors: unknown[] = [];
worker = await startWorker({
entrypoint: path.resolve(helper.tmpPath, "src/index.js"),
bindings: {
AI: {
type: "ai",
remote: true,
},
},
dev: {
auth: incorrectAuth,
inspector: false,
server: {
port: 0,
},
},
});
worker.raw.on("error", (e: unknown) => emittedErrors.push(e));
await assertAuthErrorForWorkerAiResponse(expect, emittedErrors);
expect(incorrectAuth).toHaveBeenCalled();
consoleErrorMock.mockReset();
emittedErrors = [];
const validAuth = vi.fn(() => {
assert(process.env.CLOUDFLARE_API_TOKEN);
return {
accountId: CLOUDFLARE_ACCOUNT_ID,
apiToken: {
apiToken: process.env.CLOUDFLARE_API_TOKEN,
},
};
});
await worker.patchConfig({
dev: {
auth: validAuth,
},
});
await assertValidWorkerAiResponse(expect, emittedErrors);
expect(validAuth).toHaveBeenCalled();
});
async function assertValidWorkerAiResponse(
expect: ExpectStatic,
emittedErrors: unknown[]
) {
assert(worker, "Worker is not defined");
const responseText = await fetchTimedTextFromWorker(worker);
// We've fixed the auth information so now we can indeed get
// a valid response from the worker
expect(responseText).toBeTruthy();
expect(responseText).toContain("This is a response from Workers AI.");
// And there should be no authentication error emitted
const authErrors = emittedErrors.filter((e) =>
hasMatchingCauseMessage(
e,
/Failed to establish remote session due to an authentication issue/
)
);
expect(authErrors).toHaveLength(0);
}
async function assertAuthErrorForWorkerAiResponse(
expect: ExpectStatic,
emittedErrors: unknown[]
) {
assert(worker, "Worker is not defined");
const responseText = await fetchTimedTextFromWorker(worker);
// The remote connection is not established so we can't successfully
// get a response from the worker
expect(responseText).toBe(null);
// And an authentication error was emitted
const authErrors = emittedErrors.filter((e) =>
hasMatchingCauseMessage(
e,
/Failed to establish remote session due to an authentication issue/
)
);
expect(authErrors.length).toBeGreaterThan(0);
}
/**
* Checks whether an emitted error event (or its cause chain) contains
* a message matching the given pattern.
*
* @param event - the error event to inspect
* @param pattern - the regex pattern to match against error messages
* @returns whether any error in the cause chain matches the pattern
*/
function hasMatchingCauseMessage(event: unknown, pattern: RegExp): boolean {
if (event == null || typeof event !== "object") {
return false;
}
const cause = (event as { cause?: unknown }).cause;
if (cause instanceof Error && pattern.test(cause.message)) {
return true;
}
// Recurse into nested cause
if (cause instanceof Error && cause.cause) {
return hasMatchingCauseMessage({ cause: cause.cause }, pattern);
}
return false;
}
});
describe("without remote bindings (no auth is needed)", () => {
test("starting a worker via startWorker without any remote bindings (doesn't cause wrangler to try to get the auth information)", async ({
expect,
}) => {
const helper = new WranglerE2ETestHelper();
const simpleWorkerScript = dedent`
export default {
async fetch(_request, env) {
return new Response('hello from a simple (local-only) worker');
},
}
`;
await helper.seed({
"src/index.js": simpleWorkerScript,
});
const someAuth = vi.fn(() => {
return {
accountId: "",
apiToken: {
apiToken: "",
},
};
});
worker = await startWorker({
entrypoint: path.resolve(helper.tmpPath, "src/index.js"),
dev: {
auth: someAuth,
server: {
port: 0,
},
},
});
const response = await fetchTimedTextFromWorker(worker);
expect(response).toEqual("hello from a simple (local-only) worker");
expect(someAuth).not.toHaveBeenCalled();
});
});
});
/**
* Tries to fetch some text from a target worker, as part of this it also polls from the worker
* trying multiple times to fetch from it.
*
* @param worker The worker in question
* @returns The text from the worker's response, or null if not response could be obtained.
*/
async function fetchTimedTextFromWorker(
worker: Worker
): Promise<string | null> {
let responseText: string | null = null;
try {
assert(worker, "Worker is not defined");
await waitForLong(
async () => {
responseText = await (
await worker.fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
})
).text();
},
{ timeout: 20_000 }
);
} catch {
return null;
}
return responseText;
}