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
385 lines
12 KiB
TypeScript
385 lines
12 KiB
TypeScript
import path from "path";
|
||
import { D1Database, R2Bucket } from "@cloudflare/workers-types";
|
||
import { toMatchImageSnapshot } from "jest-image-snapshot";
|
||
import { beforeEach, describe, it, MockInstance, vi } from "vitest";
|
||
import { getPlatformProxy } from "./shared";
|
||
import type {
|
||
Fetcher,
|
||
Hyperdrive,
|
||
ImagesBinding,
|
||
KVNamespace,
|
||
Workflow,
|
||
} from "@cloudflare/workers-types";
|
||
|
||
type Env = {
|
||
MY_VAR: string;
|
||
MY_VAR_A: string;
|
||
MY_JSON_VAR: Object;
|
||
MY_DEV_VAR: string;
|
||
MY_KV: KVNamespace;
|
||
MY_KV_PROD: KVNamespace;
|
||
MY_BUCKET: R2Bucket;
|
||
MY_D1: D1Database;
|
||
MY_HYPERDRIVE: Hyperdrive;
|
||
ASSETS: Fetcher;
|
||
IMAGES: ImagesBinding;
|
||
MY_WORKFLOW_INTERNAL: Workflow;
|
||
MY_WORKFLOW_EXTERNAL: Workflow;
|
||
};
|
||
|
||
const wranglerConfigFilePath = path.join(__dirname, "..", "wrangler.jsonc");
|
||
|
||
describe("getPlatformProxy - env", () => {
|
||
let warn = {} as MockInstance<typeof console.warn>;
|
||
|
||
beforeEach(() => {
|
||
// Hide stdout messages from the test logs
|
||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||
vi.spyOn(console, "error").mockImplementation(() => {});
|
||
warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||
warn.mockClear();
|
||
});
|
||
|
||
describe("var bindings", () => {
|
||
it("correctly obtains var bindings from both wrangler config and .dev.vars", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_VAR, MY_JSON_VAR, MY_DEV_VAR } = env;
|
||
expect(MY_VAR).toEqual("my-var-value");
|
||
expect(MY_JSON_VAR).toEqual({
|
||
test: true,
|
||
});
|
||
expect(MY_DEV_VAR).toEqual("my-dev-var-value");
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly makes vars from .dev.vars override the ones in wrangler config", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_VAR_A } = env;
|
||
expect(MY_VAR_A).not.toEqual("my-var-a"); // if this fails, the value was read from wrangler config – not .dev.vars
|
||
expect(MY_VAR_A).toEqual("my-dev-var-a");
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly makes vars from .dev.vars not override bindings of the same name from wrangler config", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_KV } = env;
|
||
expect(MY_KV).not.toEqual("my-dev-kv");
|
||
["get", "delete", "list", "put", "getWithMetadata"].every(
|
||
(methodName) =>
|
||
expect(
|
||
typeof (MY_KV as unknown as Record<string, unknown>)[methodName]
|
||
).toBe("function")
|
||
);
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly reads a toml from a custom path alongside with its .dev.vars", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: path.join(
|
||
__dirname,
|
||
"..",
|
||
"custom-toml",
|
||
"path",
|
||
"test.toml"
|
||
),
|
||
});
|
||
try {
|
||
const { MY_VAR, MY_JSON_VAR, MY_DEV_VAR } = env;
|
||
expect(MY_VAR).toEqual("my-var-value-from-a-custom-toml");
|
||
expect(MY_JSON_VAR).toEqual({
|
||
test: true,
|
||
customToml: true,
|
||
});
|
||
expect(MY_DEV_VAR).toEqual("my-dev-var-value-from-a-custom-location");
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
});
|
||
|
||
it("correctly reads a json config file", async ({ expect }) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: path.join(__dirname, "..", "wrangler.json"),
|
||
});
|
||
try {
|
||
const { MY_VAR, MY_JSON_VAR } = env;
|
||
expect(MY_VAR).toEqual("my-var-value-from-a-json-config-file");
|
||
expect(MY_JSON_VAR).toEqual({
|
||
test: true,
|
||
fromJson: true,
|
||
});
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly obtains functioning ASSETS bindings", async ({ expect }) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
const res = await env.ASSETS.fetch("https://0.0.0.0/test.txt");
|
||
const text = await res.text();
|
||
expect(text).toEqual("this is a test text file!\n");
|
||
await dispose();
|
||
});
|
||
|
||
it("correctly obtains functioning KV bindings", async ({ expect }) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
const { MY_KV } = env;
|
||
let numOfKeys = (await MY_KV.list()).keys.length;
|
||
expect(numOfKeys).toBe(0);
|
||
await MY_KV.put("my-key", "my-value");
|
||
numOfKeys = (await MY_KV.list()).keys.length;
|
||
expect(numOfKeys).toBe(1);
|
||
const value = await MY_KV.get("my-key");
|
||
expect(value).toBe("my-value");
|
||
await dispose();
|
||
});
|
||
|
||
it("correctly obtains functioning R2 bindings", async ({ expect }) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_BUCKET } = env;
|
||
let numOfObjects = (await MY_BUCKET.list()).objects.length;
|
||
expect(numOfObjects).toBe(0);
|
||
await MY_BUCKET.put("my-object", "my-value");
|
||
numOfObjects = (await MY_BUCKET.list()).objects.length;
|
||
expect(numOfObjects).toBe(1);
|
||
const value = await MY_BUCKET.get("my-object");
|
||
expect(await value?.text()).toBe("my-value");
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly obtains functioning D1 bindings", async ({ expect }) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_D1 } = env;
|
||
await MY_D1.exec(
|
||
`CREATE TABLE IF NOT EXISTS users ( id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL )`
|
||
);
|
||
const stmt = MY_D1.prepare("insert into users (name) values (?1)");
|
||
await MY_D1.batch([
|
||
stmt.bind("userA"),
|
||
stmt.bind("userB"),
|
||
stmt.bind("userC"),
|
||
]);
|
||
const { results } = await MY_D1.prepare(
|
||
"SELECT name FROM users LIMIT 5"
|
||
).all();
|
||
expect(results).toEqual([
|
||
{ name: "userA" },
|
||
{ name: "userB" },
|
||
{ name: "userC" },
|
||
]);
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("correctly obtains functioning Image bindings", async ({ expect }) => {
|
||
expect.extend({ toMatchImageSnapshot });
|
||
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { IMAGES } = env;
|
||
const streams = (
|
||
await fetch("https://playground.devprod.cloudflare.dev/flares.png")
|
||
).body!.tee();
|
||
|
||
// @ts-expect-error The stream types aren't matching up properly?
|
||
expect(await IMAGES.info(streams[0])).toMatchInlineSnapshot(`
|
||
{
|
||
"fileSize": 96549,
|
||
"format": "image/png",
|
||
"height": 1145,
|
||
"width": 2048,
|
||
}
|
||
`);
|
||
|
||
// @ts-expect-error The stream types aren't matching up properly?
|
||
const response = await env.IMAGES.input(streams[1])
|
||
.transform({ rotate: 90 })
|
||
.transform({ width: 128, height: 100 })
|
||
.transform({ blur: 20 })
|
||
.output({ format: "image/png" });
|
||
|
||
expect(
|
||
Buffer.from(await response.response().arrayBuffer())
|
||
).toMatchImageSnapshot();
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
// Important: the hyperdrive values are passthrough ones since the workerd specific hyperdrive values only make sense inside
|
||
// workerd itself and would simply not work in a node.js process
|
||
it("correctly obtains passthrough Hyperdrive bindings", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
const { MY_HYPERDRIVE } = env;
|
||
expect(MY_HYPERDRIVE.connectionString).toEqual(
|
||
"postgres://user:pass@127.0.0.1:1234/db"
|
||
);
|
||
expect(MY_HYPERDRIVE.database).toEqual("db");
|
||
expect(MY_HYPERDRIVE.host).toEqual("127.0.0.1");
|
||
expect(MY_HYPERDRIVE.user).toEqual("user");
|
||
expect(MY_HYPERDRIVE.password).toEqual("pass");
|
||
expect(MY_HYPERDRIVE.port).toEqual(1234);
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
describe("DO warnings", () => {
|
||
it("warns about internal DOs and doesn't crash", async ({ expect }) => {
|
||
await getPlatformProxy<Env>({
|
||
configPath: path.join(__dirname, "..", "wrangler_internal_do.jsonc"),
|
||
});
|
||
expect(warn.mock.calls[0][0].replaceAll(/[\r\n]+/g, "\n"))
|
||
.toMatchInlineSnapshot(`
|
||
"[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m You have defined bindings to the following internal Durable Objects:[0m
|
||
- {"class_name":"MyDurableObject","name":"MY_DURABLE_OBJECT"}
|
||
These will not work in local development, but they should work in production.
|
||
|
||
If you want to develop these locally, you can define your DO in a separate Worker, with a separate configuration file.
|
||
For detailed instructions, refer to the Durable Objects section here: [4mhttps://developers.cloudflare.com/workers/wrangler/api#supported-bindings[0m
|
||
"
|
||
`);
|
||
});
|
||
|
||
it("doesn't warn about external DOs and doesn't crash", async ({
|
||
expect,
|
||
}) => {
|
||
await getPlatformProxy<Env>({
|
||
configPath: path.join(__dirname, "..", "wrangler_external_do.jsonc"),
|
||
});
|
||
expect(warn).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it("warns only about internal Workflows, not cross-script ones, and doesn't crash", async ({
|
||
expect,
|
||
}) => {
|
||
await getPlatformProxy<Env>({
|
||
configPath: path.join(__dirname, "..", "wrangler_workflow.jsonc"),
|
||
});
|
||
// Only MY_WORKFLOW_INTERNAL (no script_name) is unsupported in
|
||
// getPlatformProxy(); MY_WORKFLOW_EXTERNAL (script_name set) is
|
||
// passed through to miniflare, which routes it via the dev-registry
|
||
// proxy to the named worker.
|
||
expect(warn).toHaveBeenCalledTimes(1);
|
||
expect(warn.mock.calls[0][0].replaceAll(/[\r\n]+/g, "\n"))
|
||
.toMatchInlineSnapshot(`
|
||
"[33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1mYou have defined bindings to the following Workflows without a script_name:[0m
|
||
- {"binding":"MY_WORKFLOW_INTERNAL","name":"my-workflow-internal","class_name":"MyWorkflowInternal"}
|
||
These are not available in local development, so you will not be able to bind to them when testing locally, but they should work in production.
|
||
"
|
||
`);
|
||
});
|
||
});
|
||
|
||
describe("with a target environment", () => {
|
||
it("should provide bindings targeting a specified environment and also inherit top-level ones", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
environment: "production",
|
||
});
|
||
try {
|
||
expect(env.MY_VAR).not.toBe("my-var-value");
|
||
expect(env.MY_VAR).toBe("my-PRODUCTION-var-value");
|
||
expect(env.MY_JSON_VAR).toEqual({ test: true, production: true });
|
||
|
||
expect(env.MY_KV).toBeTruthy();
|
||
expect(env.MY_KV_PROD).toBeTruthy();
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("should not provide bindings targeting an environment when none was specified", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
});
|
||
try {
|
||
expect(env.MY_VAR).not.toBe("my-PRODUCTION-var-value");
|
||
expect(env.MY_VAR).toBe("my-var-value");
|
||
expect(env.MY_JSON_VAR).toEqual({ test: true });
|
||
|
||
expect(env.MY_KV).toBeTruthy();
|
||
expect(env.MY_KV_PROD).toBeFalsy();
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("should provide secrets targeting a specified environment", async ({
|
||
expect,
|
||
}) => {
|
||
const { env, dispose } = await getPlatformProxy<Env>({
|
||
configPath: wranglerConfigFilePath,
|
||
environment: "production",
|
||
});
|
||
try {
|
||
const { MY_DEV_VAR } = env;
|
||
expect(MY_DEV_VAR).not.toEqual("my-dev-var-value");
|
||
expect(MY_DEV_VAR).toEqual("my-PRODUCTION-dev-var-value");
|
||
} finally {
|
||
await dispose();
|
||
}
|
||
});
|
||
|
||
it("should error if a non-existent environment is provided", async ({
|
||
expect,
|
||
}) => {
|
||
await expect(
|
||
getPlatformProxy({
|
||
configPath: wranglerConfigFilePath,
|
||
environment: "non-existent-environment",
|
||
})
|
||
).rejects.toThrow(
|
||
/No environment found in configuration with name "non-existent-environment"/
|
||
);
|
||
});
|
||
});
|
||
});
|