test(unwrap): clean server integration helpers

This commit is contained in:
Bryan Helmkamp 2026-04-19 21:06:36 -04:00
parent 0b91c71514
commit 2ec1fb2987
No known key found for this signature in database
10 changed files with 71 additions and 46 deletions

View file

@ -32,7 +32,7 @@ async fn configure_token_install(app: &axum::Router, token: &str) {
.body(Body::from(
r#"{"providers":[{"provider":"anthropic","api_key":"anthropic-test-key"}]}"#,
))
.unwrap(),
.expect("LLM install request should build"),
)
.await
.unwrap();
@ -49,7 +49,7 @@ async fn configure_token_install(app: &axum::Router, token: &str) {
.body(Body::from(
r#"{"canonical_url":"https://fabro.example.com"}"#,
))
.unwrap(),
.expect("server install request should build"),
)
.await
.unwrap();
@ -66,7 +66,7 @@ async fn configure_token_install(app: &axum::Router, token: &str) {
.body(Body::from(
r#"{"token":"ghp_test_token","username":"brynary"}"#,
))
.unwrap(),
.expect("GitHub token install request should build"),
)
.await
.unwrap();

View file

@ -435,7 +435,7 @@ fn request_with_connect_info(path: &str, ip: IpAddr) -> Request<Body> {
.method("GET")
.uri(path)
.body(Body::empty())
.unwrap();
.expect("routing test request should build");
let mut request = request;
request
.extensions_mut()

View file

@ -56,11 +56,16 @@ async fn create_run(app: &axum::Router, manifest: serde_json::Value) -> String {
.method("POST")
.uri(api("/runs"))
.header("content-type", "application/json")
.body(Body::from(serde_json::to_vec(&manifest).unwrap()))
.unwrap();
.body(Body::from(
serde_json::to_vec(&manifest).expect("manifest fixture should serialize"),
))
.expect("create-run request should build");
let response = app.clone().oneshot(request).await.unwrap();
let body = body_json(response.into_body()).await;
body["id"].as_str().unwrap().to_string()
body["id"]
.as_str()
.expect("create-run response should include an id")
.to_string()
}
async fn start_run(app: &axum::Router, run_id: &str) {
@ -68,7 +73,7 @@ async fn start_run(app: &axum::Router, run_id: &str) {
.method("POST")
.uri(api(&format!("/runs/{run_id}/start")))
.body(Body::empty())
.unwrap();
.expect("start-run request should build");
let response = app.clone().oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
@ -79,7 +84,7 @@ async fn wait_for_question(app: &axum::Router, run_id: &str) -> serde_json::Valu
.method("GET")
.uri(api(&format!("/runs/{run_id}/questions")))
.body(Body::empty())
.unwrap();
.expect("questions request should build");
let response = app.clone().oneshot(request).await.unwrap();
let body = body_json(response.into_body()).await;
if let Some(question) = body["data"].as_array().and_then(|items| items.first()) {
@ -95,7 +100,7 @@ async fn load_questions(app: &axum::Router, run_id: &str) -> serde_json::Value {
.method("GET")
.uri(api(&format!("/runs/{run_id}/questions")))
.body(Body::empty())
.unwrap();
.expect("questions request should build");
let response = app.clone().oneshot(request).await.unwrap();
assert_eq!(response.status(), StatusCode::OK);
body_json(response.into_body()).await

View file

@ -27,8 +27,12 @@ const TEST_DEV_TOKEN: &str =
"fabro_dev_abababababababababababababababababababababababababababababababab";
async fn start_tcp_server(auth_mode: AuthMode, ip_allowlist: Arc<IpAllowlistConfig>) -> SocketAddr {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let listener = TcpListener::bind("127.0.0.1:0")
.await
.expect("test TCP listener should bind");
let addr = listener
.local_addr()
.expect("test TCP listener should have a local address");
let state = create_app_state();
let router =
@ -51,24 +55,24 @@ fn build_unix_client(path: &Path) -> fabro_http::HttpClient {
.unix_socket(path)
.no_proxy()
.build()
.unwrap()
.expect("Unix test client should build")
}
fn write_test_config(tempdir: &TempDir, settings: &str) -> PathBuf {
let config_path = tempdir.path().join("settings.toml");
std::fs::write(&config_path, settings).unwrap();
std::fs::write(&config_path, settings).expect("test settings should write");
std::fs::write(
ServerState::new(tempdir.path()).env_path(),
format!("FABRO_DEV_TOKEN={TEST_DEV_TOKEN}\n"),
)
.unwrap();
.expect("test env file should write");
config_path
}
async fn spawn_served_listener(
settings: impl AsRef<str>,
) -> (JoinHandle<anyhow::Result<()>>, Bind, TempDir) {
let tempdir = tempfile::tempdir().unwrap();
let tempdir = tempfile::tempdir().expect("temporary server directory should create");
let config_path = write_test_config(&tempdir, settings.as_ref());
let styles: &'static Styles = Box::leak(Box::new(Styles::new(false)));
let (tx, rx) = tokio::sync::oneshot::channel();

View file

@ -78,8 +78,10 @@ pub(crate) fn api(path: &str) -> String {
}
pub(crate) async fn body_json(body: Body) -> serde_json::Value {
let bytes = to_bytes(body, usize::MAX).await.unwrap();
serde_json::from_slice(&bytes).unwrap()
let bytes = to_bytes(body, usize::MAX)
.await
.expect("response body should fit in memory");
serde_json::from_slice(&bytes).expect("response body should be valid JSON")
}
pub(crate) async fn create_and_start_run_from_manifest(
@ -90,17 +92,22 @@ pub(crate) async fn create_and_start_run_from_manifest(
.method("POST")
.uri(api("/runs"))
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&manifest).unwrap()))
.unwrap();
.body(Body::from(
serde_json::to_string(&manifest).expect("manifest fixture should serialize"),
))
.expect("create-run request should build");
let response = app.clone().oneshot(req).await.unwrap();
let body = body_json(response.into_body()).await;
let run_id = body["id"].as_str().unwrap().to_string();
let run_id = body["id"]
.as_str()
.expect("create-run response should include an id")
.to_string();
let req = Request::builder()
.method("POST")
.uri(api(&format!("/runs/{run_id}/start")))
.body(Body::empty())
.unwrap();
.expect("start-run request should build");
app.clone().oneshot(req).await.unwrap();
run_id
@ -134,7 +141,7 @@ pub(crate) async fn run_json(app: &axum::Router, run_id: &str) -> serde_json::Va
.method("GET")
.uri(api(&format!("/runs/{run_id}")))
.body(Body::empty())
.unwrap();
.expect("run lookup request should build");
let response = app.clone().oneshot(req).await.unwrap();
assert_eq!(response.status(), StatusCode::OK);
body_json(response.into_body()).await
@ -147,7 +154,10 @@ pub(crate) async fn wait_for_run_status(
) -> String {
for _ in 0..POLL_ATTEMPTS {
let body = run_json(app, run_id).await;
let status = body["status"].as_str().unwrap().to_string();
let status = body["status"]
.as_str()
.expect("run response should include a string status")
.to_string();
if expected.iter().any(|candidate| *candidate == status) {
return status;
}
@ -163,7 +173,10 @@ pub(crate) async fn wait_for_run_status_not_in(
) -> String {
for _ in 0..POLL_ATTEMPTS {
let body = run_json(app, run_id).await;
let status = body["status"].as_str().unwrap().to_string();
let status = body["status"]
.as_str()
.expect("run response should include a string status")
.to_string();
if unexpected.iter().all(|candidate| *candidate != status) {
return status;
}

View file

@ -23,14 +23,11 @@ use tower::ServiceExt;
use super::helpers::test_app_state;
fn load_spec() -> Value {
let spec_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("docs/api-reference/fabro-api.yaml");
let repo_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(3)
.expect("fabro-server crate should be nested under lib/crates/fabro-server");
let spec_path = repo_root.join("docs/api-reference/fabro-api.yaml");
let text = std::fs::read_to_string(&spec_path).expect("failed to read spec");
serde_yaml::from_str(&text).expect("failed to parse spec")
}
@ -81,7 +78,9 @@ fn request_for(method: &Method, uri: &str) -> Request<Body> {
} else {
Body::empty()
};
builder.body(body).unwrap()
builder
.body(body)
.expect("OpenAPI conformance request should build")
}
#[tokio::test]

View file

@ -19,13 +19,13 @@ async fn get_json(app: axum::Router, uri: &str) -> serde_json::Value {
.uri(uri)
.header("x-fabro-demo", "1")
.body(Body::empty())
.unwrap();
.expect("pagination request should build");
let response = app.clone().oneshot(req).await.unwrap();
assert_eq!(response.status(), StatusCode::OK, "GET {uri} failed");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.unwrap();
serde_json::from_slice(&body).unwrap()
.expect("pagination response body should fit in memory");
serde_json::from_slice(&body).expect("pagination response should be valid JSON")
}
/// Assert that a value has the paginated shape: `{ data: [...], meta: {

View file

@ -19,9 +19,9 @@ fn completion_request(stream: bool) -> Request<Body> {
"messages": [{"role": "user", "content": [{"kind": "text", "data": "Hello"}]}],
"stream": stream
}))
.unwrap(),
.expect("completion fixture should serialize"),
))
.unwrap()
.expect("completion request should build")
}
fn completion_request_with_model(stream: bool, model: &str) -> Request<Body> {
@ -35,9 +35,9 @@ fn completion_request_with_model(stream: bool, model: &str) -> Request<Body> {
"messages": [{"role": "user", "content": [{"kind": "text", "data": "Hi"}]}],
"stream": stream
}))
.unwrap(),
.expect("model completion fixture should serialize"),
))
.unwrap()
.expect("completion request should build")
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]

View file

@ -35,10 +35,12 @@ async fn wait_for_question_id(app: &axum::Router, run_id: &str) -> String {
.method("GET")
.uri(api(&format!("/runs/{run_id}/questions")))
.body(Body::empty())
.unwrap();
.expect("questions request should build");
let response = app.clone().oneshot(req).await.unwrap();
let body = body_json(response.into_body()).await;
let arr = body["data"].as_array().unwrap();
let arr = body["data"]
.as_array()
.expect("questions response should include a data array");
if let Some(question_id) = arr
.first()
.and_then(|item| item["id"].as_str())
@ -57,10 +59,12 @@ async fn wait_for_question(app: &axum::Router, run_id: &str) -> serde_json::Valu
.method("GET")
.uri(api(&format!("/runs/{run_id}/questions")))
.body(Body::empty())
.unwrap();
.expect("questions request should build");
let response = app.clone().oneshot(req).await.unwrap();
let body = body_json(response.into_body()).await;
let arr = body["data"].as_array().unwrap();
let arr = body["data"]
.as_array()
.expect("questions response should include a data array");
if let Some(question) = arr.first() {
return question.clone();
}

View file

@ -26,7 +26,7 @@ async fn wait_for_checkpoint(app: &axum::Router, run_id: &str) -> serde_json::Va
.method("GET")
.uri(api(&format!("/runs/{run_id}/checkpoint")))
.body(Body::empty())
.unwrap();
.expect("checkpoint request should build");
let response = app.clone().oneshot(req).await.unwrap();
if response.status() == StatusCode::OK {
return body_json(response.into_body()).await;