Compare commits

...

2 Commits

Author SHA1 Message Date
Ashcon Partovi
90d6a3f7bd Experimental vendor regression tests 2025-04-11 18:22:59 -07:00
Ashcon Partovi
a2df5195e0 Remove vendored express tests 2025-04-11 18:22:01 -07:00
22 changed files with 780 additions and 4590 deletions

View File

@@ -7,7 +7,7 @@
// - It cannot use Bun APIs, since it is run using Node.js.
// - It does not import dependencies, so it's faster to start.
import { spawn, spawnSync } from "node:child_process";
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import {
accessSync,
@@ -59,6 +59,7 @@ const spawnTimeout = 5_000;
const testTimeout = 3 * 60_000;
const integrationTimeout = 5 * 60_000;
const napiTimeout = 10 * 60_000;
const vendorTimeout = 10_000;
function getNodeParallelTestTimeout(testPath) {
if (testPath.includes("test-dns")) {
@@ -137,6 +138,10 @@ const { values: options, positionals: filters } = parseArgs({
type: "boolean",
default: isBuildkite,
},
["update-regressions"]: {
type: "boolean",
default: false,
},
},
});
@@ -200,8 +205,14 @@ async function runTests() {
*/
const runTest = async (title, fn) => {
const index = ++i;
const isVendor = title.startsWith("vendor/");
let result, failure, flaky;
/** @type {TestResult} */
let result;
/** @type {TestResult | undefined} */
let failure;
/** @type {boolean} */
let flaky;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
if (attempt > 1) {
await new Promise(resolve => setTimeout(resolve, 5000 + Math.random() * 10_000));
@@ -239,25 +250,95 @@ async function runTests() {
}
}
if (!failure) {
/** @type {TestEntry[]} */
const regressions = [];
/** @type {TestEntry[]} */
const regressionsFixed = [];
if (isVendor) {
const { tests, error, testPath } = result;
if (error && !error.endsWith("failing")) {
tests.push({ file: testPath, test: error, status: "fail" });
}
const errors = tests.filter(({ status }) => status === "fail").map(({ file, test }) => `${file} > ${test}`);
if (errors.length) {
const knownErrors =
vendorTests?.flatMap(({ testFailures }) => testFailures)?.filter(error => error.startsWith(testPath)) || [];
const regressedErrors = tests.filter(
({ status, file, test }) => status === "fail" && !knownErrors.includes(`${file} > ${test}`),
);
for (const error of regressedErrors) {
regressions.push(error);
}
const fixedErrors = knownErrors.filter(error => !errors.includes(error));
for (const error of fixedErrors) {
const [file, ...labels] = error.split(" > ");
regressionsFixed.push({
file,
test: labels.join(" > "),
status: "pass",
});
}
}
if (options["update-regressions"]) {
const testFailures = regressions.map(({ file, test }) => `${file} > ${test}`);
const vendorPath = join(cwd, "test", "vendor.json");
/** @type {Vendor[]} */
const vendorJson = JSON.parse(readFileSync(vendorPath, "utf-8"));
const [, vendorName] = title.split("/");
const vendorPackage = vendorJson.find(({ package: name }) => name === vendorName);
if (vendorPackage) {
const existingTestFailures = vendorPackage.testFailures || [];
const updatedTestFailures = Array.from(
new Set([...existingTestFailures.filter(error => !error.startsWith(title)), ...testFailures]),
).sort();
const vendorIndex = vendorJson.findIndex(({ package: name }) => name === vendorName);
const vendorUpdatedPackage = {
...vendorPackage,
testFailures: updatedTestFailures?.length ? updatedTestFailures : undefined,
};
if (vendorIndex === -1) {
vendorJson.push(vendorUpdatedPackage);
} else {
vendorJson[vendorIndex] = vendorUpdatedPackage;
}
writeFileSync(vendorPath, JSON.stringify(vendorJson, null, 2));
}
}
}
if (isBuildkite && regressionsFixed.length) {
const content = formatTestToMarkdown(
{ ...result, tests: regressionsFixed, testPath: title },
{ includeOk: true },
);
reportAnnotationToBuildKite({ context: "regressions", label: title, content, style: "info" });
}
if (!failure || (isVendor && !regressions.length)) {
return result;
}
if (isBuildkite) {
// Group flaky tests together, regardless of the title
const context = flaky ? "flaky" : title;
const style = flaky || title.startsWith("vendor") ? "warning" : "error";
const context = flaky && !isVendor ? "flaky" : title;
const style = flaky || isVendor ? "warning" : "error";
if (title.startsWith("vendor")) {
const content = formatTestToMarkdown({ ...failure, testPath: title });
if (content) {
reportAnnotationToBuildKite({ context, label: title, content, style });
let content;
if (isVendor) {
if (regressions.length) {
content = formatTestToMarkdown({ ...failure, tests: regressions, testPath: title });
} else {
content = formatTestToMarkdown({ ...failure, testPath: title });
}
} else {
const content = formatTestToMarkdown(failure);
if (content) {
reportAnnotationToBuildKite({ context, label: title, content, style });
}
content = formatTestToMarkdown(failure);
}
if (content) {
reportAnnotationToBuildKite({ context, label: title, content, style });
}
}
@@ -267,7 +348,7 @@ async function runTests() {
const longMarkdown = formatTestToMarkdown(failure);
appendFileSync(summaryPath, longMarkdown);
}
const shortMarkdown = formatTestToMarkdown(failure, true);
const shortMarkdown = formatTestToMarkdown(failure, { concise: true });
appendFileSync("comment.md", shortMarkdown);
}
@@ -556,7 +637,7 @@ async function spawnSafe(options) {
};
await new Promise(resolve => {
try {
subprocess = spawn(command, args, {
subprocess = nodeSpawn(command, args, {
stdio: ["ignore", "pipe", "pipe"],
timeout,
cwd,
@@ -667,6 +748,19 @@ async function spawnSafe(options) {
};
}
/**
* @param {SpawnOptions} options
* @returns {Promise<SpawnResult>}
*/
async function spawn(options) {
const result = await spawnSafe(options);
const { error, stdout } = result;
if (error) {
throw new Error(`Command failed: ${error}`, { cause: stdout });
}
return result;
}
/**
* @param {string} execPath Path to bun binary
* @param {SpawnOptions} options
@@ -769,10 +863,11 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
* @returns {Promise<TestResult>}
*/
async function spawnBunTest(execPath, testPath, options = { cwd }) {
const timeout = getTestTimeout(testPath);
const isVendor = options.cwd?.includes("vendor");
const timeout = isVendor ? vendorTimeout : getTestTimeout(testPath);
const perTestTimeout = Math.ceil(timeout / 2);
const absPath = join(options["cwd"], testPath);
const isReallyTest = isTestStrict(testPath) || absPath.includes("vendor");
const isReallyTest = isTestStrict(testPath) || isVendor;
const args = options["args"] ?? [];
const testArgs = ["test", ...args, `--timeout=${perTestTimeout}`];
@@ -1072,9 +1167,6 @@ function isHidden(path) {
return /node_modules|node.js/.test(dirname(path)) || /^\./.test(basename(path));
}
/** Files with these extensions are not treated as test cases */
const IGNORED_EXTENSIONS = new Set([".md"]);
/**
* @param {string} cwd
* @returns {string[]}
@@ -1084,9 +1176,8 @@ function getTests(cwd) {
const dirname = join(cwd, path);
for (const entry of readdirSync(dirname, { encoding: "utf-8", withFileTypes: true })) {
const { name } = entry;
const ext = name.slice(name.lastIndexOf("."));
const filename = join(path, name);
if (isHidden(filename) || IGNORED_EXTENSIONS.has(ext)) {
if (isHidden(filename)) {
continue;
}
if (entry.isFile() && isTest(filename)) {
@@ -1105,10 +1196,9 @@ function getTests(cwd) {
* @property {string} repository
* @property {string} tag
* @property {string} [packageManager]
* @property {string} [testPath]
* @property {string} [testRunner]
* @property {string[]} [testExtensions]
* @property {boolean | Record<string, boolean | string>} [skipTests]
* @property {string[]} [testPatterns]
* @property {string[]} [testFailures]
*/
/**
@@ -1117,6 +1207,7 @@ function getTests(cwd) {
* @property {string} packageManager
* @property {string} testRunner
* @property {string[]} testPaths
* @property {string[]} testFailures
*/
/**
@@ -1151,73 +1242,83 @@ async function getVendorTests(cwd) {
return Promise.all(
relevantVendors.map(
async ({ package: name, repository, tag, testPath, testExtensions, testRunner, packageManager, skipTests }) => {
async ({ package: name, repository, tag, testPatterns, testFailures, testRunner, packageManager }) => {
const vendorPath = join(cwd, "vendor", name);
if (!existsSync(vendorPath)) {
await spawnSafe({
await spawn({
command: "git",
args: ["clone", "--depth", "1", "--single-branch", repository, vendorPath],
timeout: testTimeout,
cwd,
});
}
await spawnSafe({
command: "git",
args: ["fetch", "--depth", "1", "origin", "tag", tag],
timeout: testTimeout,
cwd: vendorPath,
});
await spawn({
command: "git",
args: ["fetch", "--depth", "1", "origin", "tag", tag],
timeout: testTimeout,
cwd: vendorPath,
});
}
const packageJsonPath = join(vendorPath, "package.json");
if (!existsSync(packageJsonPath)) {
throw new Error(`Vendor '${name}' does not have a package.json: ${packageJsonPath}`);
}
const testPathPrefix = testPath || "test";
const testParentPath = join(vendorPath, testPathPrefix);
if (!existsSync(testParentPath)) {
throw new Error(`Vendor '${name}' does not have a test directory: ${testParentPath}`);
}
const isTest = path => {
if (!isJavaScriptTest(path)) {
return false;
}
if (typeof skipTests === "boolean") {
return !skipTests;
}
if (typeof skipTests === "object") {
for (const [glob, reason] of Object.entries(skipTests)) {
const pattern = new RegExp(`^${glob.replace(/\*/g, ".*")}$`);
if (pattern.test(path) && reason) {
return false;
}
const testParentPaths = new Set();
if (testPatterns?.length) {
for (const pattern of testPatterns) {
const endOfPath = pattern.lastIndexOf("/");
const endOfGlob = pattern.lastIndexOf("*");
if (endOfPath === -1 || endOfGlob < endOfPath) {
continue;
}
const testPath = pattern.substring(0, endOfPath);
if (existsSync(join(vendorPath, testPath))) {
testParentPaths.add(testPath);
}
}
} else if (existsSync(join(vendorPath, "test"))) {
testParentPaths.add("test");
}
return true;
if (!testParentPaths.size) {
throw new Error(
`Could not find test directory for vendor '${name}' (hint: set 'testPatterns' in vendor.json)`,
);
}
const isMatch = (filename, glob) => {
const pattern = new RegExp(`^${glob.replace(/\*/g, ".*")}$`);
return pattern.test(filename);
};
const testPaths = readdirSync(testParentPath, { encoding: "utf-8", recursive: true })
.filter(filename =>
testExtensions ? testExtensions.some(ext => filename.endsWith(`.${ext}`)) : isTest(filename),
)
.map(filename => join(testPathPrefix, filename))
.filter(
filename =>
!filters?.length ||
filters.some(filter => join(vendorPath, filename).replace(/\\/g, "/").includes(filter)),
);
const testPaths = Array.from(testParentPaths).flatMap(testParentPath =>
readdirSync(join(vendorPath, testParentPath), { encoding: "utf-8", recursive: true })
.filter(filename =>
testPatterns?.length
? testPatterns.some(pattern => isMatch(join(testParentPath, filename).replace(/\\/g, "/"), pattern))
: isJavaScriptTest(filename),
)
.map(filename => join(testParentPath, filename))
.filter(
filename =>
!filters?.length ||
filters.some(filter => join(vendorPath, filename).replace(/\\/g, "/").includes(filter)),
),
);
if (!testPaths.length && !filters?.length) {
throw new Error(`Could not find test files for vendor '${name}' (hint: set 'testPatterns' in vendor.json)`);
}
return {
cwd: vendorPath,
packageManager: packageManager || "bun",
testRunner: testRunner || "bun",
testPaths,
testFailures: testFailures || [],
};
},
),
@@ -1319,7 +1420,7 @@ function getExecPath(bunExe) {
let execPath;
let error;
try {
const { error, stdout } = spawnSync(bunExe, ["--print", "process.argv[0]"], {
const { error, stdout } = nodeSpawnSync(bunExe, ["--print", "process.argv[0]"], {
encoding: "utf-8",
timeout: spawnTimeout,
env: {
@@ -1405,7 +1506,7 @@ async function getExecPathFromBuildKite(target, buildId) {
*/
function getRevision(execPath) {
try {
const { error, stdout } = spawnSync(execPath, ["--revision"], {
const { error, stdout } = nodeSpawnSync(execPath, ["--revision"], {
encoding: "utf-8",
timeout: spawnTimeout,
env: {
@@ -1443,10 +1544,13 @@ function getTestLabel() {
/**
* @param {TestResult | TestResult[]} result
* @param {boolean} concise
* @param {object} options
* @param {boolean} options.concise
* @param {boolean} options.includeOk
* @returns {string}
*/
function formatTestToMarkdown(result, concise) {
function formatTestToMarkdown(result, options = {}) {
const { concise = false, includeOk = false } = options;
const results = Array.isArray(result) ? result : [result];
const buildLabel = getTestLabel();
const buildUrl = getBuildUrl();
@@ -1454,7 +1558,7 @@ function formatTestToMarkdown(result, concise) {
let markdown = "";
for (const { testPath, ok, tests, error, stdoutPreview: stdout } of results) {
if (ok || error === "SIGTERM") {
if ((ok && !includeOk) || error === "SIGTERM") {
continue;
}
@@ -1486,6 +1590,8 @@ function formatTestToMarkdown(result, concise) {
}
if (error) {
markdown += ` - ${error}`;
} else if (ok) {
markdown += ` - ok`;
}
if (platform) {
markdown += ` on ${platform}`;
@@ -1513,7 +1619,7 @@ function formatTestToMarkdown(result, concise) {
* @param {string} glob
*/
function uploadArtifactsToBuildKite(glob) {
spawn("buildkite-agent", ["artifact", "upload", glob], {
nodeSpawnSync("buildkite-agent", ["artifact", "upload", glob], {
stdio: ["ignore", "ignore", "ignore"],
timeout: spawnTimeout,
cwd,
@@ -1538,7 +1644,7 @@ function listArtifactsFromBuildKite(glob, step) {
if (step) {
args.push("--step", step);
}
const { error, status, signal, stdout, stderr } = spawnSync("buildkite-agent", args, {
const { error, status, signal, stdout, stderr } = nodeSpawnSync("buildkite-agent", args, {
stdio: ["ignore", "ignore", "ignore"],
encoding: "utf-8",
timeout: spawnTimeout,
@@ -2006,10 +2112,13 @@ export async function main() {
// It also appears to hang on 1.1.1.1, which could explain this issue:
// https://github.com/oven-sh/bun/issues/11136
if (isWindows && isCI) {
await spawn("pwsh", [
"-Command",
"Set-DnsClientServerAddress -InterfaceAlias 'Ethernet 4' -ServerAddresses ('8.8.8.8','8.8.4.4')",
]);
await spawn({
command: "pwsh",
args: [
"-Command",
"Set-DnsClientServerAddress -InterfaceAlias 'Ethernet 4' -ServerAddresses ('8.8.8.8','8.8.4.4')",
],
});
}
const results = await runTests();

View File

@@ -1 +0,0 @@
Test cases ported from [express](https://github.com/expressjs/express). MIT license.

File diff suppressed because it is too large Load Diff

View File

@@ -1,745 +0,0 @@
"use strict";
var assert = require("node:assert");
var AsyncLocalStorage = require("node:async_hooks").AsyncLocalStorage;
var express = require("express");
var request = require("supertest");
describe("express.json()", function () {
let app;
it("should parse JSON", function (done) {
request(createApp())
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it("should handle Content-Length: 0", function (done) {
request(createApp())
.post("/")
.set("Content-Type", "application/json")
.set("Content-Length", "0")
.expect(200, "{}", done);
});
it.todo("should handle empty message-body", function (done) {
request(createApp())
.post("/")
.set("Content-Type", "application/json")
.set("Transfer-Encoding", "chunked")
.expect(200, "{}", done);
});
it("should handle no message-body", function (done) {
request(createApp())
.post("/")
.set("Content-Type", "application/json")
.unset("Transfer-Encoding")
.expect(200, "{}", done);
});
// The old node error message modification in body parser is catching this
it("should 400 when only whitespace", function (done) {
request(createApp())
.post("/")
.set("Content-Type", "application/json")
.send(" \n")
.expect(400, "[entity.parse.failed] " + parseError(" \n"), done);
});
it("should 400 when invalid content-length", function (done) {
var app = express();
app.use(function (req, res, next) {
req.headers["content-length"] = "20"; // bad length
next();
});
app.use(express.json());
app.post("/", function (req, res) {
res.json(req.body);
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"str":')
.expect(400, /content length/, done);
});
it("should handle duplicated middleware", function (done) {
var app = express();
app.use(express.json());
app.use(express.json());
app.post("/", function (req, res) {
res.json(req.body);
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
describe("when JSON is invalid", function () {
let app;
beforeAll(function () {
app = createApp();
});
it("should 400 for bad token", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send("{:")
.expect(400, "[entity.parse.failed] " + parseError("{:"), done);
});
it("should 400 for incomplete", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user"')
.expect(400, "[entity.parse.failed] " + parseError('{"user"'), done);
});
it("should include original body on error object", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.set("X-Error-Property", "body")
.send(' {"user"')
.expect(400, ' {"user"', done);
});
});
describe("with limit option", function () {
it("should 413 when over limit with Content-Length", function (done) {
var buf = Buffer.alloc(1024, ".");
request(createApp({ limit: "1kb" }))
.post("/")
.set("Content-Type", "application/json")
.set("Content-Length", "1034")
.send(JSON.stringify({ str: buf.toString() }))
.expect(413, "[entity.too.large] request entity too large", done);
});
it("should 413 when over limit with chunked encoding", function (done) {
var app = createApp({ limit: "1kb" });
var buf = Buffer.alloc(1024, ".");
var test = request(app).post("/");
test.set("Content-Type", "application/json");
test.set("Transfer-Encoding", "chunked");
test.write('{"str":');
test.write('"' + buf.toString() + '"}');
test.expect(413, done);
});
it("should 413 when inflated body over limit", function (done) {
var app = createApp({ limit: "1kb" });
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a040000", "hex"));
test.expect(413, done);
});
it("should accept number of bytes", function (done) {
var buf = Buffer.alloc(1024, ".");
request(createApp({ limit: 1024 }))
.post("/")
.set("Content-Type", "application/json")
.send(JSON.stringify({ str: buf.toString() }))
.expect(413, done);
});
it("should not change when options altered", function (done) {
var buf = Buffer.alloc(1024, ".");
var options = { limit: "1kb" };
var app = createApp(options);
options.limit = "100kb";
request(app)
.post("/")
.set("Content-Type", "application/json")
.send(JSON.stringify({ str: buf.toString() }))
.expect(413, done);
});
it("should not hang response", function (done) {
var buf = Buffer.alloc(10240, ".");
var app = createApp({ limit: "8kb" });
var test = request(app).post("/");
test.set("Content-Type", "application/json");
test.write(buf);
test.write(buf);
test.write(buf);
test.expect(413, done);
});
it("should not error when inflating", function (done) {
var app = createApp({ limit: "1kb" });
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000aab562a2e2952b252d21b05a360148c58a0540b0066f7ce1e0a0400", "hex"));
test.expect(413, done);
});
});
describe("with inflate option", function () {
describe("when false", function () {
beforeAll(function () {
app = createApp({ inflate: false });
});
it.todo("should not accept content-encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(415, "[encoding.unsupported] content encoding unsupported", done);
});
});
describe("when true", function () {
beforeAll(function () {
app = createApp({ inflate: true });
});
it("should accept content-encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(200, '{"name":"论"}', done);
});
});
});
describe("with strict option", function () {
describe("when undefined", function () {
beforeAll(function () {
app = createApp();
});
it("should 400 on primitives", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send("true")
.expect(400, "[entity.parse.failed] " + parseError("#rue").replace(/#/g, "t"), done);
});
});
describe("when false", function () {
beforeAll(function () {
app = createApp({ strict: false });
});
it.todo("should parse primitives", function (done) {
request(app).post("/").set("Content-Type", "application/json").send("true").expect(200, "true", done);
});
});
describe("when true", function () {
beforeAll(function () {
app = createApp({ strict: true });
});
it("should not parse primitives", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send("true")
.expect(400, "[entity.parse.failed] " + parseError("#rue").replace(/#/g, "t"), done);
});
it("should not parse primitives with leading whitespaces", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send(" true")
.expect(400, "[entity.parse.failed] " + parseError(" #rue").replace(/#/g, "t"), done);
});
it("should allow leading whitespaces in JSON", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send(' { "user": "tobi" }')
.expect(200, '{"user":"tobi"}', done);
});
it("should include correct message in stack trace", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.set("X-Error-Property", "stack")
.send("true")
.expect(400)
.expect(shouldContainInBody(parseError("#rue").replace(/#/g, "t")))
.end(done);
});
});
});
describe("with type option", function () {
describe.skip('when "application/vnd.api+json"', function () {
beforeAll(function () {
app = createApp({ type: "application/vnd.api+json" });
});
it("should parse JSON for custom type", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/vnd.api+json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it("should ignore standard type", function (done) {
request(app).post("/").set("Content-Type", "application/json").send('{"user":"tobi"}').expect(200, "", done);
});
});
describe('when ["application/json", "application/vnd.api+json"]', function () {
beforeAll(function () {
app = createApp({
type: ["application/json", "application/vnd.api+json"],
});
});
it('should parse JSON for "application/json"', function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it.todo('should parse JSON for "application/vnd.api+json"', function (done) {
request(app)
.post("/")
.set("Content-Type", "application/vnd.api+json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it.todo('should ignore "application/x-json"', function (done) {
request(app).post("/").set("Content-Type", "application/x-json").send('{"user":"tobi"}').expect(200, "", done);
});
});
describe("when a function", function () {
it("should parse when truthy value returned", function (done) {
var app = createApp({ type: accept });
function accept(req) {
return req.headers["content-type"] === "application/vnd.api+json";
}
request(app)
.post("/")
.set("Content-Type", "application/vnd.api+json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it("should work without content-type", function (done) {
var app = createApp({ type: accept });
function accept(req) {
return true;
}
var test = request(app).post("/");
test.write('{"user":"tobi"}');
test.expect(200, '{"user":"tobi"}', done);
});
it("should not invoke without a body", function (done) {
var app = createApp({ type: accept });
function accept(req) {
throw new Error("oops!");
}
request(app).get("/").expect(404, done);
});
});
});
describe("with verify option", function () {
it("should assert value if function", function () {
assert.throws(createApp.bind(null, { verify: "lol" }), /TypeError: option verify must be function/);
});
it("should error from verify", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error("no arrays");
},
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('["tobi"]')
.expect(403, "[entity.verify.failed] no arrays", done);
});
it("should allow custom codes", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return;
var err = new Error("no arrays");
err.status = 400;
throw err;
},
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('["tobi"]')
.expect(400, "[entity.verify.failed] no arrays", done);
});
it("should allow custom type", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x5b) return;
var err = new Error("no arrays");
err.type = "foo.bar";
throw err;
},
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('["tobi"]')
.expect(403, "[foo.bar] no arrays", done);
});
it("should include original body on error object", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error("no arrays");
},
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.set("X-Error-Property", "body")
.send('["tobi"]')
.expect(403, '["tobi"]', done);
});
it("should allow pass-through", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error("no arrays");
},
});
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"tobi"}')
.expect(200, '{"user":"tobi"}', done);
});
it("should work with different charsets", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x5b) throw new Error("no arrays");
},
});
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=utf-16");
test.write(Buffer.from("feff007b0022006e0061006d00650022003a00228bba0022007d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should 415 on unknown charset prior to verify", function (done) {
var app = createApp({
verify: function (req, res, buf) {
throw new Error("unexpected verify call");
},
});
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=x-bogus");
test.write(Buffer.from("00000000", "hex"));
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done);
});
});
describe.todo("async local storage", function () {
beforeAll(function () {
var app = express();
var store = { foo: "bar" };
app.use(function (req, res, next) {
req.asyncLocalStorage = new AsyncLocalStorage();
req.asyncLocalStorage.run(store, next);
});
app.use(express.json());
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore();
if (local) {
res.setHeader("x-store-foo", String(local.foo));
}
next();
});
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore();
if (local) {
res.setHeader("x-store-foo", String(local.foo));
}
res.status(err.status || 500);
res.send("[" + err.type + "] " + err.message);
});
app.post("/", function (req, res) {
res.json(req.body);
});
app = app;
});
it("should presist store", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"tobi"}')
.expect(200)
.expect("x-store-foo", "bar")
.expect('{"user":"tobi"}')
.end(done);
});
it("should persist store when unmatched content-type", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/fizzbuzz")
.send("buzz")
.expect(200)
.expect("x-store-foo", "bar")
.expect("")
.end(done);
});
it("should presist store when inflated", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(200);
test.expect("x-store-foo", "bar");
test.expect('{"name":"论"}');
test.end(done);
});
it("should presist store when inflate error", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(400);
test.expect("x-store-foo", "bar");
test.end(done);
});
it("should presist store when parse error", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":')
.expect(400)
.expect("x-store-foo", "bar")
.end(done);
});
it("should presist store when limit exceeded", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/json")
.send('{"user":"' + Buffer.alloc(1024 * 100, ".").toString() + '"}')
.expect(413)
.expect("x-store-foo", "bar")
.end(done);
});
});
describe("charset", function () {
beforeAll(function () {
app = createApp();
});
it("should parse utf-8", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=utf-8");
test.write(Buffer.from("7b226e616d65223a22e8aeba227d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should parse utf-16", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=utf-16");
test.write(Buffer.from("feff007b0022006e0061006d00650022003a00228bba0022007d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should parse when content-length != char length", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=utf-8");
test.set("Content-Length", "13");
test.write(Buffer.from("7b2274657374223a22c3a5227d", "hex"));
test.expect(200, '{"test":"å"}', done);
});
it("should default to utf-8", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json");
test.write(Buffer.from("7b226e616d65223a22e8aeba227d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should fail on unknown charset", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json; charset=koi8-r");
test.write(Buffer.from("7b226e616d65223a22cec5d4227d", "hex"));
test.expect(415, '[charset.unsupported] unsupported charset "KOI8-R"', done);
});
});
describe("encoding", function () {
let app;
beforeAll(function () {
app = createApp({ limit: "1kb" });
});
it("should parse without encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "application/json");
test.write(Buffer.from("7b226e616d65223a22e8aeba227d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should support identity encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "identity");
test.set("Content-Type", "application/json");
test.write(Buffer.from("7b226e616d65223a22e8aeba227d", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should support gzip encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should support deflate encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "deflate");
test.set("Content-Type", "application/json");
test.write(Buffer.from("789cab56ca4bcc4d55b2527ab16e97522d00274505ac", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should be case-insensitive", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "GZIP");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56ca4bcc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(200, '{"name":"论"}', done);
});
it("should 415 on unknown encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "nulls");
test.set("Content-Type", "application/json");
test.write(Buffer.from("000000000000", "hex"));
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done);
});
it("should 400 on malformed encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bab56cc4d55b2527ab16e97522d00515be1cc0e000000", "hex"));
test.expect(400, done);
});
it("should 413 when inflated value exceeds limit", function (done) {
// gzip'd data exceeds 1kb, but deflated below 1kb
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "application/json");
test.write(Buffer.from("1f8b080000000000000bedc1010d000000c2a0f74f6d0f071400000000000000", "hex"));
test.write(Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex"));
test.write(Buffer.from("0000000000000000004f0625b3b71650c30000", "hex"));
test.expect(413, done);
});
});
});
function createApp(options?) {
var app = express();
app.use(express.json(options));
app.use(function (err, req, res, next) {
// console.log(err)
res.status(err.status || 500);
res.send(
String(
req.headers["x-error-property"] ? err[req.headers["x-error-property"]] : "[" + err.type + "] " + err.message,
),
);
});
app.post("/", function (req, res) {
res.json(req.body);
});
return app;
}
function parseError(str) {
try {
JSON.parse(str);
throw new SyntaxError("strict violation");
} catch (e) {
return e.message;
}
}
function shouldContainInBody(str) {
return function (res) {
assert.ok(res.text.indexOf(str) !== -1, "expected '" + res.text + "' to contain '" + str + "'");
};
}

View File

@@ -1,44 +0,0 @@
// @ts-nocheck
// can't use @types/express or @types/body-parser because they
// depend on @types/node which conflicts with bun-types
import { expect, test } from "bun:test";
import express from "express";
import { isIPv6 } from "node:net";
// https://github.com/oven-sh/bun/issues/8926
test("should respond with 404 when wrong method is used", async () => {
const { promise: serve, resolve } = Promise.withResolvers();
const app = express();
app.use(express.json());
app.get("/api/hotels", (req, res) => {
res.json({
success: true,
});
});
const server = app.listen(0, (_, host, port) => {
if (isIPv6(host)) {
resolve(`http://[${host}]:${port}`);
} else {
resolve(`http://${host}:${port}`);
}
});
try {
const url = await serve;
const response = await fetch(`${url}/api/hotels`, {
method: "POST",
signal: AbortSignal.timeout(500),
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Hotel 1",
price: 100,
}),
});
expect(response.status).toBe(404);
} finally {
server.close();
}
});

View File

@@ -1,531 +0,0 @@
"use strict";
var assert = require("node:assert");
var AsyncLocalStorage = require("node:async_hooks").AsyncLocalStorage;
var express = require("express");
var request = require("supertest");
describe("express.text()", function () {
let app;
beforeAll(function () {
app = createApp();
});
it("should parse text/plain", function (done) {
request(app).post("/").set("Content-Type", "text/plain").send("user is tobi").expect(200, '"user is tobi"', done);
});
it("should 400 when invalid content-length", function (done) {
var app = express();
app.use(function (req, res, next) {
req.headers["content-length"] = "20"; // bad length
next();
});
app.use(express.text());
app.post("/", function (req, res) {
res.json(req.body);
});
request(app)
.post("/")
.set("Content-Type", "text/plain")
.send("user")
.expect(400, /content length/, done);
});
it("should handle Content-Length: 0", function (done) {
request(createApp({ limit: "1kb" }))
.post("/")
.set("Content-Type", "text/plain")
.set("Content-Length", "0")
.expect(200, '""', done);
});
it.todo("should handle empty message-body", function (done) {
request(createApp({ limit: "1kb" }))
.post("/")
.set("Content-Type", "text/plain")
.set("Transfer-Encoding", "chunked")
.send("")
.expect(200, '""', done);
});
it("should handle duplicated middleware", function (done) {
var app = express();
app.use(express.text());
app.use(express.text());
app.post("/", function (req, res) {
res.json(req.body);
});
request(app).post("/").set("Content-Type", "text/plain").send("user is tobi").expect(200, '"user is tobi"', done);
});
describe("with defaultCharset option", function () {
it("should change default charset", function (done) {
var server = createApp({ defaultCharset: "koi8-r" });
var test = request(server).post("/");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("6e616d6520697320cec5d4", "hex"));
test.expect(200, '"name is нет"', done);
});
it("should honor content-type charset", function (done) {
var server = createApp({ defaultCharset: "koi8-r" });
var test = request(server).post("/");
test.set("Content-Type", "text/plain; charset=utf-8");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
});
describe("with limit option", function () {
it("should 413 when over limit with Content-Length", function (done) {
var buf = Buffer.alloc(1028, ".");
request(createApp({ limit: "1kb" }))
.post("/")
.set("Content-Type", "text/plain")
.set("Content-Length", "1028")
.send(buf.toString())
.expect(413, done);
});
it.todo("should 413 when over limit with chunked encoding", function (done) {
var app = createApp({ limit: "1kb" });
var buf = Buffer.alloc(1028, ".");
var test = request(app).post("/");
test.set("Content-Type", "text/plain");
test.set("Transfer-Encoding", "chunked");
test.write(buf.toString());
test.expect(413, done);
});
it("should 413 when inflated body over limit", function (done) {
var app = createApp({ limit: "1kb" });
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000ad3d31b05a360148c64000087e5a14704040000", "hex"));
test.expect(413, done);
});
it("should accept number of bytes", function (done) {
var buf = Buffer.alloc(1028, ".");
request(createApp({ limit: 1024 }))
.post("/")
.set("Content-Type", "text/plain")
.send(buf.toString())
.expect(413, done);
});
it("should not change when options altered", function (done) {
var buf = Buffer.alloc(1028, ".");
var options = { limit: "1kb" };
var app = createApp(options);
options.limit = "100kb";
request(app).post("/").set("Content-Type", "text/plain").send(buf.toString()).expect(413, done);
});
it("should not hang response", function (done) {
var app = createApp({ limit: "8kb" });
var buf = Buffer.alloc(10240, ".");
var test = request(app).post("/");
test.set("Content-Type", "text/plain");
test.write(buf);
test.write(buf);
test.write(buf);
test.expect(413, done);
});
it("should not error when inflating", function (done) {
var app = createApp({ limit: "1kb" });
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000ad3d31b05a360148c64000087e5a1470404", "hex"));
setTimeout(function () {
test.expect(413, done);
}, 100);
});
});
describe("with inflate option", function () {
describe.todo("when false", function () {
beforeAll(function () {
app = createApp({ inflate: false });
});
it("should not accept content-encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000", "hex"));
test.expect(415, "[encoding.unsupported] content encoding unsupported", done);
});
});
describe("when true", function () {
beforeAll(function () {
app = createApp({ inflate: true });
});
it("should accept content-encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000", "hex"));
test.expect(200, '"name is 论"', done);
});
});
});
describe("with type option", function () {
describe.todo('when "text/html"', function () {
beforeAll(function () {
app = createApp({ type: "text/html" });
});
it("should parse for custom type", function (done) {
request(app).post("/").set("Content-Type", "text/html").send("<b>tobi</b>").expect(200, '"<b>tobi</b>"', done);
});
it("should ignore standard type", function (done) {
request(app).post("/").set("Content-Type", "text/plain").send("user is tobi").expect(200, "", done);
});
});
describe('when ["text/html", "text/plain"]', function () {
beforeAll(function () {
app = createApp({ type: ["text/html", "text/plain"] });
});
it.todo('should parse "text/html"', function (done) {
request(app).post("/").set("Content-Type", "text/html").send("<b>tobi</b>").expect(200, '"<b>tobi</b>"', done);
});
it('should parse "text/plain"', function (done) {
request(app).post("/").set("Content-Type", "text/plain").send("tobi").expect(200, '"tobi"', done);
});
it.todo('should ignore "text/xml"', function (done) {
request(app).post("/").set("Content-Type", "text/xml").send("<user>tobi</user>").expect(200, "", done);
});
});
describe("when a function", function () {
it("should parse when truthy value returned", function (done) {
var app = createApp({ type: accept });
function accept(req) {
return req.headers["content-type"] === "text/vnd.something";
}
request(app)
.post("/")
.set("Content-Type", "text/vnd.something")
.send("user is tobi")
.expect(200, '"user is tobi"', done);
});
it("should work without content-type", function (done) {
var app = createApp({ type: accept });
function accept(req) {
return true;
}
var test = request(app).post("/");
test.write("user is tobi");
test.expect(200, '"user is tobi"', done);
});
it("should not invoke without a body", function (done) {
var app = createApp({ type: accept });
function accept(req) {
throw new Error("oops!");
}
request(app).get("/").expect(404, done);
});
});
});
describe("with verify option", function () {
it("should assert value is function", function () {
assert.throws(createApp.bind(null, { verify: "lol" }), /TypeError: option verify must be function/);
});
it("should error from verify", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error("no leading space");
},
});
request(app)
.post("/")
.set("Content-Type", "text/plain")
.send(" user is tobi")
.expect(403, "[entity.verify.failed] no leading space", done);
});
it("should allow custom codes", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] !== 0x20) return;
var err = new Error("no leading space");
err.status = 400;
throw err;
},
});
request(app)
.post("/")
.set("Content-Type", "text/plain")
.send(" user is tobi")
.expect(400, "[entity.verify.failed] no leading space", done);
});
it("should allow pass-through", function (done) {
var app = createApp({
verify: function (req, res, buf) {
if (buf[0] === 0x20) throw new Error("no leading space");
},
});
request(app).post("/").set("Content-Type", "text/plain").send("user is tobi").expect(200, '"user is tobi"', done);
});
it("should 415 on unknown charset prior to verify", function (done) {
var app = createApp({
verify: function (req, res, buf) {
throw new Error("unexpected verify call");
},
});
var test = request(app).post("/");
test.set("Content-Type", "text/plain; charset=x-bogus");
test.write(Buffer.from("00000000", "hex"));
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done);
});
});
describe.todo("async local storage", function () {
beforeAll(function () {
var app = express();
var store = { foo: "bar" };
app.use(function (req, res, next) {
req.asyncLocalStorage = new AsyncLocalStorage();
req.asyncLocalStorage.run(store, next);
});
app.use(express.text());
app.use(function (req, res, next) {
var local = req.asyncLocalStorage.getStore();
if (local) {
res.setHeader("x-store-foo", String(local.foo));
}
next();
});
app.use(function (err, req, res, next) {
var local = req.asyncLocalStorage.getStore();
if (local) {
res.setHeader("x-store-foo", String(local.foo));
}
res.status(err.status || 500);
res.send("[" + err.type + "] " + err.message);
});
app.post("/", function (req, res) {
res.json(req.body);
});
app = app;
});
it("should presist store", function (done) {
request(app)
.post("/")
.set("Content-Type", "text/plain")
.send("user is tobi")
.expect(200)
.expect("x-store-foo", "bar")
.expect('"user is tobi"')
.end(done);
});
it("should presist store when unmatched content-type", function (done) {
request(app)
.post("/")
.set("Content-Type", "application/fizzbuzz")
.send("buzz")
.expect(200)
.expect("x-store-foo", "bar")
.end(done);
});
it("should presist store when inflated", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000", "hex"));
test.expect(200);
test.expect("x-store-foo", "bar");
test.expect('"name is 论"');
test.end(done);
});
it("should presist store when inflate error", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b0000", "hex"));
test.expect(400);
test.expect("x-store-foo", "bar");
test.end(done);
});
it("should presist store when limit exceeded", function (done) {
request(app)
.post("/")
.set("Content-Type", "text/plain")
.send("user is " + Buffer.alloc(1024 * 100, ".").toString())
.expect(413)
.expect("x-store-foo", "bar")
.end(done);
});
});
describe("charset", function () {
beforeAll(function () {
app = createApp();
});
it("should parse utf-8", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain; charset=utf-8");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should parse codepage charsets", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain; charset=koi8-r");
test.write(Buffer.from("6e616d6520697320cec5d4", "hex"));
test.expect(200, '"name is нет"', done);
});
it("should parse when content-length != char length", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain; charset=utf-8");
test.set("Content-Length", "11");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should default to utf-8", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should 415 on unknown charset", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain; charset=x-bogus");
test.write(Buffer.from("00000000", "hex"));
test.expect(415, '[charset.unsupported] unsupported charset "X-BOGUS"', done);
});
});
describe("encoding", function () {
beforeAll(function () {
app = createApp({ limit: "10kb" });
});
it("should parse without encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should support identity encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "identity");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("6e616d6520697320e8aeba", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should support gzip encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "gzip");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should support deflate encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "deflate");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("789ccb4bcc4d55c82c5678b16e17001a6f050e", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should be case-insensitive", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "GZIP");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("1f8b080000000000000bcb4bcc4d55c82c5678b16e170072b3e0200b000000", "hex"));
test.expect(200, '"name is 论"', done);
});
it("should 415 on unknown encoding", function (done) {
var test = request(app).post("/");
test.set("Content-Encoding", "nulls");
test.set("Content-Type", "text/plain");
test.write(Buffer.from("000000000000", "hex"));
test.expect(415, '[encoding.unsupported] unsupported content encoding "nulls"', done);
});
});
});
function createApp(options) {
var app = express();
app.use(express.text(options));
app.use(function (err, req, res, next) {
res.status(err.status || 500);
res.send(
String(
req.headers["x-error-property"] ? err[req.headers["x-error-property"]] : "[" + err.type + "] " + err.message,
),
);
});
app.post("/", function (req, res) {
res.json(req.body);
});
return app;
}

View File

@@ -1 +0,0 @@
20%

View File

@@ -1 +0,0 @@
tobi

View File

@@ -1 +0,0 @@
<b>index</b>

View File

@@ -1 +0,0 @@
<h1>blog post</h1>

View File

@@ -1 +0,0 @@
tobi

View File

@@ -1 +0,0 @@
123456789

View File

@@ -1 +0,0 @@
<p>{{user.name}}</p>

View File

@@ -1,7 +0,0 @@
{
"name": "express-test",
"version": "1.0.0",
"dependencies": {
"express": "4.18.2"
}
}

View File

@@ -1,170 +0,0 @@
"use strict";
var express = require("express"),
request = require("supertest"),
assert = require("node:assert");
describe("res", function () {
describe(".json(object)", function () {
it("should not support jsonp callbacks", function (done) {
var app = express();
app.use(function (req, res) {
res.json({ foo: "bar" });
});
request(app).get("/?callback=foo").expect('{"foo":"bar"}', done);
});
it("should not override previous Content-Types", function (done) {
var app = express();
app.get("/", function (req, res) {
res.type("application/vnd.example+json");
res.json({ hello: "world" });
});
request(app)
.get("/")
.expect("Content-Type", "application/vnd.example+json; charset=utf-8")
.expect(200, '{"hello":"world"}', done);
});
describe("when given primitives", function () {
it("should respond with json for null", function (done) {
var app = express();
app.use(function (req, res) {
res.json(null);
});
request(app).get("/").expect("Content-Type", "application/json; charset=utf-8").expect(200, "null", done);
});
it("should respond with json for Number", function (done) {
var app = express();
app.use(function (req, res) {
res.json(300);
});
request(app).get("/").expect("Content-Type", "application/json; charset=utf-8").expect(200, "300", done);
});
it("should respond with json for String", function (done) {
var app = express();
app.use(function (req, res) {
res.json("str");
});
request(app).get("/").expect("Content-Type", "application/json; charset=utf-8").expect(200, '"str"', done);
});
});
describe("when given an array", function () {
it("should respond with json", function (done) {
var app = express();
app.use(function (req, res) {
res.json(["foo", "bar", "baz"]);
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '["foo","bar","baz"]', done);
});
});
describe("when given an object", function () {
it("should respond with json", function (done) {
var app = express();
app.use(function (req, res) {
res.json({ name: "tobi" });
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '{"name":"tobi"}', done);
});
});
describe('"json escape" setting', function () {
it("should be undefined by default", function () {
var app = express();
assert.strictEqual(app.get("json escape"), undefined);
});
it("should unicode escape HTML-sniffing characters", function (done) {
var app = express();
app.enable("json escape");
app.use(function (req, res) {
res.json({ "&": "<script>" });
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '{"\\u0026":"\\u003cscript\\u003e"}', done);
});
it("should not break undefined escape", function (done) {
var app = express();
app.enable("json escape");
app.use(function (req, res) {
res.json(undefined);
});
request(app).get("/").expect("Content-Type", "application/json; charset=utf-8").expect(200, "", done);
});
});
describe('"json replacer" setting', function () {
it("should be passed to JSON.stringify()", function (done) {
var app = express();
app.set("json replacer", function (key, val) {
return key[0] === "_" ? undefined : val;
});
app.use(function (req, res) {
res.json({ name: "tobi", _id: 12345 });
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '{"name":"tobi"}', done);
});
});
describe('"json spaces" setting', function () {
it("should be undefined by default", function () {
var app = express();
assert(undefined === app.get("json spaces"));
});
it("should be passed to JSON.stringify()", function (done) {
var app = express();
app.set("json spaces", 2);
app.use(function (req, res) {
res.json({ name: "tobi", age: 2 });
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done);
});
});
});
});

View File

@@ -1,316 +0,0 @@
'use strict'
var express = require('express')
, request = require('supertest')
, assert = require('node:assert')
, url = require('node:url');
describe('res', function(){
describe('.location(url)', function(){
it('should set the header', function(done){
var app = express();
app.use(function(req, res){
res.location('http://google.com/').end();
});
request(app)
.get('/')
.expect('Location', 'http://google.com/')
.expect(200, done)
})
it('should preserve trailing slashes when not present', function(done){
var app = express();
app.use(function(req, res){
res.location('http://google.com').end();
});
request(app)
.get('/')
.expect('Location', 'http://google.com')
.expect(200, done)
})
it('should encode "url"', function (done) {
var app = express()
app.use(function (req, res) {
res.location('https://google.com?q=\u2603 §10').end()
})
request(app)
.get('/')
.expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710')
.expect(200, done)
})
it('should encode data uri1', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();
});
request(app)
.get('/')
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
.expect(200, done)
})
it('should encode data uri2', function (done) {
var app = express()
app.use(function (req, res) {
res.location('data:text/javascript,export default () => { }').end();
});
request(app)
.get('/')
.expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
.expect(200, done)
})
it('should consistently handle non-string input: boolean', function (done) {
var app = express()
app.use(function (req, res) {
res.location(true).end();
});
request(app)
.get('/')
.expect('Location', 'true')
.expect(200, done)
});
it('should consistently handle non-string inputs: object', function (done) {
var app = express()
app.use(function (req, res) {
res.location({}).end();
});
request(app)
.get('/')
.expect('Location', '[object%20Object]')
.expect(200, done)
});
it.todo('should consistently handle non-string inputs: array', function (done) {
var app = express()
app.use(function (req, res) {
res.location([]).end();
});
request(app)
.get('/')
.expect('Location', '')
.expect(200, done)
});
it.todo('should consistently handle empty string input', function (done) {
var app = express()
app.use(function (req, res) {
res.location('').end();
});
request(app)
.get('/')
.expect('Location', '')
.expect(200, done)
});
if (typeof URL !== 'undefined') {
it('should accept an instance of URL', function (done) {
var app = express();
app.use(function(req, res){
res.location(new URL('http://google.com/')).end();
});
request(app)
.get('/')
.expect('Location', 'http://google.com/')
.expect(200, done);
});
}
})
describe('location header encoding', function() {
function createRedirectServerForDomain (domain) {
var app = express();
app.use(function (req, res) {
var host = url.parse(req.query.q, false, true).host;
// This is here to show a basic check one might do which
// would pass but then the location header would still be bad
if (host !== domain) {
res.status(400).end('Bad host: ' + host + ' !== ' + domain);
}
res.location(req.query.q).end();
});
return app;
}
function testRequestedRedirect (app, inputUrl, expected, expectedHost, done) {
return request(app)
// Encode uri because old supertest does not and is required
// to test older node versions. New supertest doesn't re-encode
// so this works in both.
.get('/?q=' + encodeURIComponent(inputUrl))
.expect('') // No body.
.expect(200)
.expect('Location', expected)
.end(function (err, res) {
if (err) {
console.log('headers:', res.headers)
console.error('error', res.error, err);
return done(err, res);
}
// Parse the hosts from the input URL and the Location header
var inputHost = url.parse(inputUrl, false, true).host;
var locationHost = url.parse(res.headers['location'], false, true).host;
assert.strictEqual(locationHost, expectedHost);
// Assert that the hosts are the same
if (inputHost !== locationHost) {
return done(new Error('Hosts do not match: ' + inputHost + " !== " + locationHost));
}
return done(null, res);
});
}
it('should not touch already-encoded sequences in "url"', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com?q=%A710',
'https://google.com?q=%A710',
'google.com',
done
);
});
it('should consistently handle relative urls', function (done) {
var app = createRedirectServerForDomain(null);
testRequestedRedirect(
app,
'/foo/bar',
'/foo/bar',
null,
done
);
});
it.todo('should not encode urls in such a way that they can bypass redirect allow lists', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com',
'http://google.com\\@apple.com',
'google.com',
done
);
});
it.todo('should not be case sensitive', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'HTTP://google.com\\@apple.com',
'HTTP://google.com\\@apple.com',
'google.com',
done
);
});
it.todo('should work with https', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com\\@apple.com',
'https://google.com\\@apple.com',
'google.com',
done
);
});
it.todo('should correctly encode schemaless paths', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'//google.com\\@apple.com/',
'//google.com\\@apple.com/',
'google.com',
done
);
});
it.todo('should keep backslashes in the path', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com/foo\\bar\\baz',
'https://google.com/foo\\bar\\baz',
'google.com',
done
);
});
it.todo('should escape header splitting for old node versions', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
'http://google.com\\@apple.com/%0d%0afoo:%20bar',
'google.com',
done
);
});
it('should encode unicode correctly', function (done) {
var app = createRedirectServerForDomain(null);
testRequestedRedirect(
app,
'/%e2%98%83',
'/%e2%98%83',
null,
done
);
});
it.todo('should encode unicode correctly even with a bad host', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'http://google.com\\@apple.com/%e2%98%83',
'http://google.com\\@apple.com/%e2%98%83',
'google.com',
done
);
});
it('should work correctly despite using deprecated url.parse', function (done) {
var app = createRedirectServerForDomain('google.com');
testRequestedRedirect(
app,
'https://google.com\'.bb.com/1.html',
'https://google.com\'.bb.com/1.html',
'google.com',
done
);
});
it.todo('should encode file uri path', function (done) {
var app = createRedirectServerForDomain('');
testRequestedRedirect(
app,
'file:///etc\\passwd',
'file:///etc\\passwd',
'',
done
);
});
});
})

View File

@@ -1,214 +0,0 @@
'use strict'
var express = require('express');
var request = require('supertest');
var utils = require('./support/utils');
describe('res', function(){
describe('.redirect(url)', function(){
it('should default to a 302 redirect', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com');
});
request(app)
.get('/')
.expect('location', 'http://google.com')
.expect(302, done)
})
it('should encode "url"', function (done) {
var app = express()
app.use(function (req, res) {
res.redirect('https://google.com?q=\u2603 §10')
})
request(app)
.get('/')
.expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710')
.expect(302, done)
})
it('should not touch already-encoded sequences in "url"', function (done) {
var app = express()
app.use(function (req, res) {
res.redirect('https://google.com?q=%A710')
})
request(app)
.get('/')
.expect('Location', 'https://google.com?q=%A710')
.expect(302, done)
})
})
describe('.redirect(status, url)', function(){
it('should set the response status', function(done){
var app = express();
app.use(function(req, res){
res.redirect(303, 'http://google.com');
});
request(app)
.get('/')
.expect('Location', 'http://google.com')
.expect(303, done)
})
})
describe('when the request method is HEAD', function(){
it('should ignore the body', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com');
});
request(app)
.head('/')
.expect(302)
.expect('Location', 'http://google.com')
.expect(utils.shouldNotHaveBody())
.end(done)
})
})
describe('when accepting html', function(){
it.todo('should respond with html', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com');
});
request(app)
.get('/')
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', 'http://google.com')
.expect(302, '<p>Found. Redirecting to http://google.com</p>', done)
})
it.todo('should escape the url', function(done){
var app = express();
app.use(function(req, res){
res.redirect('<la\'me>');
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', '%3Cla\'me%3E')
.expect(302, '<p>Found. Redirecting to %3Cla&#39;me%3E</p>', done)
})
it.todo('should not render evil javascript links in anchor href (prevent XSS)', function(done){
var app = express();
var xss = 'javascript:eval(document.body.innerHTML=`<p>XSS</p>`);';
var encodedXss = 'javascript:eval(document.body.innerHTML=%60%3Cp%3EXSS%3C/p%3E%60);';
app.use(function(req, res){
res.redirect(xss);
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', encodedXss)
.expect(302, '<p>Found. Redirecting to ' + encodedXss +'</p>', done);
});
it.todo('should include the redirect type', function(done){
var app = express();
app.use(function(req, res){
res.redirect(301, 'http://google.com');
});
request(app)
.get('/')
.set('Accept', 'text/html')
.expect('Content-Type', /html/)
.expect('Location', 'http://google.com')
.expect(301, '<p>Moved Permanently. Redirecting to http://google.com</p>', done);
})
})
describe('when accepting text', function(){
it('should respond with text', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com');
});
request(app)
.get('/')
.set('Accept', 'text/plain, */*')
.expect('Content-Type', /plain/)
.expect('Location', 'http://google.com')
.expect(302, 'Found. Redirecting to http://google.com', done)
})
it('should encode the url', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://example.com/?param=<script>alert("hax");</script>');
});
request(app)
.get('/')
.set('Host', 'http://example.com')
.set('Accept', 'text/plain, */*')
.expect('Content-Type', /plain/)
.expect('Location', 'http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E')
.expect(302, 'Found. Redirecting to http://example.com/?param=%3Cscript%3Ealert(%22hax%22);%3C/script%3E', done)
})
it('should include the redirect type', function(done){
var app = express();
app.use(function(req, res){
res.redirect(301, 'http://google.com');
});
request(app)
.get('/')
.set('Accept', 'text/plain, */*')
.expect('Content-Type', /plain/)
.expect('Location', 'http://google.com')
.expect(301, 'Moved Permanently. Redirecting to http://google.com', done);
})
})
describe('when accepting neither text or html', function(){
it('should respond with an empty body', function(done){
var app = express();
app.use(function(req, res){
res.redirect('http://google.com');
});
request(app)
.get('/')
.set('Accept', 'application/octet-stream')
.expect(302)
.expect('location', 'http://google.com')
.expect('content-length', '0')
.expect(utils.shouldNotHaveHeader('Content-Type'))
.expect(utils.shouldNotHaveBody())
.end(done)
})
})
})

View File

@@ -1,475 +0,0 @@
"use strict";
var assert = require("node:assert");
var express = require("express");
var request = require("supertest");
var utils = require("./support/utils");
var shouldSkipQuery = require("./support/utils").shouldSkipQuery;
var methods = utils.methods;
describe("res", function () {
describe(".send()", function () {
it('should set body to ""', function (done) {
var app = express();
app.use(function (req, res) {
res.send();
});
request(app).get("/").expect(200, "", done);
});
});
describe(".send(null)", function () {
it('should set body to ""', function (done) {
var app = express();
app.use(function (req, res) {
res.send(null);
});
request(app).get("/").expect("Content-Length", "0").expect(200, "", done);
});
});
describe(".send(undefined)", function () {
it('should set body to ""', function (done) {
var app = express();
app.use(function (req, res) {
res.send(undefined);
});
request(app).get("/").expect(200, "", done);
});
});
describe(".send(Number)", function () {
it.todo("should send as application/json", function (done) {
var app = express();
app.use(function (req, res) {
res.send(1000);
});
request(app).get("/").expect("Content-Type", "application/json; charset=utf-8").expect(200, "1000", done);
});
});
describe(".send(String)", function () {
it("should send as html", function (done) {
var app = express();
app.use(function (req, res) {
res.send("<p>hey</p>");
});
request(app).get("/").expect("Content-Type", "text/html; charset=utf-8").expect(200, "<p>hey</p>", done);
});
it("should set ETag", function (done) {
var app = express();
app.use(function (req, res) {
var str = Array(1000).join("-");
res.send(str);
});
request(app).get("/").expect("ETag", 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"').expect(200, done);
});
it("should not override Content-Type", function (done) {
var app = express();
app.use(function (req, res) {
res.set("Content-Type", "text/plain").send("hey");
});
request(app).get("/").expect("Content-Type", "text/plain; charset=utf-8").expect(200, "hey", done);
});
it("should override charset in Content-Type", function (done) {
var app = express();
app.use(function (req, res) {
res.set("Content-Type", "text/plain; charset=iso-8859-1").send("hey");
});
request(app).get("/").expect("Content-Type", "text/plain; charset=utf-8").expect(200, "hey", done);
});
it("should keep charset in Content-Type for Buffers", function (done) {
var app = express();
app.use(function (req, res) {
res.set("Content-Type", "text/plain; charset=iso-8859-1").send(Buffer.from("hi"));
});
request(app).get("/").expect("Content-Type", "text/plain; charset=iso-8859-1").expect(200, "hi", done);
});
});
describe(".send(Buffer)", function () {
it("should send as octet-stream", function (done) {
var app = express();
app.use(function (req, res) {
res.send(Buffer.from("hello"));
});
request(app)
.get("/")
.expect(200)
.expect("Content-Type", "application/octet-stream")
.expect(utils.shouldHaveBody(Buffer.from("hello")))
.end(done);
});
it("should set ETag", function (done) {
var app = express();
app.use(function (req, res) {
res.send(Buffer.alloc(999, "-"));
});
request(app).get("/").expect("ETag", 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"').expect(200, done);
});
it("should not override Content-Type", function (done) {
var app = express();
app.use(function (req, res) {
res.set("Content-Type", "text/plain").send(Buffer.from("hey"));
});
request(app).get("/").expect("Content-Type", "text/plain; charset=utf-8").expect(200, "hey", done);
});
it.todo("should accept Uint8Array", function (done) {
var app = express();
app.use(function (req, res) {
const encodedHey = new TextEncoder().encode("hey");
res.set("Content-Type", "text/plain").send(encodedHey);
});
request(app).get("/").expect("Content-Type", "text/plain; charset=utf-8").expect(200, "hey", done);
});
it("should not override ETag", function (done) {
var app = express();
app.use(function (req, res) {
res.type("text/plain").set("ETag", '"foo"').send(Buffer.from("hey"));
});
request(app).get("/").expect("ETag", '"foo"').expect(200, "hey", done);
});
});
describe(".send(Object)", function () {
it("should send as application/json", function (done) {
var app = express();
app.use(function (req, res) {
res.send({ name: "tobi" });
});
request(app)
.get("/")
.expect("Content-Type", "application/json; charset=utf-8")
.expect(200, '{"name":"tobi"}', done);
});
});
describe("when the request method is HEAD", function () {
it("should ignore the body", function (done) {
var app = express();
app.use(function (req, res) {
res.send("yay");
});
request(app).head("/").expect(200).expect(utils.shouldNotHaveBody()).end(done);
});
});
describe("when .statusCode is 204", function () {
it.todo("should strip Content-* fields, Transfer-Encoding field, and body", function (done) {
var app = express();
app.use(function (req, res) {
res.status(204).set("Transfer-Encoding", "chunked").send("foo");
});
request(app)
.get("/")
.expect(utils.shouldNotHaveHeader("Content-Type"))
.expect(utils.shouldNotHaveHeader("Content-Length"))
.expect(utils.shouldNotHaveHeader("Transfer-Encoding"))
.expect(204, "", done);
});
});
describe("when .statusCode is 205", function () {
it("should strip Transfer-Encoding field and body, set Content-Length", function (done) {
var app = express();
app.use(function (req, res) {
res.status(205).set("Transfer-Encoding", "chunked").send("foo");
});
request(app)
.get("/")
.expect(utils.shouldNotHaveHeader("Transfer-Encoding"))
.expect("Content-Length", "0")
.expect(205, "", done);
});
});
describe("when .statusCode is 304", function () {
it.todo("should strip Content-* fields, Transfer-Encoding field, and body", function (done) {
var app = express();
app.use(function (req, res) {
res.status(304).set("Transfer-Encoding", "chunked").send("foo");
});
request(app)
.get("/")
.expect(utils.shouldNotHaveHeader("Content-Type"))
.expect(utils.shouldNotHaveHeader("Content-Length"))
.expect(utils.shouldNotHaveHeader("Transfer-Encoding"))
.expect(304, "", done);
});
});
it("should always check regardless of length", function (done) {
var app = express();
var etag = '"asdf"';
app.use(function (req, res, next) {
res.set("ETag", etag);
res.send("hey");
});
request(app).get("/").set("If-None-Match", etag).expect(304, done);
});
it("should respond with 304 Not Modified when fresh", function (done) {
var app = express();
var etag = '"asdf"';
app.use(function (req, res) {
var str = Array(1000).join("-");
res.set("ETag", etag);
res.send(str);
});
request(app).get("/").set("If-None-Match", etag).expect(304, done);
});
it("should not perform freshness check unless 2xx or 304", function (done) {
var app = express();
var etag = '"asdf"';
app.use(function (req, res, next) {
res.status(500);
res.set("ETag", etag);
res.send("hey");
});
request(app).get("/").set("If-None-Match", etag).expect("hey").expect(500, done);
});
it("should not support jsonp callbacks", function (done) {
var app = express();
app.use(function (req, res) {
res.send({ foo: "bar" });
});
request(app).get("/?callback=foo").expect('{"foo":"bar"}', done);
});
it("should be chainable", function (done) {
var app = express();
app.use(function (req, res) {
assert.equal(res.send("hey"), res);
});
request(app).get("/").expect(200, "hey", done);
});
describe('"etag" setting', function () {
describe("when enabled", function () {
it("should send ETag", function (done) {
var app = express();
app.use(function (req, res) {
res.send("kajdslfkasdf");
});
app.enable("etag");
request(app).get("/").expect("ETag", 'W/"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0"').expect(200, done);
});
methods.forEach(function (method) {
if (method === "connect") return;
it("should send ETag in response to " + method.toUpperCase() + " request", function (done) {
if (method === "query" && shouldSkipQuery(process.versions.node)) {
this.skip();
}
var app = express();
app[method]("/", function (req, res) {
res.send("kajdslfkasdf");
});
request(app)[method]("/").expect("ETag", 'W/"c-IgR/L5SF7CJQff4wxKGF/vfPuZ0"').expect(200, done);
});
});
it("should send ETag for empty string response", function (done) {
var app = express();
app.use(function (req, res) {
res.send("");
});
app.enable("etag");
request(app).get("/").expect("ETag", 'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"').expect(200, done);
});
it("should send ETag for long response", function (done) {
var app = express();
app.use(function (req, res) {
var str = Array(1000).join("-");
res.send(str);
});
app.enable("etag");
request(app).get("/").expect("ETag", 'W/"3e7-qPnkJ3CVdVhFJQvUBfF10TmVA7g"').expect(200, done);
});
it("should not override ETag when manually set", function (done) {
var app = express();
app.use(function (req, res) {
res.set("etag", '"asdf"');
res.send("hello!");
});
app.enable("etag");
request(app).get("/").expect("ETag", '"asdf"').expect(200, done);
});
it("should not send ETag for res.send()", function (done) {
var app = express();
app.use(function (req, res) {
res.send();
});
app.enable("etag");
request(app).get("/").expect(utils.shouldNotHaveHeader("ETag")).expect(200, done);
});
});
describe("when disabled", function () {
it("should send no ETag", function (done) {
var app = express();
app.use(function (req, res) {
var str = Array(1000).join("-");
res.send(str);
});
app.disable("etag");
request(app).get("/").expect(utils.shouldNotHaveHeader("ETag")).expect(200, done);
});
it("should send ETag when manually set", function (done) {
var app = express();
app.disable("etag");
app.use(function (req, res) {
res.set("etag", '"asdf"');
res.send("hello!");
});
request(app).get("/").expect("ETag", '"asdf"').expect(200, done);
});
});
describe('when "strong"', function () {
it("should send strong ETag", function (done) {
var app = express();
app.set("etag", "strong");
app.use(function (req, res) {
res.send("hello, world!");
});
request(app).get("/").expect("ETag", '"d-HwnTDHB9U/PRbFMN1z1wps51lqk"').expect(200, done);
});
});
describe('when "weak"', function () {
it("should send weak ETag", function (done) {
var app = express();
app.set("etag", "weak");
app.use(function (req, res) {
res.send("hello, world!");
});
request(app).get("/").expect("ETag", 'W/"d-HwnTDHB9U/PRbFMN1z1wps51lqk"').expect(200, done);
});
});
describe("when a function", function () {
it("should send custom ETag", function (done) {
var app = express();
app.set("etag", function (body, encoding) {
var chunk = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
assert.strictEqual(chunk.toString(), "hello, world!");
return '"custom"';
});
app.use(function (req, res) {
res.send("hello, world!");
});
request(app).get("/").expect("ETag", '"custom"').expect(200, done);
});
it("should not send falsy ETag", function (done) {
var app = express();
app.set("etag", function (body, encoding) {
return undefined;
});
app.use(function (req, res) {
res.send("hello, world!");
});
request(app).get("/").expect(utils.shouldNotHaveHeader("ETag")).expect(200, done);
});
});
});
});

View File

@@ -1,774 +0,0 @@
"use strict";
var after = require("./support/after");
var assert = require("node:assert");
var AsyncLocalStorage = require("node:async_hooks").AsyncLocalStorage;
var express = require("express"),
request = require("supertest");
// var onFinished = require("on-finished");
var path = require("node:path");
var fixtures = path.join(__dirname, "fixtures");
var utils = require("./support/utils");
describe("res", function () {
describe(".sendFile(path)", function () {
it("should error missing path", function (done) {
var app = createApp();
request(app)
.get("/")
.expect(500, /path.*required/, done);
});
it("should error for non-string path", function (done) {
var app = createApp(42);
request(app)
.get("/")
.expect(500, /TypeError: path must be a string to res.sendFile/, done);
});
it("should error for non-absolute path", function (done) {
var app = createApp("name.txt");
request(app)
.get("/")
.expect(500, /TypeError: path must be absolute/, done);
});
it.todo("should transfer a file", function (done) {
var app = createApp(path.resolve(fixtures, "name.txt"));
request(app).get("/").expect(200, "tobi", done);
});
it("should transfer a file with special characters in string", function (done) {
var app = createApp(path.resolve(fixtures, "% of dogs.txt"));
request(app).get("/").expect(200, "20%", done);
});
it.todo("should include ETag", function (done) {
var app = createApp(path.resolve(fixtures, "name.txt"));
request(app)
.get("/")
.expect("ETag", /^(?:W\/)?"[^"]+"$/)
.expect(200, "tobi", done);
});
it.todo("should 304 when ETag matches", function (done) {
var app = createApp(path.resolve(fixtures, "name.txt"));
request(app)
.get("/")
.expect("ETag", /^(?:W\/)?"[^"]+"$/)
.expect(200, "tobi", function (err, res) {
if (err) return done(err);
var etag = res.headers.etag;
request(app).get("/").set("If-None-Match", etag).expect(304, done);
});
});
it.todo("should disable the ETag function if requested", function (done) {
var app = createApp(path.resolve(fixtures, "name.txt")).disable("etag");
request(app).get("/").expect(handleHeaders).expect(200, done);
function handleHeaders(res) {
assert(res.headers.etag === undefined);
}
});
it("should 404 for directory", function (done) {
var app = createApp(path.resolve(fixtures, "blog"));
request(app).get("/").expect(404, done);
});
it("should 404 when not found", function (done) {
var app = createApp(path.resolve(fixtures, "does-no-exist"));
app.use(function (req, res) {
res.statusCode = 200;
res.send("no!");
});
request(app).get("/").expect(404, done);
});
it("should send cache-control by default", function (done) {
var app = createApp(path.resolve(__dirname, "fixtures/name.txt"));
request(app).get("/").expect("Cache-Control", "public, max-age=0").expect(200, done);
});
it("should not serve dotfiles by default", function (done) {
var app = createApp(path.resolve(__dirname, "fixtures/.name"));
request(app).get("/").expect(404, done);
});
it("should not override manual content-types", function (done) {
var app = express();
app.use(function (req, res) {
res.contentType("application/x-bogus");
res.sendFile(path.resolve(fixtures, "name.txt"));
});
request(app).get("/").expect("Content-Type", "application/x-bogus").end(done);
});
it.todo("should not error if the client aborts", function (done) {
var app = express();
var cb = after(2, done);
var error = null;
app.use(function (req, res) {
setImmediate(function () {
res.sendFile(path.resolve(fixtures, "name.txt"));
setTimeout(function () {
cb(error);
}, 10);
});
test.req.abort();
});
app.use(function (err, req, res, next) {
error = err;
next(err);
});
var server = app.listen();
var test = request(server).get("/");
test.end(function (err) {
assert.ok(err);
server.close(cb);
});
});
});
describe(".sendFile(path, fn)", function () {
it("should invoke the callback when complete", function (done) {
var cb = after(2, done);
var app = createApp(path.resolve(fixtures, "name.txt"), cb);
request(app).get("/").expect(200, cb);
});
it.todo("should invoke the callback when client aborts", function (done) {
var cb = after(2, done);
var app = express();
app.use(function (req, res) {
setImmediate(function () {
res.sendFile(path.resolve(fixtures, "name.txt"), function (err) {
assert.ok(err);
assert.strictEqual(err.code, "ECONNABORTED");
cb();
});
});
test.req.abort();
});
var server = app.listen();
var test = request(server).get("/");
test.end(function (err) {
assert.ok(err);
server.close(cb);
});
});
// TODO: refactor to not use onFinished
// it("should invoke the callback when client already aborted", function (done) {
// var cb = after(2, done);
// var app = express();
// app.use(function (req, res) {
// onFinished(res, function () {
// res.sendFile(path.resolve(fixtures, "name.txt"), function (err) {
// assert.ok(err);
// assert.strictEqual(err.code, "ECONNABORTED");
// cb();
// });
// });
// test.req.abort();
// });
// var server = app.listen();
// var test = request(server).get("/");
// test.end(function (err) {
// assert.ok(err);
// server.close(cb);
// });
// });
it("should invoke the callback without error when HEAD", function (done) {
var app = express();
var cb = after(2, done);
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "name.txt"), cb);
});
request(app).head("/").expect(200, cb);
});
it.todo("should invoke the callback without error when 304", function (done) {
var app = express();
var cb = after(3, done);
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "name.txt"), cb);
});
request(app)
.get("/")
.expect("ETag", /^(?:W\/)?"[^"]+"$/)
.expect(200, "tobi", function (err, res) {
if (err) return cb(err);
var etag = res.headers.etag;
request(app).get("/").set("If-None-Match", etag).expect(304, cb);
});
});
it("should invoke the callback on 404", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "does-not-exist"), function (err) {
res.send(err ? "got " + err.status + " error" : "no error");
});
});
request(app).get("/").expect(200, "got 404 error", done);
});
describe.todo("async local storage", function () {
it("should presist store", function (done) {
var app = express();
var cb = after(2, done);
var store = { foo: "bar" };
app.use(function (req, res, next) {
req.asyncLocalStorage = new AsyncLocalStorage();
req.asyncLocalStorage.run(store, next);
});
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "name.txt"), function (err) {
if (err) return cb(err);
var local = req.asyncLocalStorage.getStore();
assert.strictEqual(local.foo, "bar");
cb();
});
});
request(app).get("/").expect("Content-Type", "text/plain; charset=utf-8").expect(200, "tobi", cb);
});
it("should presist store on error", function (done) {
var app = express();
var store = { foo: "bar" };
app.use(function (req, res, next) {
req.asyncLocalStorage = new AsyncLocalStorage();
req.asyncLocalStorage.run(store, next);
});
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "does-not-exist"), function (err) {
var local = req.asyncLocalStorage.getStore();
if (local) {
res.setHeader("x-store-foo", String(local.foo));
}
res.send(err ? "got " + err.status + " error" : "no error");
});
});
request(app).get("/").expect(200).expect("x-store-foo", "bar").expect("got 404 error").end(done);
});
});
});
describe(".sendFile(path, options)", function () {
it("should pass options to send module", function (done) {
request(createApp(path.resolve(fixtures, "name.txt"), { start: 0, end: 1 }))
.get("/")
.expect(200, "to", done);
});
describe('with "acceptRanges" option', function () {
describe("when true", function () {
it("should advertise byte range accepted", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "nums.txt"), {
acceptRanges: true,
});
});
request(app).get("/").expect(200).expect("Accept-Ranges", "bytes").expect("123456789").end(done);
});
it("should respond to range request", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "nums.txt"), {
acceptRanges: true,
});
});
request(app).get("/").set("Range", "bytes=0-4").expect(206, "12345", done);
});
});
describe("when false", function () {
it("should not advertise accept-ranges", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "nums.txt"), {
acceptRanges: false,
});
});
request(app).get("/").expect(200).expect(utils.shouldNotHaveHeader("Accept-Ranges")).end(done);
});
it("should not honor range requests", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "nums.txt"), {
acceptRanges: false,
});
});
request(app).get("/").set("Range", "bytes=0-4").expect(200, "123456789", done);
});
});
});
describe('with "cacheControl" option', function () {
describe("when true", function () {
it("should send cache-control header", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
cacheControl: true,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=0").end(done);
});
});
describe("when false", function () {
it("should not send cache-control header", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
cacheControl: false,
});
});
request(app).get("/").expect(200).expect(utils.shouldNotHaveHeader("Cache-Control")).end(done);
});
});
});
describe('with "dotfiles" option', function () {
describe('when "allow"', function () {
it("should allow dotfiles", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, ".name"), {
dotfiles: "allow",
});
});
request(app)
.get("/")
.expect(200)
.expect(utils.shouldHaveBody(Buffer.from("tobi")))
.end(done);
});
});
describe('when "deny"', function () {
it("should deny dotfiles", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, ".name"), {
dotfiles: "deny",
});
});
request(app)
.get("/")
.expect(403)
.expect(/Forbidden/)
.end(done);
});
});
describe('when "ignore"', function () {
it("should ignore dotfiles", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, ".name"), {
dotfiles: "ignore",
});
});
request(app)
.get("/")
.expect(404)
.expect(/Not Found/)
.end(done);
});
});
});
describe('with "headers" option', function () {
it("should set headers on response", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
headers: {
"X-Foo": "Bar",
"X-Bar": "Foo",
},
});
});
request(app).get("/").expect(200).expect("X-Foo", "Bar").expect("X-Bar", "Foo").end(done);
});
it("should use last header when duplicated", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
headers: {
"X-Foo": "Bar",
"x-foo": "bar",
},
});
});
request(app).get("/").expect(200).expect("X-Foo", "bar").end(done);
});
it("should override Content-Type", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
headers: {
"Content-Type": "text/x-custom",
},
});
});
request(app).get("/").expect(200).expect("Content-Type", "text/x-custom").end(done);
});
it("should not set headers on 404", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "does-not-exist"), {
headers: {
"X-Foo": "Bar",
},
});
});
request(app).get("/").expect(404).expect(utils.shouldNotHaveHeader("X-Foo")).end(done);
});
});
describe('with "immutable" option', function () {
describe("when true", function () {
it("should send cache-control header with immutable", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
immutable: true,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=0, immutable").end(done);
});
});
describe("when false", function () {
it("should not send cache-control header with immutable", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
immutable: false,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=0").end(done);
});
});
});
describe('with "lastModified" option', function () {
describe("when true", function () {
it("should send last-modified header", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
lastModified: true,
});
});
request(app).get("/").expect(200).expect(utils.shouldHaveHeader("Last-Modified")).end(done);
});
it("should conditionally respond with if-modified-since", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
lastModified: true,
});
});
request(app)
.get("/")
.set("If-Modified-Since", new Date(Date.now() + 99999).toUTCString())
.expect(304, done);
});
});
describe("when false", function () {
it("should not have last-modified header", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
lastModified: false,
});
});
request(app).get("/").expect(200).expect(utils.shouldNotHaveHeader("Last-Modified")).end(done);
});
it("should not honor if-modified-since", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
lastModified: false,
});
});
request(app)
.get("/")
.set("If-Modified-Since", new Date(Date.now() + 99999).toUTCString())
.expect(200)
.expect(utils.shouldNotHaveHeader("Last-Modified"))
.end(done);
});
});
});
describe('with "maxAge" option', function () {
it("should set cache-control max-age to milliseconds", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: 20000,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=20").end(done);
});
it("should cap cache-control max-age to 1 year", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: 99999999999,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=31536000").end(done);
});
it.todo("should min cache-control max-age to 0", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: -20000,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=0").end(done);
});
it.todo("should floor cache-control max-age", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: 21911.23,
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=21").end(done);
});
describe("when cacheControl: false", function () {
it("should not send cache-control", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
cacheControl: false,
maxAge: 20000,
});
});
request(app).get("/").expect(200).expect(utils.shouldNotHaveHeader("Cache-Control")).end(done);
});
});
describe("when string", function () {
it("should accept plain number as milliseconds", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: "20000",
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=20").end(done);
});
it('should accept suffix "s" for seconds', function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: "20s",
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=20").end(done);
});
it('should accept suffix "m" for minutes', function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: "20m",
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=1200").end(done);
});
it('should accept suffix "d" for days', function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(path.resolve(fixtures, "user.html"), {
maxAge: "20d",
});
});
request(app).get("/").expect(200).expect("Cache-Control", "public, max-age=1728000").end(done);
});
});
});
describe.todo('with "root" option', function () {
it("should allow relative path", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile("name.txt", {
root: fixtures,
});
});
request(app).get("/").expect(200, "tobi", done);
});
it("should allow up within root", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile("fake/../name.txt", {
root: fixtures,
});
});
request(app).get("/").expect(200, "tobi", done);
});
it.todo("should reject up outside root", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile(".." + path.sep + path.relative(path.dirname(fixtures), path.join(fixtures, "name.txt")), {
root: fixtures,
});
});
request(app).get("/").expect(403, done);
});
it("should reject reading outside root", function (done) {
var app = express();
app.use(function (req, res) {
res.sendFile("../name.txt", {
root: fixtures,
});
});
request(app).get("/").expect(403, done);
});
});
});
});
function createApp(path?, options?, fn?) {
var app = express();
app.use(function sendFileMiddleware (req, res) {
res.sendFile(path, options, fn);
});
return app;
}

View File

@@ -1,36 +0,0 @@
/**
* https://www.npmjs.com/package/after
*
* I refuse to install a 30 line function via npm
*/
module.exports = after;
/**
* Invoke a callback after `n` calls
*/
function after(count, callback, err_cb) {
var bail = false;
err_cb = err_cb || noop;
proxy.count = count;
return count === 0 ? callback() : proxy;
function proxy(err, result) {
if (proxy.count <= 0) {
throw new Error("after called too many times");
}
--proxy.count;
// after first error, rest are passed to err_cb
if (err) {
bail = true;
callback(err);
// future error callbacks will go to error handler
callback = err_cb;
} else if (proxy.count === 0 && !bail) {
callback(null, result);
}
}
}
function noop() {}

View File

@@ -1,83 +0,0 @@
/**
* Module dependencies.
* @private
*/
var assert = require("node:assert");
var { METHODS } = require("node:http");
/**
* Module exports.
* @public
*/
exports.shouldHaveBody = shouldHaveBody;
exports.shouldHaveHeader = shouldHaveHeader;
exports.shouldNotHaveBody = shouldNotHaveBody;
exports.shouldNotHaveHeader = shouldNotHaveHeader;
exports.shouldSkipQuery = shouldSkipQuery;
exports.methods = METHODS.map(method => method.toLowerCase()); // from lib/utils.js
/**
* Assert that a supertest response has a specific body.
*
* @param {Buffer} buf
* @returns {function}
*/
function shouldHaveBody(buf) {
return function (res) {
var body = !Buffer.isBuffer(res.body) ? Buffer.from(res.text) : res.body;
assert.ok(body, "response has body");
assert.strictEqual(body.toString("hex"), buf.toString("hex"));
};
}
/**
* Assert that a supertest response does have a header.
*
* @param {string} header Header name to check
* @returns {function}
*/
function shouldHaveHeader(header) {
return function (res) {
assert.ok(header.toLowerCase() in res.headers, "should have header " + header);
};
}
/**
* Assert that a supertest response does not have a body.
*
* @returns {function}
*/
function shouldNotHaveBody() {
return function (res) {
assert.ok(res.text === "" || res.text === undefined);
};
}
/**
* Assert that a supertest response does not have a header.
*
* @param {string} header Header name to check
* @returns {function}
*/
function shouldNotHaveHeader(header) {
return function (res) {
assert.ok(!(header.toLowerCase() in res.headers), "should not have header " + header);
};
}
function getMajorVersion(versionString) {
return versionString.split(".")[0];
}
function shouldSkipQuery(versionString) {
// Skipping HTTP QUERY tests on Node 21, it is reported in http.METHODS on 21.7.2 but not supported
// update this implementation to run on supported versions of 21 once they exist
// upstream tracking https://github.com/nodejs/node/issues/51562
// express tracking issue: https://github.com/expressjs/express/issues/5615
return Number(getMajorVersion(versionString)) === 21;
}

View File

@@ -3,5 +3,597 @@
"package": "elysia",
"repository": "https://github.com/elysiajs/elysia",
"tag": "1.1.24"
},
{
"package": "koa",
"repository": "https://github.com/koajs/koa",
"tag": "2.16.1",
"testPatterns": [
"__tests__/*.test.js"
],
"testFailures": [
"__tests__/application/onerror.test.js > app.onerror(err) > should do nothing if .silent",
"__tests__/application/onerror.test.js > app.onerror(err) > should do nothing if status is 404",
"__tests__/application/onerror.test.js > app.onerror(err) > should log the error to stderr",
"__tests__/application/respond.test.js > app.respond > when .body is missing > with custom status=700 > should respond with the associated status message",
"__tests__/application/respond.test.js > app.respond > when status and body property > should 204",
"__tests__/application/respond.test.js > segmentation fault",
"__tests__/application/response.test.js > app.response > should not include status message in body for http2",
"__tests__/context/onerror.test.js > ctx.onerror(err) > should unset all headers",
"__tests__/context/onerror.test.js > timeout",
"__tests__/response/status.test.js > res.status= > when 204 > should strip content related header fields",
"__tests__/response/status.test.js > res.status= > when 204 > should strip content related header fields after status set",
"__tests__/response/status.test.js > res.status= > when 205 > should strip content related header fields",
"__tests__/response/status.test.js > res.status= > when 205 > should strip content related header fields after status set",
"__tests__/response/status.test.js > res.status= > when 304 > should strip content related header fields",
"__tests__/response/status.test.js > res.status= > when 304 > should strip content related header fields after status set"
]
},
{
"package": "express",
"repository": "https://github.com/expressjs/express",
"tag": "5.1.0",
"testPatterns": [
"test/*.js",
"test/acceptance/*.js"
],
"testFailures": [
"test/Route.js > Route > should not stack overflow with a large sync stack",
"test/Router.js > Router > should not stack overflow with a large sync middleware stack",
"test/Router.js > Router > should not stack overflow with a large sync route stack",
"test/Router.js > Router > should not stack overflow with many registered routes",
"test/acceptance/downloads.js > downloads > GET /files/../index.js > should respond with 403",
"test/acceptance/downloads.js > downloads > GET /files/../index.js > should respond with 403",
"test/app.js > code 3",
"test/app.options.js > OPTIONS > when error occurs in response handler > should pass error to callback",
"test/app.router.js > app.router > methods > should include GET",
"test/app.router.js > app.router > methods > should include PUT",
"test/express.json.js > express.json() > should handle empty message-body",
"test/express.raw.js > code 1",
"test/express.static.js > code 13",
"test/express.text.js > code 1",
"test/express.urlencoded.js > code 1",
"test/middleware.basic.js > middleware > .next() > should behave like connect",
"test/req.xhr.js > code 1",
"test/res.location.js > res > .location(url) > should consistently handle empty string input",
"test/res.location.js > res > .location(url) > should consistently handle non-string inputs: array",
"test/res.send.js > res > when .statusCode is 204 > should strip Content-* fields, Transfer-Encoding field, and body",
"test/res.send.js > res > when .statusCode is 304 > should strip Content-* fields, Transfer-Encoding field, and body",
"test/res.sendFile.js > res > .sendFile(path) > should not error if the client aborts",
"test/res.sendFile.js > timeout",
"test/res.set.js > res > .set(field, values) > should coerce to an array of strings",
"test/res.status.js > res > .status(code) > accept valid ranges > should set the response status code to 700",
"test/res.status.js > res > .status(code) > accept valid ranges > should set the response status code to 800",
"test/res.status.js > res > .status(code) > accept valid ranges > should set the response status code to 900"
]
},
{
"package": "hono",
"repository": "https://github.com/honojs/hono",
"tag": "v4.7.6",
"testPatterns": [
"src/*.test.ts",
"runtime-tests/bun/*.test.tsx"
],
"testFailures": [
"src/adapter/bun/websocket.test.ts > createBunWebSocket() > Should events are called",
"src/adapter/bun/websocket.test.ts > createWSContext() > Should send() and close() works",
"src/adapter/bun/websocket.test.ts > upgradeWebSocket() > Should throw error when server is null",
"src/adapter/cloudflare-pages/handler.test.ts > Adapter for Cloudflare Pages > Should not use `basePath()` if path argument is not passed",
"src/adapter/cloudflare-pages/handler.test.ts > Adapter for Cloudflare Pages > Should return 200 response",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should handle a HTTPException by returning error.getResponse()",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should handle a non-Error thrown by next()",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should handle an Error thrown by next()",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should handle an HTTPException thrown by next()",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should rethrow an Error",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should rethrow non-Error exceptions",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should return the Pages response if the middleware does not return a response",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should return the middleware response",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should return the middleware response if next() is not called",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should return the middleware response when exceptions are handled",
"src/adapter/cloudflare-pages/handler.test.ts > Middleware adapter for Cloudflare Pages > Should set the data in eventContext.data",
"src/adapter/cloudflare-pages/handler.test.ts > serveStatic() > Should pass the raw request to ASSETS.fetch",
"src/adapter/cloudflare-pages/handler.test.ts > serveStatic() > Should respond with 404 if ASSETS.fetch returns a 404 response",
"src/adapter/cloudflare-workers/serve-static.test.ts > code 1",
"src/adapter/cloudflare-workers/websocket.test.ts > upgradeWebSocket middleware > Should call next() when header does not have upgrade",
"src/adapter/deno/websocket.test.ts > WebSockets > Should call next() when header does not have upgrade",
"src/adapter/lambda-edge/handler.test.ts > code 3",
"src/adapter/service-worker/handler.test.ts > ✗ ",
"src/context.test.ts > Context > c.notFound()",
"src/context.test.ts > Context header > Should be able to overwrite a fetch response with a new response.",
"src/context.test.ts > Context header > Should be able to overwrite a response with a fetch response.",
"src/context.test.ts > event and executionCtx > Should return the event if accessing c.event",
"src/context.test.ts > event and executionCtx > Should return the executionCtx if accessing c.executionCtx",
"src/helper/adapter/index.test.ts > getRuntimeKey > Should return the current runtime key",
"src/helper/dev/index.test.ts > ✗ ",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > POST request",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > client disconnect",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > compressed",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > does not propagate undefined request headers",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > modify header",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > not found",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > remove hop-by-hop headers",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > specify hop-by-hop header by options",
"src/helper/proxy/index.test.ts > Proxy Middleware > proxy > uncompressed",
"src/helper/proxy/index.test.ts > ✗ ",
"src/helper/streaming/stream.test.ts > Basic Streaming Helper > Check stream Response if aborted by abort signal",
"src/helper/streaming/stream.test.ts > Basic Streaming Helper > Check stream Response if error occurred",
"src/helper/streaming/stream.test.ts > Basic Streaming Helper > Check stream Response if pipe is aborted by abort signal",
"src/helper/testing/index.test.ts > hono testClient > Should not throw an error with $ws()",
"src/helper/websocket/index.test.ts > defineWebSocketHelper > When response is undefined, should call next()",
"src/jsx/utils.test.ts > styleObjectForEach > Should output the number as it is, when a number type is passed > $property",
"src/jsx/utils.test.ts > styleObjectForEach > Should output with px suffix, when a number type is passed > $property",
"src/middleware/body-limit/index.test.ts > Body Limit Middleware > POST request > ReadableStream body > should return 413 response",
"src/middleware/cache/index.test.ts > Cache Middleware > Should correctly apply a single Vary header from middleware",
"src/middleware/cache/index.test.ts > Cache Middleware > Should correctly apply and return a single Vary header with Accept specified by middleware",
"src/middleware/cache/index.test.ts > Cache Middleware > Should deduplicate while merging multiple Vary headers from middleware and handler",
"src/middleware/cache/index.test.ts > Cache Middleware > Should merge Vary headers from middleware and handler without duplicating",
"src/middleware/cache/index.test.ts > Cache Middleware > Should merge Vary headers specified by middleware as a string with additional headers added by handler",
"src/middleware/cache/index.test.ts > Cache Middleware > Should not allow \"*\" as a Vary header in middleware configuration due to its impact on caching effectiveness",
"src/middleware/cache/index.test.ts > Cache Middleware > Should not be enabled if caches is not defined",
"src/middleware/cache/index.test.ts > Cache Middleware > Should not return cached response",
"src/middleware/cache/index.test.ts > Cache Middleware > Should not return duplicate header values",
"src/middleware/cache/index.test.ts > Cache Middleware > Should prevent duplication of Vary headers when identical ones are set by both middleware and handler",
"src/middleware/cache/index.test.ts > Cache Middleware > Should return cached response",
"src/middleware/cache/index.test.ts > Cache Middleware > Should return composed handler header values",
"src/middleware/cache/index.test.ts > Cache Middleware > Should return composed middleware header values",
"src/middleware/cache/index.test.ts > Customizing Caching Keys > Should retrieve cached response with dynamic cache name and key",
"src/middleware/cache/index.test.ts > Customizing Caching Keys > Should use dynamically generated cache key",
"src/middleware/cache/index.test.ts > Customizing Caching Keys > Should use dynamically generated cache name",
"src/middleware/combine/index.test.ts > every > Should call all middleware",
"src/middleware/combine/index.test.ts > every > Should return the same response a middleware returns if it short-circuits the chain",
"src/middleware/combine/index.test.ts > every > Should throw error if any middleware returns false",
"src/middleware/combine/index.test.ts > every > Should throw error if any middleware throws an error",
"src/middleware/combine/index.test.ts > except > Should call all middleware, except the one that matches some of the condition function",
"src/middleware/combine/index.test.ts > except > Should call all middleware, except the one that matches some of the conditions",
"src/middleware/combine/index.test.ts > except > Should call all middleware, except the one that matches the condition",
"src/middleware/combine/index.test.ts > some > Should call only the first middleware",
"src/middleware/combine/index.test.ts > some > Should not call skipped middleware even if an error is thrown",
"src/middleware/combine/index.test.ts > some > Should not call skipped middleware even if an error is thrown with returning truthy value middleware",
"src/middleware/combine/index.test.ts > some > Should try to call the second middleware if the first one returns false",
"src/middleware/combine/index.test.ts > some > Should try to call the second middleware if the first one throws an error",
"src/middleware/compress/index.test.ts > Compress Middleware > Compression Behavior > should compress large responses with deflate",
"src/middleware/compress/index.test.ts > Compress Middleware > Compression Behavior > should compress large responses with gzip",
"src/middleware/compress/index.test.ts > Compress Middleware > Compression Behavior > should prioritize gzip over deflate when both are accepted",
"src/middleware/compress/index.test.ts > Compress Middleware > Compression Behavior > should remove Content-Length when compressing",
"src/middleware/compress/index.test.ts > Compress Middleware > Edge Cases > should compress custom 404 Not Found responses",
"src/middleware/compress/index.test.ts > Compress Middleware > JSON Handling > should compress large JSON responses",
"src/middleware/compress/index.test.ts > Compress Middleware > Streaming Responses > should compress streaming responses written in multiple chunks",
"src/middleware/language/index.test.ts > languageDetector > Debug Mode > should log debug information when enabled",
"src/middleware/language/index.test.ts > languageDetector > Debug Mode > should log errors in debug mode",
"src/middleware/language/index.test.ts > languageDetector > Error Handling > should fall back to default language on error",
"src/middleware/logger/index.test.ts > Logger by Middleware in NO_COLOR > Log status 200 with big body",
"src/middleware/logger/index.test.ts > Logger by Middleware in NO_COLOR > Log status 200 with empty body",
"src/middleware/logger/index.test.ts > Logger by Middleware in NO_COLOR > Log status 200 with small body",
"src/middleware/logger/index.test.ts > Logger by Middleware in NO_COLOR > Log status 404",
"src/middleware/logger/index.test.ts > Logger by Middleware in NO_COLOR > Time in seconds",
"src/middleware/logger/index.test.ts > ✗ ",
"src/middleware/method-override/index.test.ts > Method Override Middleware > Header > Should override POST to DELETE",
"src/middleware/method-override/index.test.ts > SIGTRAP",
"src/middleware/request-id/index.test.ts > Request ID Middleware > Should return random request id",
"src/middleware/serve-static/index.test.ts > code 1",
"src/middleware/timing/index.test.ts > Server-Timing API > Should not be enabled if the main app has the timing middleware",
"src/request.test.ts > Body methods with caching > req.parseBody() > Return type > specify return type explicitly",
"src/request.test.ts > Body methods with caching > req.parseBody() > Return type > without options",
"src/request.test.ts > Body methods with caching > req.parseBody() > Return type > {all: true, dot: true}",
"src/request.test.ts > Body methods with caching > req.parseBody() > Return type > {all: true}",
"src/request.test.ts > Body methods with caching > req.parseBody() > Return type > {dot: true}",
"src/router/linear-router/router.test.ts > LinearRouter > Common > ALL and Star > Should return /x and star",
"src/router/linear-router/router.test.ts > LinearRouter > Common > ALL star, ALL star, GET static, ALL star... > Should return wildcard, star2 and bar",
"src/router/linear-router/router.test.ts > LinearRouter > Common > All > GET, all hello",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Basic Usage > GET, post hello",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Complex capturing group > GET request",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group > GET /foo/bar",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group > GET /foo/baz",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group > GET /foo/qux",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/bar",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/baz",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/qux",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Simple capturing group > GET /foo/bar",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Simple capturing group > GET /foo/baz",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture Group > Simple capturing group > GET /foo/qux",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture complex multiple directories > GET /part1/middle-b/end-c/latest",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture complex multiple directories > GET /part1/middle-b/latest",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents/123",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture regex pattern has trailing wildcard > GET /foo/bar/file.html",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Capture simple multiple directories > GET /foo/bar/file.html",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Complex > /*",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Complex > Default",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Complex > Named Param",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Complex > Regexp",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Complex > Wildcard",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Duplicate param name > child",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Duplicate param name > parent",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Duplicate param name > self",
"src/router/linear-router/router.test.ts > LinearRouter > Common > GET star, ALL static, GET star... > Should return /x and star",
"src/router/linear-router/router.test.ts > LinearRouter > Common > GET star, GET static, ALL star... > Should return star1, star2, and bar",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Including slashes > GET /js/chunk/123.js",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Including slashes > GET /js/chunk/nest/123.js",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Including slashes > GET /js/main.js",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > Blog > DELETE /entry",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > Blog > GET /",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > Blog > GET /entry/123",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > Blog > GET /entry/123/comment/456",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > Blog > POST /entry",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > `params` per a handler > GET /entry/123/show",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Multi match > hierarchy",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Optional route > GET /api/animals",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Optional route > GET /api/animals/dog",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Optional route > GET /v1",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Optional route > GET /v1/123",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Optional route > GET /v1/123/abc",
"src/router/linear-router/router.test.ts > LinearRouter > Common > REST API > GET /users/hono",
"src/router/linear-router/router.test.ts > LinearRouter > Common > REST API > GET /users/hono/posts",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Registration order > handler -> fallback",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Registration order > middleware -> handler",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Reserved words > Reserved words and named parameter",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Reserved words > Reserved words and optional named parameter",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Reserved words > Reserved words and wildcard",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Routing with a hostname > GET /hello",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Routing with a hostname > GET www1.example.com/hello",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Routing with a hostname > GET www2.example.com/hello",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Same path > GET /hey",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Trailing slash > GET /book",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Trailing slash > GET /book/",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Unknown method > UNKNOWN_METHOD /",
"src/router/linear-router/router.test.ts > LinearRouter > Common > Unknown method > UNKNOWN_METHOD /all",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > ALL and GET > GET /",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix/test",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > GET only > GET /",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > GET only > GET /long/prefix",
"src/router/linear-router/router.test.ts > LinearRouter > Common > long prefix, then star > GET only > GET /long/prefix/test",
"src/router/linear-router/router.test.ts > LinearRouter > Common > non ascii characters > GET /$/hono",
"src/router/linear-router/router.test.ts > LinearRouter > Common > non ascii characters > GET /()/hono",
"src/router/linear-router/router.test.ts > LinearRouter > Common > page > GET /page",
"src/router/linear-router/router.test.ts > LinearRouter > Common > star > Under a certain path",
"src/router/linear-router/router.test.ts > LinearRouter > Common > star > top",
"src/router/linear-router/router.test.ts > LinearRouter > Common > static routes of ALL and GET > get /foo",
"src/router/linear-router/router.test.ts > ✗ ",
"src/router/pattern-router/router.test.ts > Pattern > Common > ALL and Star > Should return /x and star",
"src/router/pattern-router/router.test.ts > Pattern > Common > ALL star, ALL star, GET static, ALL star... > Should return wildcard, star2 and bar",
"src/router/pattern-router/router.test.ts > Pattern > Common > All > GET, all hello",
"src/router/pattern-router/router.test.ts > Pattern > Common > Basic Usage > GET, post hello",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Complex capturing group > GET request",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group > GET /foo/bar",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group > GET /foo/baz",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group > GET /foo/qux",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group with prefix > GET /foo/bar",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group with prefix > GET /foo/baz",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Non-capturing group with prefix > GET /foo/qux",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Simple capturing group > GET /foo/bar",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Simple capturing group > GET /foo/baz",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture Group > Simple capturing group > GET /foo/qux",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture complex multiple directories > GET /part1/middle-b/end-c/latest",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture complex multiple directories > GET /part1/middle-b/latest",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture multiple directories and optional > GET /foo/bar/contents",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture multiple directories and optional > GET /foo/bar/contents/123",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture regex pattern has trailing wildcard > GET /foo/bar/file.html",
"src/router/pattern-router/router.test.ts > Pattern > Common > Capture simple multiple directories > GET /foo/bar/file.html",
"src/router/pattern-router/router.test.ts > Pattern > Common > Complex > /*",
"src/router/pattern-router/router.test.ts > Pattern > Common > Complex > Default",
"src/router/pattern-router/router.test.ts > Pattern > Common > Complex > Named Param",
"src/router/pattern-router/router.test.ts > Pattern > Common > Complex > Regexp",
"src/router/pattern-router/router.test.ts > Pattern > Common > Complex > Wildcard",
"src/router/pattern-router/router.test.ts > Pattern > Common > Duplicate param name > child",
"src/router/pattern-router/router.test.ts > Pattern > Common > Duplicate param name > parent",
"src/router/pattern-router/router.test.ts > Pattern > Common > Duplicate param name > self",
"src/router/pattern-router/router.test.ts > Pattern > Common > GET star, ALL static, GET star... > Should return /x and star",
"src/router/pattern-router/router.test.ts > Pattern > Common > GET star, GET static, ALL star... > Should return star1, star2, and bar",
"src/router/pattern-router/router.test.ts > Pattern > Common > Including slashes > GET /js/chunk/123.js",
"src/router/pattern-router/router.test.ts > Pattern > Common > Including slashes > GET /js/chunk/nest/123.js",
"src/router/pattern-router/router.test.ts > Pattern > Common > Including slashes > GET /js/main.js",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > Blog > DELETE /entry",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > Blog > GET /",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > Blog > GET /entry/123",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > Blog > GET /entry/123/comment/456",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > Blog > POST /entry",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > `params` per a handler > GET /entry/123/show",
"src/router/pattern-router/router.test.ts > Pattern > Common > Multi match > hierarchy",
"src/router/pattern-router/router.test.ts > Pattern > Common > Optional route > GET /api/animals",
"src/router/pattern-router/router.test.ts > Pattern > Common > Optional route > GET /api/animals/dog",
"src/router/pattern-router/router.test.ts > Pattern > Common > Optional route > GET /v1",
"src/router/pattern-router/router.test.ts > Pattern > Common > Optional route > GET /v1/123",
"src/router/pattern-router/router.test.ts > Pattern > Common > Optional route > GET /v1/123/abc",
"src/router/pattern-router/router.test.ts > Pattern > Common > REST API > GET /users/hono",
"src/router/pattern-router/router.test.ts > Pattern > Common > REST API > GET /users/hono/posts",
"src/router/pattern-router/router.test.ts > Pattern > Common > Registration order > handler -> fallback",
"src/router/pattern-router/router.test.ts > Pattern > Common > Registration order > middleware -> handler",
"src/router/pattern-router/router.test.ts > Pattern > Common > Reserved words > Reserved words and named parameter",
"src/router/pattern-router/router.test.ts > Pattern > Common > Reserved words > Reserved words and optional named parameter",
"src/router/pattern-router/router.test.ts > Pattern > Common > Reserved words > Reserved words and wildcard",
"src/router/pattern-router/router.test.ts > Pattern > Common > Routing with a hostname > GET /hello",
"src/router/pattern-router/router.test.ts > Pattern > Common > Routing with a hostname > GET www1.example.com/hello",
"src/router/pattern-router/router.test.ts > Pattern > Common > Routing with a hostname > GET www2.example.com/hello",
"src/router/pattern-router/router.test.ts > Pattern > Common > Same path > GET /hey",
"src/router/pattern-router/router.test.ts > Pattern > Common > Trailing slash > GET /book",
"src/router/pattern-router/router.test.ts > Pattern > Common > Trailing slash > GET /book/",
"src/router/pattern-router/router.test.ts > Pattern > Common > Unknown method > UNKNOWN_METHOD /",
"src/router/pattern-router/router.test.ts > Pattern > Common > Unknown method > UNKNOWN_METHOD /all",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > ALL and GET > GET /",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > ALL and GET > GET /long/prefix",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > ALL and GET > GET /long/prefix/test",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > GET only > GET /",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > GET only > GET /long/prefix",
"src/router/pattern-router/router.test.ts > Pattern > Common > long prefix, then star > GET only > GET /long/prefix/test",
"src/router/pattern-router/router.test.ts > Pattern > Common > non ascii characters > GET /$/hono",
"src/router/pattern-router/router.test.ts > Pattern > Common > non ascii characters > GET /()/hono",
"src/router/pattern-router/router.test.ts > Pattern > Common > page > GET /page",
"src/router/pattern-router/router.test.ts > Pattern > Common > star > Under a certain path",
"src/router/pattern-router/router.test.ts > Pattern > Common > star > top",
"src/router/pattern-router/router.test.ts > Pattern > Common > static routes of ALL and GET > get /foo",
"src/router/pattern-router/router.test.ts > ✗ ",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > ALL and Star > Should return /x and star",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > ALL star, ALL star, GET static, ALL star... > Should return wildcard, star2 and bar",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > All > GET, all hello",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Basic Usage > GET, post hello",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Complex capturing group > GET request",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group > GET /foo/bar",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group > GET /foo/baz",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group > GET /foo/qux",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/bar",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/baz",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/qux",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Simple capturing group > GET /foo/bar",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Simple capturing group > GET /foo/baz",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture Group > Simple capturing group > GET /foo/qux",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture complex multiple directories > GET /part1/middle-b/end-c/latest",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture complex multiple directories > GET /part1/middle-b/latest",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents/123",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture regex pattern has trailing wildcard > GET /foo/bar/file.html",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Capture simple multiple directories > GET /foo/bar/file.html",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Complex > /*",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Complex > Default",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Complex > Named Param",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Complex > Regexp",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Complex > Wildcard",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Duplicate param name > child",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Duplicate param name > parent",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Duplicate param name > self",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > GET star, ALL static, GET star... > Should return /x and star",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > GET star, GET static, ALL star... > Should return star1, star2, and bar",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Including slashes > GET /js/chunk/123.js",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Including slashes > GET /js/chunk/nest/123.js",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Including slashes > GET /js/main.js",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > Blog > DELETE /entry",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > Blog > GET /",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > Blog > GET /entry/123",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > Blog > GET /entry/123/comment/456",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > Blog > POST /entry",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > `params` per a handler > GET /entry/123/show",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Multi match > hierarchy",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Optional route > GET /api/animals",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Optional route > GET /api/animals/dog",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Optional route > GET /v1",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Optional route > GET /v1/123",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Optional route > GET /v1/123/abc",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > REST API > GET /users/hono",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > REST API > GET /users/hono/posts",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Registration order > handler -> fallback",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Registration order > middleware -> handler",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Reserved words > Reserved words and named parameter",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Reserved words > Reserved words and optional named parameter",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Reserved words > Reserved words and wildcard",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Routing with a hostname > GET /hello",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Routing with a hostname > GET www1.example.com/hello",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Routing with a hostname > GET www2.example.com/hello",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Same path > GET /hey",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Trailing slash > GET /book",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Trailing slash > GET /book/",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Unknown method > UNKNOWN_METHOD /",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > Unknown method > UNKNOWN_METHOD /all",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > ALL and GET > GET /",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix/test",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > GET only > GET /",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > GET only > GET /long/prefix",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > long prefix, then star > GET only > GET /long/prefix/test",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > non ascii characters > GET /$/hono",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > non ascii characters > GET /()/hono",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > page > GET /page",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > star > Under a certain path",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > star > top",
"src/router/reg-exp-router/router.test.ts > RegExpRouter > Common > static routes of ALL and GET > get /foo",
"src/router/reg-exp-router/router.test.ts > ✗ ",
"src/router/smart-router/router.test.ts > SmartRouter > Common > ALL and Star > Should return /x and star",
"src/router/smart-router/router.test.ts > SmartRouter > Common > ALL star, ALL star, GET static, ALL star... > Should return wildcard, star2 and bar",
"src/router/smart-router/router.test.ts > SmartRouter > Common > All > GET, all hello",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Basic Usage > GET, post hello",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Complex capturing group > GET request",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group > GET /foo/bar",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group > GET /foo/baz",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group > GET /foo/qux",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/bar",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/baz",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/qux",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Simple capturing group > GET /foo/bar",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Simple capturing group > GET /foo/baz",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture Group > Simple capturing group > GET /foo/qux",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture complex multiple directories > GET /part1/middle-b/end-c/latest",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture complex multiple directories > GET /part1/middle-b/latest",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents/123",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture regex pattern has trailing wildcard > GET /foo/bar/file.html",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Capture simple multiple directories > GET /foo/bar/file.html",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Complex > /*",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Complex > Default",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Complex > Named Param",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Complex > Regexp",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Complex > Wildcard",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Duplicate param name > child",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Duplicate param name > parent",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Duplicate param name > self",
"src/router/smart-router/router.test.ts > SmartRouter > Common > GET star, ALL static, GET star... > Should return /x and star",
"src/router/smart-router/router.test.ts > SmartRouter > Common > GET star, GET static, ALL star... > Should return star1, star2, and bar",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Including slashes > GET /js/chunk/123.js",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Including slashes > GET /js/chunk/nest/123.js",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Including slashes > GET /js/main.js",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > Blog > DELETE /entry",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > Blog > GET /",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > Blog > GET /entry/123",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > Blog > GET /entry/123/comment/456",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > Blog > POST /entry",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > `params` per a handler > GET /entry/123/show",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Multi match > hierarchy",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Optional route > GET /api/animals",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Optional route > GET /api/animals/dog",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Optional route > GET /v1",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Optional route > GET /v1/123",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Optional route > GET /v1/123/abc",
"src/router/smart-router/router.test.ts > SmartRouter > Common > REST API > GET /users/hono",
"src/router/smart-router/router.test.ts > SmartRouter > Common > REST API > GET /users/hono/posts",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Registration order > handler -> fallback",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Registration order > middleware -> handler",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Reserved words > Reserved words and named parameter",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Reserved words > Reserved words and optional named parameter",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Reserved words > Reserved words and wildcard",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Routing with a hostname > GET /hello",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Routing with a hostname > GET www1.example.com/hello",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Routing with a hostname > GET www2.example.com/hello",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Same path > GET /hey",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Trailing slash > GET /book",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Trailing slash > GET /book/",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Unknown method > UNKNOWN_METHOD /",
"src/router/smart-router/router.test.ts > SmartRouter > Common > Unknown method > UNKNOWN_METHOD /all",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > ALL and GET > GET /",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix/test",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > GET only > GET /",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > GET only > GET /long/prefix",
"src/router/smart-router/router.test.ts > SmartRouter > Common > long prefix, then star > GET only > GET /long/prefix/test",
"src/router/smart-router/router.test.ts > SmartRouter > Common > non ascii characters > GET /$/hono",
"src/router/smart-router/router.test.ts > SmartRouter > Common > non ascii characters > GET /()/hono",
"src/router/smart-router/router.test.ts > SmartRouter > Common > page > GET /page",
"src/router/smart-router/router.test.ts > SmartRouter > Common > star > Under a certain path",
"src/router/smart-router/router.test.ts > SmartRouter > Common > star > top",
"src/router/smart-router/router.test.ts > SmartRouter > Common > static routes of ALL and GET > get /foo",
"src/router/smart-router/router.test.ts > ✗ ",
"src/router/trie-router/router.test.ts > TrieRouter > Common > ALL and Star > Should return /x and star",
"src/router/trie-router/router.test.ts > TrieRouter > Common > ALL star, ALL star, GET static, ALL star... > Should return wildcard, star2 and bar",
"src/router/trie-router/router.test.ts > TrieRouter > Common > All > GET, all hello",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Basic Usage > GET, post hello",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Complex capturing group > GET request",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group > GET /foo/bar",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group > GET /foo/baz",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group > GET /foo/qux",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/bar",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/baz",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Non-capturing group with prefix > GET /foo/qux",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Simple capturing group > GET /foo/bar",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Simple capturing group > GET /foo/baz",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture Group > Simple capturing group > GET /foo/qux",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture complex multiple directories > GET /part1/middle-b/end-c/latest",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture complex multiple directories > GET /part1/middle-b/latest",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture multiple directories and optional > GET /foo/bar/contents/123",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture regex pattern has trailing wildcard > GET /foo/bar/file.html",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Capture simple multiple directories > GET /foo/bar/file.html",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Complex > /*",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Complex > Default",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Complex > Named Param",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Complex > Regexp",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Complex > Wildcard",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Duplicate param name > child",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Duplicate param name > parent",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Duplicate param name > self",
"src/router/trie-router/router.test.ts > TrieRouter > Common > GET star, ALL static, GET star... > Should return /x and star",
"src/router/trie-router/router.test.ts > TrieRouter > Common > GET star, GET static, ALL star... > Should return star1, star2, and bar",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Including slashes > GET /js/chunk/123.js",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Including slashes > GET /js/chunk/nest/123.js",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Including slashes > GET /js/main.js",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > Blog > DELETE /entry",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > Blog > GET /",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > Blog > GET /entry/123",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > Blog > GET /entry/123/comment/456",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > Blog > POST /entry",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > `params` per a handler > GET /entry/123/show",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Multi match > hierarchy",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Optional route > GET /api/animals",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Optional route > GET /api/animals/dog",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Optional route > GET /v1",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Optional route > GET /v1/123",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Optional route > GET /v1/123/abc",
"src/router/trie-router/router.test.ts > TrieRouter > Common > REST API > GET /users/hono",
"src/router/trie-router/router.test.ts > TrieRouter > Common > REST API > GET /users/hono/posts",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Registration order > handler -> fallback",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Registration order > middleware -> handler",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Reserved words > Reserved words and named parameter",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Reserved words > Reserved words and optional named parameter",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Reserved words > Reserved words and wildcard",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Routing with a hostname > GET /hello",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Routing with a hostname > GET www1.example.com/hello",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Routing with a hostname > GET www2.example.com/hello",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Same path > GET /hey",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Trailing slash > GET /book",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Trailing slash > GET /book/",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Unknown method > UNKNOWN_METHOD /",
"src/router/trie-router/router.test.ts > TrieRouter > Common > Unknown method > UNKNOWN_METHOD /all",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > ALL and GET > GET /",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > ALL and GET > GET /long/prefix/test",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > GET only > GET /",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > GET only > GET /long/prefix",
"src/router/trie-router/router.test.ts > TrieRouter > Common > long prefix, then star > GET only > GET /long/prefix/test",
"src/router/trie-router/router.test.ts > TrieRouter > Common > non ascii characters > GET /$/hono",
"src/router/trie-router/router.test.ts > TrieRouter > Common > non ascii characters > GET /()/hono",
"src/router/trie-router/router.test.ts > TrieRouter > Common > page > GET /page",
"src/router/trie-router/router.test.ts > TrieRouter > Common > star > Under a certain path",
"src/router/trie-router/router.test.ts > TrieRouter > Common > star > top",
"src/router/trie-router/router.test.ts > TrieRouter > Common > static routes of ALL and GET > get /foo",
"src/router/trie-router/router.test.ts > ✗ ",
"src/utils/body.test.ts > BodyData > without options",
"src/utils/body.test.ts > BodyData > {all: boolean, dot: boolean}",
"src/utils/body.test.ts > BodyData > {all: boolean}",
"src/utils/body.test.ts > BodyData > {all: true, dot: true}",
"src/utils/body.test.ts > BodyData > {all: true}",
"src/utils/body.test.ts > BodyData > {dot: boolean}",
"src/utils/body.test.ts > BodyData > {dot: true}",
"src/utils/body.test.ts > Parse Body Util > Return type > specify return type explicitly",
"src/utils/body.test.ts > Parse Body Util > Return type > without options",
"src/utils/body.test.ts > Parse Body Util > Return type > {all: boolean, dot: boolean}",
"src/utils/body.test.ts > Parse Body Util > Return type > {all: boolean}",
"src/utils/body.test.ts > Parse Body Util > Return type > {all: true, dot: true}",
"src/utils/body.test.ts > Parse Body Util > Return type > {all: true}",
"src/utils/body.test.ts > Parse Body Util > Return type > {dot: boolean}",
"src/utils/body.test.ts > Parse Body Util > Return type > {dot: true}",
"src/utils/body.test.ts > Parse Body Util > should not update file object properties",
"src/utils/color.test.ts > ✗ ",
"src/utils/concurrent.test.ts > concurrent execution > concurrency $concurrency, count $count",
"src/utils/ipaddr.test.ts > convertIPv4ToString > convertIPv4ToString($input) === $expected",
"src/utils/ipaddr.test.ts > convertIPv6ToString > convertIPv6ToString($input) === $expected",
"src/utils/stream.test.ts > StreamingApi > abort()",
"src/utils/stream.test.ts > StreamingApi > onAbort()",
"src/utils/types.test.ts > JSONParsed > Set/Map > should convert Map to empty object",
"src/utils/types.test.ts > JSONParsed > Set/Map > should convert Set to empty object",
"src/utils/types.test.ts > JSONParsed > Should parse a complex interface",
"src/utils/types.test.ts > JSONParsed > Should parse a complex type",
"src/utils/types.test.ts > JSONParsed > array > should convert (T | undefined)[] type to JSONParsedT | null>[]",
"src/utils/types.test.ts > JSONParsed > array > should convert Function[] type to null[]",
"src/utils/types.test.ts > JSONParsed > array > should convert symbol[] type to null[]",
"src/utils/types.test.ts > JSONParsed > array > should convert undefined[] type to null[]",
"src/utils/types.test.ts > JSONParsed > array > should convert { key: readonly T[]} correctly",
"src/utils/types.test.ts > JSONParsed > invalid types > should convert function type to never",
"src/utils/types.test.ts > JSONParsed > invalid types > should convert symbol type to never",
"src/utils/types.test.ts > JSONParsed > invalid types > should convert undefined type to never",
"src/utils/types.test.ts > JSONParsed > object > should convert T | undefined to T | undefined",
"src/utils/types.test.ts > JSONParsed > object > should omit keys with function value",
"src/utils/types.test.ts > JSONParsed > object > should omit keys with invalid union",
"src/utils/types.test.ts > JSONParsed > object > should omit keys with symbol value",
"src/utils/types.test.ts > JSONParsed > object > should omit keys with undefined value",
"src/utils/types.test.ts > JSONParsed > object > should omit symbol keys",
"src/utils/types.test.ts > JSONParsed > primitives > should convert boolean type to boolean",
"src/utils/types.test.ts > JSONParsed > primitives > should convert null type to null",
"src/utils/types.test.ts > JSONParsed > primitives > should convert number type to number",
"src/utils/types.test.ts > JSONParsed > primitives > should convert string type to string",
"src/utils/types.test.ts > JSONParsed > toJSON > should convert { a: { toJSON() => T } } to { a: T }",
"src/utils/types.test.ts > JSONParsed > toJSON > should convert { toJSON() => T } to T",
"src/utils/types.test.ts > JSONParsed > toJSON > toJSON is not called recursively",
"src/utils/types.test.ts > JSONParsed > tuple > should convert [T, S] type to [T, S]",
"src/utils/types.test.ts > JSONParsed > tuple > should convert [T, undefined] type to [T, null]",
"src/utils/url.test.ts > url > getPattern > regex pattern with next",
"src/validator/validator.test.ts > Validator with using Zod directly > Should exclude Response & TypedResponse type"
]
}
]
]