Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 41 additions & 25 deletions rivet-cli/tests/serve_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,38 +795,54 @@ fn test_csp_header_present() {

// ── STPA-Sec Section 12.4: Dashboard Reload Failure (H-16, SC-18) ─────────

/// POST /reload and parse the HTTP status from the response. Retries
/// once on `status == 0` — the same transient-connection-drop flake
/// class `fetch_page_with_retry` handles for GETs, observed under
/// CI runner load even though the server's health probe has already
/// passed. Used by the reload-flow tests.
fn post_reload_status(port: u16) -> u16 {
fn once(port: u16) -> u16 {
use std::io::{Read, Write};
let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{port}")) else {
return 0;
};
let _ = stream.set_read_timeout(Some(Duration::from_secs(30)));
let request = format!(
"POST /reload HTTP/1.1\r\n\
Host: 127.0.0.1:{port}\r\n\
HX-Request: true\r\n\
Content-Length: 0\r\n\
Connection: close\r\n\r\n"
);
if stream.write_all(request.as_bytes()).is_err() {
return 0;
}
let mut response = Vec::new();
let _ = stream.read_to_end(&mut response);
let response = String::from_utf8_lossy(&response).to_string();
response
.lines()
.next()
.and_then(|l| l.split_whitespace().nth(1))
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(0)
}
let first = once(port);
if first != 0 {
return first;
}
std::thread::sleep(Duration::from_millis(200));
once(port)
}

// rivet: verifies SC-18, UCA-D-4
#[test]
fn test_reload_yaml_error_returns_error_response() {
// When a reload is triggered on our valid project, the server must return
// a success response (200 or redirect) and not crash.
let (mut child, port) = start_server();

use std::io::{Read, Write};
let mut stream = std::net::TcpStream::connect(format!("127.0.0.1:{port}")).expect("connect");
stream
.set_read_timeout(Some(std::time::Duration::from_secs(30)))
.ok();

let request = format!(
"POST /reload HTTP/1.1\r\n\
Host: 127.0.0.1:{port}\r\n\
HX-Request: true\r\n\
Content-Length: 0\r\n\
Connection: close\r\n\r\n"
);
stream.write_all(request.as_bytes()).expect("write reload");

let mut response = Vec::new();
stream.read_to_end(&mut response).ok();
let response = String::from_utf8_lossy(&response).to_string();

let status = response
.lines()
.next()
.and_then(|l| l.split_whitespace().nth(1))
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(0);
let status = post_reload_status(port);

assert!(
status == 200 || (300..400).contains(&status),
Expand Down
Loading