Skip to content

Running Tests

Thunder has two categories of tests: Rust tests (unit, integration, and E2E tests written in Rust) and JS/TS tests (TypeScript test suites executed by the built-in test runner inside V8 isolates).

CommandWhat it runs
make testFast Rust tests + JS/TS tests (skips server E2E).
make test-fullFull Rust suite + JS/TS tests.
make test-jsJS/TS tests only.
make test-rust-fastFast Rust tests only (skips E2E and stress tests).
make test-rust-fullFull Rust test suite.
Terminal window
cargo test

This runs every test in every crate, including E2E tests in edge-server that start a real HTTP listener. It can take a few minutes.

Terminal window
cargo test-dev

This is a cargo alias defined in .cargo/config.toml. It runs the entire workspace but excludes the edge-server crate and skips any test whose name contains e2e or stress_. Use this for rapid iteration.

Terminal window
cargo test-full

Runs cargo test --workspace with no exclusions.

Terminal window
cargo test-server-e2e

Runs only the E2E tests in the edge-server crate with --nocapture for full output. Use this when you are working on ingress or admin API routes.

You can test individual crates in isolation:

Terminal window
cargo test -p runtime-core
cargo test -p functions
cargo test -p edge-server
cargo test -p edge-cli
Terminal window
cargo test -p runtime-core test_name_substring

Add -- --nocapture if you need to see println! / tracing output:

Terminal window
cargo test -p runtime-core test_name_substring -- --nocapture

The JS/TS test suite uses Thunder’s built-in test runner, which discovers and executes .ts test files inside V8 isolates.

Terminal window
make test-js

This is equivalent to:

Terminal window
cargo run -- test --path "./tests/js/**/*.ts" --ignore "./tests/js/lib/**"

The --ignore flag excludes shared library files under tests/js/lib/ that are imported by test files but are not test suites themselves.

Terminal window
cargo run -- test --path "./tests/js/web_apis_full.test.ts"

The tests/js/ directory contains the following test suites:

FileCoverage area
web_apis_full.test.tsFull Web API surface (fetch, URL, Headers, streams, etc.).
web_apis_partial_and_none.test.tsPartially implemented and unimplemented Web APIs.
web_apis_none_deterministic.test.tsNon-deterministic Web APIs (crypto random, Date.now).
web_platform_additional.test.tsAdditional Web Platform APIs.
web_standards_report_regression.test.tsRegression tests for the web standards report.
crypto_and_timers.test.tsWeb Crypto API and timer functions.
js_builtins_and_collections.test.tsJS built-ins (Map, Set, Proxy, Reflect, etc.).
language_engine_compat.test.tsLanguage engine compatibility (generators, async iterators, etc.).
networking_tcp_patterns.test.tsNetworking and TCP patterns.
requested_items_compat.test.tsRequested item compatibility checks.
runner_advanced_features.test.tsTest runner advanced features (describe, it, hooks).
mocking_system.test.tsBuilt-in mocking system.

Thunder’s Rust tests follow two main archetypes.

Tests that need to run a small piece of JavaScript without a full HTTP lifecycle. These create a JsRuntime directly, load a script, and assert on the output or return value. Commonly found in runtime-core.

#[test]
fn test_console_log() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
std::thread::spawn(move || {
rt.block_on(async {
// Create isolate, run JS, check output
});
})
.join()
.unwrap();
}

The std::thread::spawn wrapper is required because JsRuntime is !Send — it must stay on the thread where it was created.

Tests that exercise the complete request-handling path: function registration, isolate pool allocation, HTTP request dispatch through the handler, and response validation. Found primarily in functions and edge-server.

These tests typically start a real Hyper server on a random port, send HTTP requests, and assert on status codes, headers, and response bodies.

Each Rust test file in Thunder is self-contained: helper functions (isolate builders, request factories, assertion utilities) are duplicated within each test module rather than extracted into a shared test crate. This is intentional — it avoids coupling between test files and makes each file independently understandable.