test(server): add http1 request and path parsing coverage

This commit is contained in:
2026-02-27 13:44:24 +02:00
parent 4b2324d131
commit 2cfe85ad82

View File

@@ -3,7 +3,6 @@ use common::prelude::*;
use http_body_util::Full;
use hyper::{
Method, Request, Response, StatusCode,
body::Incoming,
header::{ALLOW, CONNECTION, CONTENT_LENGTH, CONTENT_TYPE, HeaderValue},
server::conn::http1::Builder,
service::service_fn,
@@ -57,7 +56,7 @@ pub async fn handle_http1_connection(
}
}
fn handle_request(req: &Request<Incoming>) -> Response<RespBody> {
fn handle_request<B>(req: &Request<B>) -> Response<RespBody> {
if req.method() != Method::GET {
let mut response = text_response(StatusCode::METHOD_NOT_ALLOWED, "method not allowed");
response
@@ -129,3 +128,180 @@ fn text_response(status: StatusCode, msg: &'static str) -> Response<RespBody> {
.insert(CONNECTION, HeaderValue::from_static("close"));
response
}
#[cfg(test)]
mod tests {
use super::*;
use claims::{assert_err, assert_none, assert_ok, assert_some};
use http_body_util::BodyExt;
fn make_get_request(uri: &str) -> Request<()> {
assert_ok!(Request::builder().method(Method::GET).uri(uri).body(()))
}
#[test]
fn parse_bytes_path_accepts_valid_numeric_size() {
let min_n = assert_ok!(parse_bytes_path("/bytes/0"));
assert_eq!(min_n, 0);
let n = assert_ok!(parse_bytes_path("/bytes/1024"));
assert_eq!(n, 1024);
let max_n = assert_ok!(parse_bytes_path(&format!("/bytes/{MAX_PAYLOAD_SIZE}")));
assert_eq!(max_n, MAX_PAYLOAD_SIZE);
}
#[test]
fn parse_bytes_path_rejects_non_bytes_prefix() {
let status = assert_err!(parse_bytes_path("/foo/1024"));
assert_eq!(status, StatusCode::NOT_FOUND);
}
#[test]
fn parse_bytes_path_rejects_empty_size_segment() {
let status = assert_err!(parse_bytes_path("/bytes/"));
assert_eq!(status, StatusCode::BAD_REQUEST);
}
#[test]
fn parse_bytes_path_rejects_non_numeric_size() {
let status = assert_err!(parse_bytes_path("/bytes/foo"));
assert_eq!(status, StatusCode::BAD_REQUEST);
}
#[test]
fn parse_bytes_path_rejects_nested_segments() {
let status = assert_err!(parse_bytes_path("/bytes/16/extra"));
assert_eq!(status, StatusCode::BAD_REQUEST);
}
#[test]
fn parse_bytes_path_rejects_payload_above_max() {
let status = assert_err!(parse_bytes_path(&format!(
"/bytes/{}",
MAX_PAYLOAD_SIZE + 1
)));
assert_eq!(status, StatusCode::PAYLOAD_TOO_LARGE);
}
#[test]
fn handle_request_get_bytes_returns_200_with_expected_headers() {
let req = make_get_request("/bytes/16");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::OK);
let content_type = assert_some!(resp.headers().get("content-type"));
assert_eq!(content_type, "application/octet-stream");
let content_length = assert_some!(resp.headers().get("content-length"));
assert_eq!(content_length, "16");
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
assert_none!(resp.headers().get("allow"));
}
#[tokio::test]
async fn handle_request_get_bytes_returns_body_with_requested_length() {
let req = make_get_request("/bytes/32");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::OK);
let content_length = assert_some!(resp.headers().get("content-length"));
assert_eq!(content_length, "32");
let body = assert_ok!(resp.into_body().collect().await).to_bytes();
assert_eq!(body.len(), 32);
assert_eq!(body[0], 0x00);
assert_eq!(body[1], 0x01);
assert_eq!(body[31], 0x1F);
}
#[test]
fn handle_request_get_unknown_path_returns_404() {
let req = make_get_request("/unknown");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let content_type = assert_some!(resp.headers().get("content-type"));
assert_eq!(content_type, "text/plain; charset=utf-8");
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
#[test]
fn handle_request_post_bytes_returns_405_and_allow_get() {
let req = Request::builder()
.method(Method::POST)
.uri("/bytes/32")
.body(())
.expect("post request");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let allow = assert_some!(resp.headers().get("allow"));
assert_eq!(allow, "GET");
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
#[test]
fn handle_request_get_bytes_without_size_segment_returns_404() {
let req = make_get_request("/bytes");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
assert_none!(resp.headers().get("content-length"));
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
#[test]
fn handle_request_get_bytes_with_non_numeric_size_returns_400() {
let req = make_get_request("/bytes/foo");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
assert_none!(resp.headers().get("content-length"));
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
#[test]
fn handle_request_get_bytes_with_nested_path_returns_400() {
let req = make_get_request("/bytes/16/extra");
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
assert_none!(resp.headers().get("content-length"));
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
#[test]
fn handle_request_get_bytes_exceeding_max_payload_returns_413() {
let req = make_get_request(&format!("/bytes/{}", MAX_PAYLOAD_SIZE + 1));
let resp = handle_request(&req);
assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
assert_none!(resp.headers().get("content-length"));
let connection = assert_some!(resp.headers().get("connection"));
assert_eq!(connection, "close");
}
}