Skip to content
File

Blob: src/workerd/io/worker-getexportedhandler-test.c++

3.9 KB
1// Copyright (c) 2024 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5// Tests for Worker::Lock::getExportedHandler() error behaviour, specifically
6// the isDynamicDispatch path which surfaces user-configuration mistakes as JSG
7// TypeErrors rather than internal log-only errors.
8 
9#include <workerd/api/global-scope.h>
10#include <workerd/io/frankenvalue.h>
11#include <workerd/io/worker.h>
12#include <workerd/tests/test-fixture.h>
13 
14#include <kj/test.h>
15 
16namespace workerd {
17namespace {
18 
19// ---------------------------------------------------------------------------
20// isDynamicDispatch = true: both error cases must throw a JSG TypeError.
21// ---------------------------------------------------------------------------
22 
23KJ_TEST("getExportedHandler: DO class via dynamic dispatch throws JSG TypeError") {
24 TestFixture fixture({
25 .mainModuleSource = R"(
26 import { DurableObject } from "cloudflare:workers";
27 export class SomeActor extends DurableObject {}
28 export default { async fetch(req) { return new Response("ok"); } }
29 )"_kj,
30 });
31 
32 fixture.runInIoContext([&](const TestFixture::Environment& env) {
33 KJ_EXPECT_THROW_MESSAGE(
34 "jsg.TypeError: The entrypoint name SomeActor refers to a Durable Object class, but the "
35 "incoming request is trying to invoke it as a stateless worker.",
36 env.lock.getExportedHandler("SomeActor"_kj, kj::none, Frankenvalue{}, kj::none, true));
37 });
38}
39 
40KJ_TEST("getExportedHandler: missing entrypoint via dynamic dispatch throws JSG TypeError") {
41 TestFixture fixture({
42 .mainModuleSource = R"(
43 export default { async fetch(req) { return new Response("ok"); } }
44 )"_kj,
45 });
46 
47 fixture.runInIoContext([&](const TestFixture::Environment& env) {
48 KJ_EXPECT_THROW_MESSAGE(
49 "jsg.TypeError: The entrypoint name nonExistent was not found in this worker. Ensure the "
50 "worker exports an entrypoint with that name.",
51 env.lock.getExportedHandler("nonExistent"_kj, kj::none, Frankenvalue{}, kj::none, true));
52 });
53}
54 
55// ---------------------------------------------------------------------------
56// isDynamicDispatch = false: both error cases must NOT throw a JSG TypeError
57// (they log and then throw a non-JSG internal error via KJ_FAIL_ASSERT).
58// ---------------------------------------------------------------------------
59 
60KJ_TEST("getExportedHandler: DO class via static dispatch throws internal error") {
61 TestFixture fixture({
62 .mainModuleSource = R"(
63 import { DurableObject } from "cloudflare:workers";
64 export class SomeActor extends DurableObject {}
65 export default { async fetch(req) { return new Response("ok"); } }
66 )"_kj,
67 });
68 
69 fixture.runInIoContext([&](const TestFixture::Environment& env) {
70 // LOG_ERROR_PERIODICALLY fires, then KJ_FAIL_ASSERT throws.
71 // No JSG TypeError — the error is treated as internal.
72 KJ_EXPECT_LOG(ERROR, "worker is not an actor but class name was requested; n = SomeActor");
73 KJ_EXPECT_THROW_MESSAGE("worker_do_not_log; Unable to get exported handler",
74 env.lock.getExportedHandler("SomeActor"_kj, kj::none, Frankenvalue{}, kj::none, false));
75 });
76}
77 
78KJ_TEST("getExportedHandler: missing entrypoint via static dispatch throws internal error") {
79 TestFixture fixture({
80 .mainModuleSource = R"(
81 export default { async fetch(req) { return new Response("ok"); } }
82 )"_kj,
83 });
84 
85 fixture.runInIoContext([&](const TestFixture::Environment& env) {
86 // LOG_ERROR_PERIODICALLY fires, then KJ_FAIL_ASSERT throws.
87 // No JSG TypeError — the error is treated as internal.
88 KJ_EXPECT_LOG(ERROR, "worker has no such named entrypoint; n = nonExistent");
89 KJ_EXPECT_THROW_MESSAGE("worker_do_not_log; Unable to get exported handler",
90 env.lock.getExportedHandler("nonExistent"_kj, kj::none, Frankenvalue{}, kj::none, false));
91 });
92}
93 
94} // namespace
95} // namespace workerd