Skip to content

Commit

Permalink
WfP Assets support (#7448)
Browse files Browse the repository at this point in the history
* WfP Assets support

* Add e2e test for WfP & Assets
  • Loading branch information
GregBrimble authored Dec 6, 2024
1 parent 21a9e24 commit 20a0f17
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 83 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-plants-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

feat: Allow Workers for Platforms scripts (scripts deployed with `--dispatch-namespace`) to bring along `assets`
203 changes: 142 additions & 61 deletions packages/wrangler/e2e/deployments.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from "node:assert";
import dedent from "ts-dedent";
import { fetch } from "undici";
import { afterAll, describe, expect, it, vi } from "vitest";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import { CLOUDFLARE_ACCOUNT_ID } from "./helpers/account-id";
import { WranglerE2ETestHelper } from "./helpers/e2e-wrangler-test";
import { generateResourceName } from "./helpers/generate-resource-name";
Expand All @@ -14,6 +14,8 @@ const normalize = (str: string) =>
[CLOUDFLARE_ACCOUNT_ID]: "CLOUDFLARE_ACCOUNT_ID",
}).replaceAll(/^Author:(\s+).+@.+$/gm, "Author:$1person@example.com");
const workerName = generateResourceName();
const dispatchNamespaceName = generateResourceName("dispatch");
const dispatchWorkerName = generateResourceName();

describe("deployments", { timeout: TIMEOUT }, () => {
let deployedUrl: string;
Expand Down Expand Up @@ -250,11 +252,118 @@ const checkAssets = async (testCases: AssetTestCase[], deployedUrl: string) => {
}
};

describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
let deployedUrl: string;
describe.each([
{
name: "regular Worker",
flags: "",
async beforeAll() {},
async afterAll(helper: WranglerE2ETestHelper) {
await helper.run(`wrangler delete`);
},
expectInitialStdout: (output: string) => {
expect(output).toEqual(`🌀 Building list of assets...
🌀 Starting asset upload...
🌀 Found 3 new or modified static assets to upload. Proceeding with upload...
+ /404.html
+ /index.html
+ /[boop].html
Uploaded 1 of 3 assets
Uploaded 2 of 3 assets
Uploaded 3 of 3 assets
✨ Success! Uploaded 3 files (TIMINGS)
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000`);
},
expectSubsequentStdout: (output: string) => {
expect(output).toEqual(`🌀 Building list of assets...
🌀 Starting asset upload...
No files to upload. Proceeding with deployment...
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000`);
},
},
{
name: "Workers for Platforms",
flags: `--dispatch-namespace ${dispatchNamespaceName}`,
url: "",
async beforeAll(helper: WranglerE2ETestHelper) {
await helper.seed({
"dispatch-worker/wrangler.toml": dedent`
name = "${dispatchWorkerName}"
main = "./src/index.js"
compatibility_date = "2023-01-01"
[[dispatch_namespaces]]
binding = "DISPATCH"
namespace = "${dispatchNamespaceName}"
`,
"dispatch-worker/src/index.js": dedent`
export default {
async fetch(request, env, ctx) {
const stub = env.DISPATCH.get("${workerName}");
return stub.fetch(request);
}
}
`,
});
await helper.run(
`wrangler dispatch-namespace create ${dispatchNamespaceName}`
);
const { stdout } = await helper.run(
`wrangler deploy -c dispatch-worker/wrangler.toml`
);
const match = stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
this.url = match.groups.url;
},
async afterAll(helper: WranglerE2ETestHelper) {
await helper.run(`wrangler delete -c dispatch-worker/wrangler.toml`);
await helper.run(
`wrangler dispatch-namespace delete ${dispatchNamespaceName}`
);
},
expectInitialStdout: (output: string) => {
expect(output).toEqual(`🌀 Building list of assets...
🌀 Starting asset upload...
🌀 Found 3 new or modified static assets to upload. Proceeding with upload...
+ /404.html
+ /index.html
+ /[boop].html
Uploaded 1 of 3 assets
Uploaded 2 of 3 assets
Uploaded 3 of 3 assets
✨ Success! Uploaded 3 files (TIMINGS)
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Dispatch Namespace: tmp-e2e-dispatch-00000000-0000-0000-0000-000000000000
Current Version ID: 00000000-0000-0000-0000-000000000000`);
},
expectSubsequentStdout: (output: string) => {
expect(output).toEqual(`🌀 Building list of assets...
🌀 Starting asset upload...
No files to upload. Proceeding with deployment...
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Dispatch Namespace: tmp-e2e-dispatch-00000000-0000-0000-0000-000000000000
Current Version ID: 00000000-0000-0000-0000-000000000000`);
},
},
])("Workers + Assets deployment: $name", { timeout: TIMEOUT }, (testcase) => {
let deployedUrl: string | undefined;
const helper = new WranglerE2ETestHelper();
beforeAll(async () => {
await testcase.beforeAll(helper);
});
afterAll(async () => {
await helper.run(`wrangler delete`);
await testcase.afterAll(helper);
});
it("deploys a Workers + Assets project with assets only", async () => {
await helper.seed({
Expand All @@ -267,29 +376,17 @@ describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
...initialAssets,
});

const output = await helper.run(`wrangler deploy`);
expect(normalize(output.stdout)).toMatchInlineSnapshot(`
"🌀 Building list of assets...
🌀 Starting asset upload...
🌀 Found 3 new or modified static assets to upload. Proceeding with upload...
+ /404.html
+ /index.html
+ /[boop].html
Uploaded 1 of 3 assets
Uploaded 2 of 3 assets
Uploaded 3 of 3 assets
✨ Success! Uploaded 3 files (TIMINGS)
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000"
`);
const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
const output = await helper.run(`wrangler deploy ${testcase.flags}`);
testcase.expectInitialStdout(normalize(output.stdout));
if (testcase.url) {
deployedUrl = testcase.url;
} else {
const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
}

const testCases: AssetTestCase[] = [
// Tests html_handling = "auto_trailing_slash" (default):
Expand Down Expand Up @@ -349,23 +446,16 @@ describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
}`,
...initialAssets,
});
const output = await helper.run(`wrangler deploy`);
const output = await helper.run(`wrangler deploy ${testcase.flags}`);
// expect only no asset files to be uploaded as no new asset files have been added
expect(normalize(output.stdout)).toMatchInlineSnapshot(`
"🌀 Building list of assets...
🌀 Starting asset upload...
No files to upload. Proceeding with deployment...
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000"
`);
const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
testcase.expectSubsequentStdout(normalize(output.stdout));
if (!deployedUrl) {
const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
}

const testCases: AssetTestCase[] = [
// because html handling has now been set to "none", only exact matches will be served
Expand Down Expand Up @@ -400,7 +490,6 @@ describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
);
expect(text).toContain("<h1>404.html</h1>");
});

it("runs user worker ahead of matching assets when serve_directly = false", async () => {
await helper.seed({
"wrangler.toml": dedent`
Expand All @@ -423,24 +512,16 @@ describe("Workers + Assets deployment", { timeout: TIMEOUT }, () => {
...initialAssets,
});

const output = await helper.run(`wrangler deploy`);
const output = await helper.run(`wrangler deploy ${testcase.flags}`);
// expect only no asset files to be uploaded as no new asset files have been added
expect(normalize(output.stdout)).toMatchInlineSnapshot(`
"🌀 Building list of assets...
🌀 Starting asset upload...
No files to upload. Proceeding with deployment...
Total Upload: xx KiB / gzip: xx KiB
Uploaded tmp-e2e-worker-00000000-0000-0000-0000-000000000000 (TIMINGS)
Deployed tmp-e2e-worker-00000000-0000-0000-0000-000000000000 triggers (TIMINGS)
https://tmp-e2e-worker-00000000-0000-0000-0000-000000000000.SUBDOMAIN.workers.dev
Current Version ID: 00000000-0000-0000-0000-000000000000"
`);

const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
testcase.expectSubsequentStdout(normalize(output.stdout));
if (!deployedUrl) {
const match = output.stdout.match(
/(?<url>https:\/\/tmp-e2e-.+?\..+?\.workers\.dev)/
);
assert(match?.groups);
deployedUrl = match.groups.url;
}

const testCases: AssetTestCase[] = [
{
Expand Down
14 changes: 14 additions & 0 deletions packages/wrangler/e2e/helpers/e2e-wrangler-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,20 @@ export class WranglerE2ETestHelper {
return id;
}

async dispatchNamespace(isLocal: boolean) {
const name = generateResourceName("dispatch");
if (isLocal) {
throw new Error(
"Dispatch namespaces are not supported in local mode (yet)"
);
}
await this.run(`wrangler dispatch-namespace create ${name}`);
onTestFinished(async () => {
await this.run(`wrangler dispatch-namespace delete ${name}`);
});
return name;
}

async r2(isLocal: boolean) {
const name = generateResourceName("r2");
if (isLocal) {
Expand Down
87 changes: 69 additions & 18 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5140,6 +5140,36 @@ addEventListener('fetch', event => {});`
});
await runWrangler("deploy");
});

it("should be able to upload to a WfP script", async () => {
const assets = [
{ filePath: "file-1.txt", content: "Content of file-1" },
{ filePath: "boop/file-2.txt", content: "Content of file-2" },
];
writeAssets(assets);
writeWorkerSource({ format: "js" });
writeWranglerConfig({
compatibility_date: "2024-09-27",
compatibility_flags: ["nodejs_compat"],
assets: {
directory: "assets",
html_handling: "none",
},
});
await mockAUSRequest(undefined, undefined, undefined, "my-namespace");
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedAssets: {
jwt: "<<aus-completion-token>>",
config: { html_handling: "none" },
},
expectedCompatibilityDate: "2024-09-27",
expectedCompatibilityFlags: ["nodejs_compat"],
expectedMainModule: undefined,
expectedDispatchNamespace: "my-namespace",
});
await runWrangler("deploy --dispatch-namespace my-namespace");
});
});

describe("workers_dev setting", () => {
Expand Down Expand Up @@ -12542,25 +12572,46 @@ function mockPostQueueHTTPConsumer(
const mockAUSRequest = async (
bodies?: AssetManifest[],
buckets: string[][] = [[]],
jwt: string = "<<aus-completion-token>>"
jwt: string = "<<aus-completion-token>>",
dispatchNamespace?: string
) => {
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies?.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt, buckets },
},
{ status: 201 }
);
}
)
);
if (dispatchNamespace) {
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/dispatch/namespaces/my-namespace/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies?.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt, buckets },
},
{ status: 201 }
);
}
)
);
} else {
msw.use(
http.post<never, AssetManifest>(
`*/accounts/some-account-id/workers/scripts/test-name/assets-upload-session`,
async ({ request }) => {
bodies?.push(await request.json());
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt, buckets },
},
{ status: 201 }
);
}
)
);
}
};

const mockAssetUploadRequest = async (
Expand Down
Loading

0 comments on commit 20a0f17

Please sign in to comment.