Testing Standards
SkillDev toolsLets your agent write and review tests for the Static Web Server project, including unit tests, fixtures, and mocking.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Testing Standards skill
About this capability
Write or review tests for the Static Web Server (SWS) project — unit tests, integration tests, test fixtures, and mocking strategies
What this skill tells your AI
The instructions your AI receives, as published by static-web-server/static-web-server in .agents/skills/testing/SKILL.md and read by ahel’s review.
Load this skill when writing, reviewing, or organizing tests — unit, integration, or fixture-based tests.
When to load: adding or editing any #[test] / #[tokio::test], creating a new file under tests/, adding fixtures under tests/fixtures/, or reviewing a PR that changes test coverage.
Testing Philosophy
- Test behavior, not implementation: Assert on HTTP status codes, response headers, and body content — not internal state. If refactoring without changing behavior breaks a test, the test is wrong
- One scenario per test: Each test verifies one request scenario. Multiple asserts are fine if they check the same logical outcome
- Tests are documentation: A test name describes the expected behavior.
compression_static_file_existsis better thantest_compression_1 - Fast feedback: Unit tests < 1ms. Integration tests < 100ms
Rust Testing
Unit Tests
- Location:
#[cfg(test)] mod tests { ... }at the bottom of each source file. SWS follows this convention throughoutsrc/ - Naming:
fn feature_scenario_description() - Structure: Arrange (setup response/fixture) → Act (call function) → Assert (check headers, status, body)
- Cover edge cases: Empty input, maximum input, invalid input, boundary values, unsupported methods
- Use
assert_eq!andassert!: Prefer specific assertions over rawassert!
Integration Tests
- Location:
tests/directory at the crate root - Scope: Each file tests one user-visible feature (e.g.,
tests/compression.rs,tests/cors.rs,tests/dir_listing.rs) - Test against real file fixtures: Use
tests/fixtures/public/for test files. Add new fixtures when testing new scenarios - Use the fixture infrastructure: Import from
static_web_server::testing::fixtures:fixture_settings("toml/handler_fixtures.toml")— load TOML configfixture_req_handler_opts(general, advanced)— build handler optionsfixture_req_handler(opts)— create a request handler
- Test with different HTTP methods: Loop over GET, HEAD, OPTIONS and assert correct behavior per method
Handler Tests
SWS's most common test pattern: create a handler, send a synthetic request, assert on the response:
use std::net::SocketAddr;
use hyper::{Method, Request, header::ACCEPT_ENCODING};
use static_web_server::testing::fixtures::*;
use static_web_server::settings::cli::General;
#[tokio::test]
async fn compression_static_file_exists() {
let opts = fixture_settings("toml/handler_fixtures.toml");
let general = General {
compression_static: true,
..opts.general
};
let req_handler_opts = fixture_req_handler_opts(general, opts.advanced);
let req_handler = fixture_req_handler(req_handler_opts);
let remote_addr: Option<SocketAddr> = Some(REMOTE_ADDR.parse().unwrap());
let mut req = Request::new(());
*req.method_mut() = Method::GET;
*req.uri_mut() = "http://localhost/index.htm".parse().unwrap();
req.headers_mut().insert(ACCEPT_ENCODING, "gzip, deflate, br".parse().unwrap());
match req_handler.handle(&mut req, remote_addr).await {
Ok(res) => {
assert_eq!(res.status(), 200);
assert_eq!(res.headers()["content-encoding"], "br");
assert_eq!(res.headers()["vary"], "accept-encoding");
}
Err(err) => panic!("unexpected error: {err}"),
}
}
REMOTE_ADDR ("127.0.0.1:1234") is exported from static_web_server::testing::fixtures.
Static File Tests
Tests in tests/static_files.rs call static_files::handle() directly with a HandleOpts struct. This tests the file-serving logic in isolation (without the full handler pipeline):
let result = static_files::handle(&HandleOpts {
method: &Method::GET,
headers: &HeaderMap::new(),
base_path: &root_dir(),
uri_path: "index.htm",
index_files: &["index.htm"],
// ... other opts
}).await;
Cleaning Up
Integration tests using pre-existing fixtures under tests/fixtures/ are read-only and need no cleanup. If a test creates temporary files (e.g., a temp upload directory), clean them up in a Drop handler or #[tokio::test] teardown step.
Test Fixture Organization
tests/
fixtures/
public/ # Default test file tree
index.html
404.html
assets/
main.css
main.css.zst # Pre-compressed variant for static compression tests
compression/ # Compression-specific test fixtures
markdown/ # Markdown content-negotiation test fixtures
toml/ # TOML config files for handler tests
tls/ # TLS certificate/key test fixtures
What to Test
- Always test: Public API surface, error cases, edge cases, HTTP status codes, response headers, supported/unsupported methods
- Sometimes test: Private functions with branching logic (3+ code paths) or performance-critical code (include benchmarks for the latter)
- Don't test: Trivial getters/setters, framework glue code, exact log message strings
Run Commands
# Run all tests with all features
RUSTFLAGS="--cfg tokio_unstable" cargo test --tests --features="all"
# Run a specific test
cargo test --test compression -- compression_static_file_exists
# Run with trace logging visible
RUST_LOG=trace cargo test --test static_files -- --nocapture
Checklist
- Do tests cover the happy path and at least one error path?
- Do integration tests clean up after themselves?
- Are test names descriptive?
- Are mocks used only for external dependencies (network), while filesystem access uses real test fixtures?
- Are test fixtures minimal (synthetic, small files)?
Signals
- GitHub stars
- 2k
- Forks
- 130
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Catalog kind
- skill
- Gateway key
testing-static-web-server- Source
- github.com/static-web-server/static-web-server