Skip to content

Commit 84d7624

Browse files
committed
add HTTP 405 coverage and error response routing tests
1 parent b06eb08 commit 84d7624

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

stackslib/src/net/tests/httpcore.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,11 @@ fn test_http_response_type_codec() {
526526
"GET".to_string(),
527527
"/v2/neighbors".to_string(),
528528
),
529+
(
530+
StacksHttpResponse::new_empty_error(&*http_error_from_code_and_text(405, "".into())),
531+
"GET".to_string(),
532+
"/v2/neighbors".to_string(),
533+
),
529534
(
530535
StacksHttpResponse::new_empty_error(&*http_error_from_code_and_text(500, "".into())),
531536
"GET".to_string(),
@@ -567,6 +572,11 @@ fn test_http_response_type_codec() {
567572
"GET".to_string(),
568573
"/v2/neighbors".to_string(),
569574
),
575+
(
576+
StacksHttpResponse::new_empty_error(&*http_error_from_code_and_text(405, "foo".into())),
577+
"GET".to_string(),
578+
"/v2/neighbors".to_string(),
579+
),
570580
(
571581
StacksHttpResponse::new_empty_error(&*http_error_from_code_and_text(500, "foo".into())),
572582
"GET".to_string(),
@@ -656,6 +666,7 @@ fn test_http_response_type_codec() {
656666
HttpResponsePreamble::error_text(402, http_reason(402), ""),
657667
HttpResponsePreamble::error_text(403, http_reason(403), ""),
658668
HttpResponsePreamble::error_text(404, http_reason(404), ""),
669+
HttpResponsePreamble::error_text(405, http_reason(405), ""),
659670
HttpResponsePreamble::error_text(500, http_reason(500), ""),
660671
HttpResponsePreamble::error_text(503, http_reason(503), ""),
661672
// generic error
@@ -666,6 +677,7 @@ fn test_http_response_type_codec() {
666677
HttpResponsePreamble::error_text(402, http_reason(402), "foo"),
667678
HttpResponsePreamble::error_text(403, http_reason(403), "foo"),
668679
HttpResponsePreamble::error_text(404, http_reason(404), "foo"),
680+
HttpResponsePreamble::error_text(405, http_reason(405), "foo"),
669681
HttpResponsePreamble::error_text(500, http_reason(500), "foo"),
670682
HttpResponsePreamble::error_text(503, http_reason(503), "foo"),
671683
// generic error
@@ -689,7 +701,8 @@ fn test_http_response_type_codec() {
689701
test_block_info.serialize_to_vec(),
690702
test_microblock_info_bytes.clone(),
691703
Txid([0x1; 32]).to_hex().as_bytes().to_vec(),
692-
// errors
704+
// errors (400, 401, 402, 403, 404, 405, 500, 503, 502)
705+
vec![],
693706
vec![],
694707
vec![],
695708
vec![],
@@ -707,6 +720,7 @@ fn test_http_response_type_codec() {
707720
"foo".as_bytes().to_vec(),
708721
"foo".as_bytes().to_vec(),
709722
"foo".as_bytes().to_vec(),
723+
"foo".as_bytes().to_vec(),
710724
];
711725

712726
for ((test, request_verb, request_path), (expected_http_preamble, _expected_http_body)) in
@@ -1219,6 +1233,7 @@ fn test_http_response_is_success() {
12191233
(401, false), // Unauthorized
12201234
(403, false), // Forbidden
12211235
(404, false), // Not Found
1236+
(405, false), // Method Not Allowed
12221237
(418, false), // I'm a teapot
12231238
(429, false), // Too Many Requests
12241239
(499, false), // Any other 4xx
@@ -1292,3 +1307,60 @@ fn test_send_request_success() {
12921307
"Expected a successful request, but got {result:?}"
12931308
);
12941309
}
1310+
1311+
#[test]
1312+
fn test_http_error_responses() {
1313+
let valid_block_path = format!("/v3/blocks/{}", "0".repeat(64));
1314+
1315+
// (verb, path, expected_status, allow_header_should_contain)
1316+
let fixtures: Vec<(&str, &str, u16, Option<&str>)> = vec![
1317+
// 404: nonexistent path
1318+
("GET", "/nonexistent/path", 404, None),
1319+
// 405: wrong method on existing path (should have Allow header)
1320+
("DELETE", "/v2/info", 405, Some("GET")),
1321+
// 400: path structure matches but parameters invalid (permissive regex)
1322+
("GET", "/v3/blocks/invalid_block_id", 400, None),
1323+
("GET", "/v3/tenures/invalid", 400, None),
1324+
// Valid block_id format should NOT return 400
1325+
("GET", &valid_block_path, 200, None),
1326+
];
1327+
1328+
for (verb, path, expected_status, allow_contains) in fixtures {
1329+
let addr = "127.0.0.1:20443".parse().unwrap();
1330+
let mut http = StacksHttp::new(addr, &ConnectionOptions::default());
1331+
1332+
let request_data =
1333+
format!("{verb} {path} HTTP/1.1\r\nHost: localhost:20443\r\nConnection: close\r\n\r\n");
1334+
1335+
let (preamble, offset) = http.read_preamble(request_data.as_bytes()).unwrap();
1336+
let result = http.read_payload(&preamble, &request_data.as_bytes()[offset..]);
1337+
1338+
match result {
1339+
// Valid request parsed successfully
1340+
Ok((StacksHttpMessage::Request(_), _)) => {
1341+
assert_eq!(
1342+
expected_status, 200,
1343+
"{verb} {path}: parsed OK but expected {expected_status}"
1344+
);
1345+
}
1346+
// Server returned an error response (400, 404, 405, etc.)
1347+
Ok((StacksHttpMessage::Response(resp), _))
1348+
| Ok((StacksHttpMessage::Error(_, resp), _)) => {
1349+
assert_eq!(
1350+
resp.preamble().status_code,
1351+
expected_status,
1352+
"{verb} {path}: expected {expected_status}, got {}",
1353+
resp.preamble().status_code
1354+
);
1355+
1356+
// Verify Allow header is present for 405 responses
1357+
if let Some(expected_method) = allow_contains {
1358+
let allow = resp.preamble().headers.get("allow");
1359+
assert!(allow.is_some(), "{verb} {path}: missing Allow header");
1360+
assert!(allow.unwrap().contains(expected_method));
1361+
}
1362+
}
1363+
Err(e) => panic!("{verb} {path}: unexpected error {e:?}"),
1364+
}
1365+
}
1366+
}

0 commit comments

Comments
 (0)