use std::sync::Arc;
use axum::{body::to_bytes, extract::State, http::StatusCode, response::IntoResponse as _};
use crate::service::handlers::{AppState, DepInfo, DepStatus, compute_status, handle_health};
use crate::service::inference_probe::InferenceStatus;
use super::tests::{FailSearch, FakeAnalyze, FakeLlm, FakeSearch};
#[test]
fn health_status_ok_all_good() {
let deps = DepStatus {
trusty_search: DepInfo {
required: true,
reachable: true,
},
trusty_analyze: DepInfo {
required: false,
reachable: true,
},
};
assert_eq!(
compute_status(InferenceStatus::Ok, &deps),
"ok",
"all good → status must be ok"
);
}
#[test]
fn health_status_degraded_required_dep_down() {
let deps = DepStatus {
trusty_search: DepInfo {
required: true,
reachable: false, },
trusty_analyze: DepInfo {
required: false,
reachable: true,
},
};
assert_eq!(
compute_status(InferenceStatus::Ok, &deps),
"degraded",
"required dep down → status must be degraded"
);
}
#[test]
fn health_status_degraded_inference_auth_error() {
let deps = DepStatus {
trusty_search: DepInfo {
required: true,
reachable: true,
},
trusty_analyze: DepInfo {
required: false,
reachable: true,
},
};
assert_eq!(
compute_status(InferenceStatus::AuthError, &deps),
"degraded",
"auth_error inference → status must be degraded"
);
}
#[test]
fn health_status_ok_inference_unknown() {
let deps = DepStatus {
trusty_search: DepInfo {
required: true,
reachable: true,
},
trusty_analyze: DepInfo {
required: false,
reachable: true,
},
};
assert_eq!(
compute_status(InferenceStatus::Unknown, &deps),
"ok",
"Unknown inference (probe timed out) must not degrade status (#739)"
);
}
#[test]
fn health_status_ok_optional_dep_down() {
let deps = DepStatus {
trusty_search: DepInfo {
required: true,
reachable: true,
},
trusty_analyze: DepInfo {
required: false,
reachable: false, },
};
assert_eq!(
compute_status(InferenceStatus::Ok, &deps),
"ok",
"optional dep down → status must remain ok"
);
}
#[tokio::test]
async fn health_required_dep_down_sets_degraded() {
let state = AppState::new(
crate::config::ReviewConfig::load(None),
Arc::new(FakeLlm),
Arc::new(FailSearch),
None,
);
let response = handle_health(State(state)).await;
let resp: axum::response::Response = response.into_response();
assert_eq!(
resp.status(),
StatusCode::OK,
"HTTP status must be 200 even when degraded (spec REV-706)"
);
let body_bytes = to_bytes(resp.into_body(), 65536).await.expect("body bytes");
let body: serde_json::Value = serde_json::from_slice(&body_bytes).expect("valid JSON");
assert_eq!(
body["status"], "degraded",
"required dep (trusty_search) unreachable → status must be degraded"
);
assert_eq!(
body["inference"], "ok",
"inference must be ok (FakeLlm always succeeds)"
);
assert_eq!(
body["deps"]["trusty_search"]["reachable"], false,
"trusty_search.reachable must be false when search is down"
);
assert_eq!(
body["deps"]["trusty_search"]["required"], true,
"trusty_search.required must remain true"
);
}
#[tokio::test]
async fn health_optional_dep_down_stays_ok() {
let state = AppState::new(
crate::config::ReviewConfig::load(None),
Arc::new(FakeLlm),
Arc::new(FakeSearch),
Some(Arc::new(FakeAnalyze)),
);
let response = handle_health(State(state)).await;
let resp: axum::response::Response = response.into_response();
assert_eq!(resp.status(), StatusCode::OK);
let body_bytes = to_bytes(resp.into_body(), 65536).await.expect("body bytes");
let body: serde_json::Value = serde_json::from_slice(&body_bytes).expect("valid JSON");
assert_eq!(
body["status"], "ok",
"optional dep (trusty_analyze) unreachable → status must remain ok"
);
assert_eq!(
body["deps"]["trusty_analyze"]["reachable"], false,
"trusty_analyze.reachable must be false (FakeAnalyze.health() fails)"
);
assert_eq!(
body["deps"]["trusty_search"]["reachable"], true,
"trusty_search.reachable must be true (FakeSearch.health() succeeds)"
);
}