summaryrefslogtreecommitdiff
path: root/cli/tests/workers
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2021-02-15 14:48:47 +0100
committerGitHub <noreply@github.com>2021-02-15 14:48:47 +0100
commit8c6d147e6ae40a1f92aba1aedc0d95ef437dd4ba (patch)
treefbb59c9a6b294fd242854480067ae50cfb61fba7 /cli/tests/workers
parent1afe6b48e0bfee44f37206eb0fc03ea6ae37ad4d (diff)
chore: Reorganise workers tests (#9493)
Diffstat (limited to 'cli/tests/workers')
-rw-r--r--cli/tests/workers/bench_round_robin.ts65
-rw-r--r--cli/tests/workers/bench_startup.ts33
-rw-r--r--cli/tests/workers/error.ts5
-rw-r--r--cli/tests/workers/post_undefined.ts5
-rw-r--r--cli/tests/workers/test.ts677
-rw-r--r--cli/tests/workers/worker_error.ts5
-rw-r--r--cli/tests/workers/worker_error.ts.out7
-rw-r--r--cli/tests/workers/worker_location.ts6
-rw-r--r--cli/tests/workers/worker_nested_error.ts5
-rw-r--r--cli/tests/workers/worker_nested_error.ts.out7
-rw-r--r--cli/tests/workers/worker_types.ts4
11 files changed, 819 insertions, 0 deletions
diff --git a/cli/tests/workers/bench_round_robin.ts b/cli/tests/workers/bench_round_robin.ts
new file mode 100644
index 000000000..7974760af
--- /dev/null
+++ b/cli/tests/workers/bench_round_robin.ts
@@ -0,0 +1,65 @@
+// Benchmark measures time it takes to send a message to a group of workers one
+// at a time and wait for a response from all of them. Just a general
+// throughput and consistency benchmark.
+const data = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n";
+const workerCount = 4;
+const cmdsPerWorker = 400;
+
+import { Deferred, deferred } from "../../../test_util/std/async/deferred.ts";
+
+function handleAsyncMsgFromWorker(
+ promiseTable: Map<number, Deferred<string>>,
+ msg: { cmdId: number; data: string },
+): void {
+ const promise = promiseTable.get(msg.cmdId);
+ if (promise === null) {
+ throw new Error(`Failed to find promise: cmdId: ${msg.cmdId}, msg: ${msg}`);
+ }
+ promise?.resolve(data);
+}
+
+async function main(): Promise<void> {
+ const workers: Array<[Map<number, Deferred<string>>, Worker]> = [];
+ for (let i = 1; i <= workerCount; ++i) {
+ const worker = new Worker(
+ new URL("bench_worker.ts", import.meta.url).href,
+ { type: "module" },
+ );
+ const promise = deferred();
+ worker.onmessage = (e): void => {
+ if (e.data.cmdId === 0) promise.resolve();
+ };
+ worker.postMessage({ cmdId: 0, action: 2 });
+ await promise;
+ workers.push([new Map(), worker]);
+ }
+ // assign callback function
+ for (const [promiseTable, worker] of workers) {
+ worker.onmessage = (e): void => {
+ handleAsyncMsgFromWorker(promiseTable, e.data);
+ };
+ }
+ for (const cmdId of Array(cmdsPerWorker).keys()) {
+ const promises: Array<Promise<string>> = [];
+ for (const [promiseTable, worker] of workers) {
+ const promise = deferred<string>();
+ promiseTable.set(cmdId, promise);
+ worker.postMessage({ cmdId: cmdId, action: 1, data });
+ promises.push(promise);
+ }
+ for (const promise of promises) {
+ await promise;
+ }
+ }
+ for (const [, worker] of workers) {
+ const promise = deferred();
+ worker.onmessage = (e): void => {
+ if (e.data.cmdId === 3) promise.resolve();
+ };
+ worker.postMessage({ action: 3 });
+ await promise;
+ }
+ console.log("Finished!");
+}
+
+main();
diff --git a/cli/tests/workers/bench_startup.ts b/cli/tests/workers/bench_startup.ts
new file mode 100644
index 000000000..5fbf23b45
--- /dev/null
+++ b/cli/tests/workers/bench_startup.ts
@@ -0,0 +1,33 @@
+// Benchmark measures time it takes to start and stop a number of workers.
+const workerCount = 50;
+
+async function bench(): Promise<void> {
+ const workers: Worker[] = [];
+ for (let i = 1; i <= workerCount; ++i) {
+ const worker = new Worker(
+ new URL("bench_worker.ts", import.meta.url).href,
+ { type: "module" },
+ );
+ const promise = new Promise<void>((resolve): void => {
+ worker.onmessage = (e): void => {
+ if (e.data.cmdId === 0) resolve();
+ };
+ });
+ worker.postMessage({ cmdId: 0, action: 2 });
+ await promise;
+ workers.push(worker);
+ }
+ console.log("Done creating workers closing workers!");
+ for (const worker of workers) {
+ const promise = new Promise<void>((resolve): void => {
+ worker.onmessage = (e): void => {
+ if (e.data.cmdId === 3) resolve();
+ };
+ });
+ worker.postMessage({ action: 3 });
+ await promise;
+ }
+ console.log("Finished!");
+}
+
+bench();
diff --git a/cli/tests/workers/error.ts b/cli/tests/workers/error.ts
new file mode 100644
index 000000000..495971090
--- /dev/null
+++ b/cli/tests/workers/error.ts
@@ -0,0 +1,5 @@
+function foo() {
+ throw new Error("foo");
+}
+
+foo();
diff --git a/cli/tests/workers/post_undefined.ts b/cli/tests/workers/post_undefined.ts
new file mode 100644
index 000000000..1b9b8d6ca
--- /dev/null
+++ b/cli/tests/workers/post_undefined.ts
@@ -0,0 +1,5 @@
+self.onmessage = (ev: MessageEvent) => {
+ console.log("received in worker", ev.data);
+ self.postMessage(undefined);
+ console.log("posted from worker");
+};
diff --git a/cli/tests/workers/test.ts b/cli/tests/workers/test.ts
new file mode 100644
index 000000000..0888e01db
--- /dev/null
+++ b/cli/tests/workers/test.ts
@@ -0,0 +1,677 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// Requires to be run with `--allow-net` flag
+
+import {
+ assert,
+ assertEquals,
+ assertThrows,
+} from "../../../test_util/std/testing/asserts.ts";
+import { deferred } from "../../../test_util/std/async/deferred.ts";
+
+Deno.test({
+ name: "worker terminate",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+
+ const jsWorker = new Worker(
+ new URL("test_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+ const tsWorker = new Worker(
+ new URL("test_worker.ts", import.meta.url).href,
+ { type: "module", name: "tsWorker" },
+ );
+
+ tsWorker.onmessage = (e): void => {
+ assertEquals(e.data, "Hello World");
+ promise.resolve();
+ };
+
+ jsWorker.onmessage = (e): void => {
+ assertEquals(e.data, "Hello World");
+ tsWorker.postMessage("Hello World");
+ };
+
+ jsWorker.onerror = (e: Event): void => {
+ e.preventDefault();
+ jsWorker.postMessage("Hello World");
+ };
+
+ jsWorker.postMessage("Hello World");
+ await promise;
+ tsWorker.terminate();
+ jsWorker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker from data url",
+ async fn() {
+ const promise = deferred();
+ const tsWorker = new Worker(
+ "data:application/typescript;base64,aWYgKHNlbGYubmFtZSAhPT0gInRzV29ya2VyIikgewogIHRocm93IEVycm9yKGBJbnZhbGlkIHdvcmtlciBuYW1lOiAke3NlbGYubmFtZX0sIGV4cGVjdGVkIHRzV29ya2VyYCk7Cn0KCm9ubWVzc2FnZSA9IGZ1bmN0aW9uIChlKTogdm9pZCB7CiAgcG9zdE1lc3NhZ2UoZS5kYXRhKTsKICBjbG9zZSgpOwp9Owo=",
+ { type: "module", name: "tsWorker" },
+ );
+
+ tsWorker.onmessage = (e): void => {
+ assertEquals(e.data, "Hello World");
+ promise.resolve();
+ };
+
+ tsWorker.postMessage("Hello World");
+
+ await promise;
+ tsWorker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker nested",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+
+ const nestedWorker = new Worker(
+ new URL("nested_worker.js", import.meta.url).href,
+ { type: "module", name: "nested" },
+ );
+
+ nestedWorker.onmessage = (e): void => {
+ assert(e.data.type !== "error");
+ promise.resolve();
+ };
+
+ nestedWorker.postMessage("Hello World");
+ await promise;
+ nestedWorker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker throws when executing",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const throwingWorker = new Worker(
+ new URL("throwing_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ // deno-lint-ignore no-explicit-any
+ throwingWorker.onerror = (e: any): void => {
+ e.preventDefault();
+ assert(/Uncaught Error: Thrown error/.test(e.message));
+ promise.resolve();
+ };
+
+ await promise;
+ throwingWorker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker globals",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const workerOptions: WorkerOptions = { type: "module" };
+ const w = new Worker(
+ new URL("worker_globals.ts", import.meta.url).href,
+ workerOptions,
+ );
+ w.onmessage = (e): void => {
+ assertEquals(e.data, "true, true, true");
+ promise.resolve();
+ };
+ w.postMessage("Hello, world!");
+ await promise;
+ w.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker fetch API",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+
+ const fetchingWorker = new Worker(
+ new URL("fetching_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ // deno-lint-ignore no-explicit-any
+ fetchingWorker.onerror = (e: any): void => {
+ e.preventDefault();
+ promise.reject(e.message);
+ };
+
+ // Defer promise.resolve() to allow worker to shut down
+ fetchingWorker.onmessage = (e): void => {
+ assert(e.data === "Done!");
+ promise.resolve();
+ };
+
+ await promise;
+ fetchingWorker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker terminate busy loop",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+
+ const busyWorker = new Worker(
+ new URL("busy_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ let testResult = 0;
+
+ busyWorker.onmessage = (e): void => {
+ testResult = e.data;
+ if (testResult >= 10000) {
+ busyWorker.terminate();
+ busyWorker.onmessage = (_e): void => {
+ throw new Error("unreachable");
+ };
+ setTimeout(() => {
+ assertEquals(testResult, 10000);
+ promise.resolve();
+ }, 100);
+ }
+ };
+
+ busyWorker.postMessage("ping");
+ await promise;
+ },
+});
+
+Deno.test({
+ name: "worker race condition",
+ fn: async function (): Promise<void> {
+ // See issue for details
+ // https://github.com/denoland/deno/issues/4080
+ const promise = deferred();
+
+ const racyWorker = new Worker(
+ new URL("racy_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ racyWorker.onmessage = (e): void => {
+ assertEquals(e.data.buf.length, 999999);
+ racyWorker.onmessage = (_e): void => {
+ throw new Error("unreachable");
+ };
+ setTimeout(() => {
+ promise.resolve();
+ }, 100);
+ };
+
+ await promise;
+ },
+});
+
+Deno.test({
+ name: "worker is event listener",
+ fn: async function (): Promise<void> {
+ let messageHandlersCalled = 0;
+ let errorHandlersCalled = 0;
+
+ const promise1 = deferred();
+ const promise2 = deferred();
+
+ const worker = new Worker(
+ new URL("event_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ worker.onmessage = (_e: Event): void => {
+ messageHandlersCalled++;
+ };
+ worker.addEventListener("message", (_e: Event) => {
+ messageHandlersCalled++;
+ });
+ worker.addEventListener("message", (_e: Event) => {
+ messageHandlersCalled++;
+ promise1.resolve();
+ });
+
+ worker.onerror = (e): void => {
+ errorHandlersCalled++;
+ e.preventDefault();
+ };
+ worker.addEventListener("error", (_e: Event) => {
+ errorHandlersCalled++;
+ });
+ worker.addEventListener("error", (_e: Event) => {
+ errorHandlersCalled++;
+ promise2.resolve();
+ });
+
+ worker.postMessage("ping");
+ await promise1;
+ assertEquals(messageHandlersCalled, 3);
+
+ worker.postMessage("boom");
+ await promise2;
+ assertEquals(errorHandlersCalled, 3);
+ worker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker scope is event listener",
+ fn: async function (): Promise<void> {
+ const promise1 = deferred();
+
+ const worker = new Worker(
+ new URL("event_worker_scope.js", import.meta.url).href,
+ { type: "module" },
+ );
+
+ worker.onmessage = (e: MessageEvent): void => {
+ const { messageHandlersCalled, errorHandlersCalled } = e.data;
+ assertEquals(messageHandlersCalled, 4);
+ assertEquals(errorHandlersCalled, 4);
+ promise1.resolve();
+ };
+
+ worker.onerror = (_e): void => {
+ throw new Error("unreachable");
+ };
+
+ worker.postMessage("boom");
+ worker.postMessage("ping");
+ await promise1;
+ worker.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker with Deno namespace",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const promise2 = deferred();
+
+ const regularWorker = new Worker(
+ new URL("non_deno_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+ const denoWorker = new Worker(
+ new URL("deno_worker.ts", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: "inherit",
+ },
+ },
+ );
+
+ regularWorker.onmessage = (e): void => {
+ assertEquals(e.data, "Hello World");
+ regularWorker.terminate();
+ promise.resolve();
+ };
+
+ denoWorker.onmessage = (e): void => {
+ assertEquals(e.data, "Hello World");
+ denoWorker.terminate();
+ promise2.resolve();
+ };
+
+ regularWorker.postMessage("Hello World");
+ await promise;
+ denoWorker.postMessage("Hello World");
+ await promise2;
+ },
+});
+
+Deno.test({
+ name: "worker with crypto in scope",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const w = new Worker(
+ new URL("worker_crypto.js", import.meta.url).href,
+ { type: "module" },
+ );
+ w.onmessage = (e): void => {
+ assertEquals(e.data, true);
+ promise.resolve();
+ };
+ w.postMessage(null);
+ await promise;
+ w.terminate();
+ },
+});
+
+Deno.test({
+ name: "Worker event handler order",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const w = new Worker(
+ new URL("test_worker.ts", import.meta.url).href,
+ { type: "module", name: "tsWorker" },
+ );
+ const arr: number[] = [];
+ w.addEventListener("message", () => arr.push(1));
+ w.onmessage = (e): void => {
+ arr.push(2);
+ };
+ w.addEventListener("message", () => arr.push(3));
+ w.addEventListener("message", () => {
+ assertEquals(arr, [1, 2, 3]);
+ promise.resolve();
+ });
+ w.postMessage("Hello World");
+ await promise;
+ w.terminate();
+ },
+});
+
+Deno.test({
+ name: "Worker immediate close",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const w = new Worker(
+ new URL("./immediately_close_worker.js", import.meta.url).href,
+ { type: "module" },
+ );
+ setTimeout(() => {
+ promise.resolve();
+ }, 1000);
+ await promise;
+ w.terminate();
+ },
+});
+
+Deno.test({
+ name: "Worker post undefined",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const worker = new Worker(
+ new URL("./post_undefined.ts", import.meta.url).href,
+ { type: "module" },
+ );
+
+ const handleWorkerMessage = (e: MessageEvent): void => {
+ console.log("main <- worker:", e.data);
+ worker.terminate();
+ promise.resolve();
+ };
+
+ worker.addEventListener("messageerror", () => console.log("message error"));
+ worker.addEventListener("error", () => console.log("error"));
+ worker.addEventListener("message", handleWorkerMessage);
+
+ console.log("\npost from parent");
+ worker.postMessage(undefined);
+ await promise;
+ },
+});
+
+Deno.test("Worker inherits permissions", async function () {
+ const promise = deferred();
+ const worker = new Worker(
+ new URL("./read_check_worker.js", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: "inherit",
+ },
+ },
+ );
+
+ worker.onmessage = ({ data: hasPermission }) => {
+ assert(hasPermission);
+ promise.resolve();
+ };
+
+ worker.postMessage(null);
+
+ await promise;
+ worker.terminate();
+});
+
+Deno.test("Worker limit children permissions", async function () {
+ const promise = deferred();
+ const worker = new Worker(
+ new URL("./read_check_worker.js", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: {
+ read: false,
+ },
+ },
+ },
+ );
+
+ worker.onmessage = ({ data: hasPermission }) => {
+ assert(!hasPermission);
+ promise.resolve();
+ };
+
+ worker.postMessage(null);
+
+ await promise;
+ worker.terminate();
+});
+
+Deno.test("Worker limit children permissions granularly", async function () {
+ const promise = deferred();
+ const worker = new Worker(
+ new URL("./read_check_granular_worker.js", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: {
+ read: [
+ new URL("./read_check_worker.js", import.meta.url),
+ ],
+ },
+ },
+ },
+ );
+
+ //Routes are relative to the spawned worker location
+ const routes = [
+ { permission: false, route: "read_check_granular_worker.js" },
+ { permission: true, route: "read_check_worker.js" },
+ ];
+
+ let checked = 0;
+ worker.onmessage = ({ data }) => {
+ checked++;
+ assertEquals(data.hasPermission, routes[data.index].permission);
+ routes.shift();
+ if (checked === routes.length) {
+ promise.resolve();
+ }
+ };
+
+ routes.forEach(({ route }, index) =>
+ worker.postMessage({
+ index,
+ route,
+ })
+ );
+
+ await promise;
+ worker.terminate();
+});
+
+Deno.test("Nested worker limit children permissions", async function () {
+ const promise = deferred();
+
+ /** This worker has read permissions but doesn't grant them to its children */
+ const worker = new Worker(
+ new URL("./parent_read_check_worker.js", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: "inherit",
+ },
+ },
+ );
+
+ worker.onmessage = ({ data }) => {
+ assert(data.parentHasPermission);
+ assert(!data.childHasPermission);
+ promise.resolve();
+ };
+
+ worker.postMessage(null);
+
+ await promise;
+ worker.terminate();
+});
+
+Deno.test("Nested worker limit children permissions granularly", async function () {
+ const promise = deferred();
+
+ /** This worker has read permissions but doesn't grant them to its children */
+ const worker = new Worker(
+ new URL("./parent_read_check_granular_worker.js", import.meta.url)
+ .href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: {
+ read: [
+ new URL("./read_check_granular_worker.js", import.meta.url),
+ ],
+ },
+ },
+ },
+ );
+
+ //Routes are relative to the spawned worker location
+ const routes = [
+ {
+ childHasPermission: false,
+ parentHasPermission: true,
+ route: "read_check_granular_worker.js",
+ },
+ {
+ childHasPermission: false,
+ parentHasPermission: false,
+ route: "read_check_worker.js",
+ },
+ ];
+
+ let checked = 0;
+ worker.onmessage = ({ data }) => {
+ checked++;
+ assertEquals(
+ data.childHasPermission,
+ routes[data.index].childHasPermission,
+ );
+ assertEquals(
+ data.parentHasPermission,
+ routes[data.index].parentHasPermission,
+ );
+ if (checked === routes.length) {
+ promise.resolve();
+ }
+ };
+
+ // Index needed cause requests will be handled asynchronously
+ routes.forEach(({ route }, index) =>
+ worker.postMessage({
+ index,
+ route,
+ })
+ );
+
+ await promise;
+ worker.terminate();
+});
+
+// This test relies on env permissions not being granted on main thread
+Deno.test("Worker initialization throws on worker permissions greater than parent thread permissions", function () {
+ assertThrows(
+ () => {
+ const worker = new Worker(
+ new URL("./deno_worker.ts", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: {
+ env: true,
+ },
+ },
+ },
+ );
+ worker.terminate();
+ },
+ Deno.errors.PermissionDenied,
+ "Can't escalate parent thread permissions",
+ );
+});
+
+Deno.test("Worker with disabled permissions", async function () {
+ const promise = deferred();
+
+ const worker = new Worker(
+ new URL("./no_permissions_worker.js", import.meta.url).href,
+ {
+ type: "module",
+ deno: {
+ namespace: true,
+ permissions: "none",
+ },
+ },
+ );
+
+ worker.onmessage = ({ data: sandboxed }) => {
+ assert(sandboxed);
+ promise.resolve();
+ };
+
+ worker.postMessage(null);
+ await promise;
+ worker.terminate();
+});
+
+Deno.test({
+ name: "worker location",
+ fn: async function (): Promise<void> {
+ const promise = deferred();
+ const workerModuleHref =
+ new URL("worker_location.ts", import.meta.url).href;
+ const w = new Worker(workerModuleHref, { type: "module" });
+ w.onmessage = (e): void => {
+ assertEquals(e.data, `${workerModuleHref}, true`);
+ promise.resolve();
+ };
+ w.postMessage("Hello, world!");
+ await promise;
+ w.terminate();
+ },
+});
+
+Deno.test({
+ name: "worker with relative specifier",
+ fn: async function (): Promise<void> {
+ assertEquals(location.href, "http://127.0.0.1:4545/cli/tests/");
+ const promise = deferred();
+ const w = new Worker(
+ "./workers/test_worker.ts",
+ { type: "module", name: "tsWorker" },
+ );
+ w.onmessage = (e): void => {
+ assertEquals(e.data, "Hello, world!");
+ promise.resolve();
+ };
+ w.postMessage("Hello, world!");
+ await promise;
+ w.terminate();
+ },
+});
diff --git a/cli/tests/workers/worker_error.ts b/cli/tests/workers/worker_error.ts
new file mode 100644
index 000000000..696680de8
--- /dev/null
+++ b/cli/tests/workers/worker_error.ts
@@ -0,0 +1,5 @@
+const worker = new Worker(
+ new URL("error.ts", import.meta.url).href,
+ { type: "module", name: "bar" },
+);
+setTimeout(() => worker.terminate(), 30000);
diff --git a/cli/tests/workers/worker_error.ts.out b/cli/tests/workers/worker_error.ts.out
new file mode 100644
index 000000000..e46478979
--- /dev/null
+++ b/cli/tests/workers/worker_error.ts.out
@@ -0,0 +1,7 @@
+[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
+ at foo ([WILDCARD])
+ at [WILDCARD]
+error: Uncaught (in promise) Error: Unhandled error event reached main worker.
+ throw new Error("Unhandled error event reached main worker.");
+ ^
+ at Worker.#poll ([WILDCARD])
diff --git a/cli/tests/workers/worker_location.ts b/cli/tests/workers/worker_location.ts
new file mode 100644
index 000000000..89fa83036
--- /dev/null
+++ b/cli/tests/workers/worker_location.ts
@@ -0,0 +1,6 @@
+onmessage = function (): void {
+ postMessage(
+ `${location.href}, ${location instanceof WorkerLocation}`,
+ );
+ close();
+};
diff --git a/cli/tests/workers/worker_nested_error.ts b/cli/tests/workers/worker_nested_error.ts
new file mode 100644
index 000000000..aba2011be
--- /dev/null
+++ b/cli/tests/workers/worker_nested_error.ts
@@ -0,0 +1,5 @@
+const worker = new Worker(
+ new URL("worker_error.ts", import.meta.url).href,
+ { type: "module", name: "baz" },
+);
+setTimeout(() => worker.terminate(), 30000);
diff --git a/cli/tests/workers/worker_nested_error.ts.out b/cli/tests/workers/worker_nested_error.ts.out
new file mode 100644
index 000000000..e46478979
--- /dev/null
+++ b/cli/tests/workers/worker_nested_error.ts.out
@@ -0,0 +1,7 @@
+[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
+ at foo ([WILDCARD])
+ at [WILDCARD]
+error: Uncaught (in promise) Error: Unhandled error event reached main worker.
+ throw new Error("Unhandled error event reached main worker.");
+ ^
+ at Worker.#poll ([WILDCARD])
diff --git a/cli/tests/workers/worker_types.ts b/cli/tests/workers/worker_types.ts
new file mode 100644
index 000000000..b67a3b782
--- /dev/null
+++ b/cli/tests/workers/worker_types.ts
@@ -0,0 +1,4 @@
+// deno-lint-ignore require-await
+self.onmessage = async (_msg: MessageEvent) => {
+ self.postMessage("hello");
+};