use super::super::health::{
ensure_health_probe_palace, run_health_round_trip_inner, seed_probe_sentinel_if_absent,
HealthProbeError, PROBE_SENTINEL_CONTENT,
};
use super::super::router;
use super::super::HEALTH_PROBE_PALACE;
use super::test_state;
use axum::body::{to_bytes, Body};
use axum::http::{Request, StatusCode};
use serde_json::Value;
use tower::util::ServiceExt;
use trusty_common::memory_core::palace::PalaceId;
use trusty_common::memory_core::retrieval::RecallResult;
use uuid::Uuid;
#[tokio::test]
#[ignore = "loads the default ONNX embedder; run with --include-ignored"]
async fn health_endpoint_returns_ok() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["status"], "ok");
assert_eq!(v["version"], env!("CARGO_PKG_VERSION"));
}
#[tokio::test]
#[ignore = "loads the default ONNX embedder; run with --include-ignored"]
async fn health_endpoint_includes_resource_fields() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
let rss_mb = v["rss_mb"].as_u64().expect("rss_mb is u64");
assert!(rss_mb < 1024 * 1024, "rss_mb unit must be MB");
let cpu = v["cpu_pct"].as_f64().expect("cpu_pct is a number");
assert!(cpu >= 0.0, "cpu_pct must be non-negative");
assert_eq!(v["disk_bytes"].as_u64(), Some(0));
assert!(v["uptime_secs"].is_u64(), "uptime_secs must be present");
}
#[tokio::test]
async fn health_endpoint_cheap_by_default() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["status"], "ok", "cheap health must report ok; got {v:?}");
assert_eq!(v["version"], env!("CARGO_PKG_VERSION"));
assert!(
v.get("detail").is_none() || v["detail"].is_null(),
"cheap health must not carry detail; got {v:?}"
);
}
#[tokio::test]
async fn health_endpoint_includes_fd_gauge() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
#[cfg(unix)]
{
let open_fds = v["open_fds"]
.as_u64()
.expect("open_fds must be present on Unix");
assert!(
open_fds > 0,
"open_fds must be > 0 (at least stdin/stdout/stderr)"
);
let limit = v["fd_soft_limit"]
.as_u64()
.expect("fd_soft_limit must be present on Unix");
assert!(limit > 0, "fd_soft_limit must be > 0");
assert!(
open_fds < limit,
"open_fds ({open_fds}) must be below fd_soft_limit ({limit}) in tests"
);
}
}
#[tokio::test]
#[ignore = "loads the default ONNX embedder; run with --include-ignored"]
async fn health_endpoint_round_trip_on_fresh_install_is_ok() {
let state = test_state();
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 1024).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(v["status"], "ok");
assert!(
v.get("detail").is_none() || v["detail"].is_null(),
"fresh-install health must not carry a degraded detail (got {v:?})"
);
}
#[tokio::test]
#[ignore = "loads the default ONNX embedder; run with --include-ignored"]
async fn health_endpoint_round_trip_with_palace_is_ok() {
let state = test_state();
let palace = trusty_common::memory_core::Palace {
id: PalaceId::new("health-probe-palace"),
name: "health-probe-palace".to_string(),
description: None,
created_at: chrono::Utc::now(),
data_dir: state.data_root.join("health-probe-palace"),
};
state
.registry
.create_palace(&state.data_root, palace)
.expect("create_palace");
let app = router().with_state(state);
let resp = app
.oneshot(
Request::builder()
.uri("/health")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let bytes = to_bytes(resp.into_body(), 2048).await.unwrap();
let v: Value = serde_json::from_slice(&bytes).unwrap();
assert_eq!(
v["status"], "ok",
"round-trip should succeed against a fresh palace; got {v:?}"
);
assert!(
v.get("detail").is_none() || v["detail"].is_null(),
"successful round-trip must not carry a detail field (got {v:?})"
);
}
#[tokio::test]
async fn health_probe_palace_is_invisible() {
let state = test_state();
ensure_health_probe_palace(&state).expect("ensure_health_probe_palace");
assert!(
state.data_root.join(HEALTH_PROBE_PALACE).exists(),
"probe palace directory should be persisted on disk"
);
let service = crate::service::MemoryService::new(state);
let listed = service.list_palaces().await.expect("list_palaces");
assert!(
listed.iter().all(|p| !p.id.starts_with("__")),
"no `__`-prefixed palace may appear in the user-facing list; got {:?}",
listed.iter().map(|p| &p.id).collect::<Vec<_>>()
);
assert!(
!listed.iter().any(|p| p.id == HEALTH_PROBE_PALACE),
"the dedicated `__health_probe__` palace must be invisible; got {:?}",
listed.iter().map(|p| &p.id).collect::<Vec<_>>()
);
}
#[tokio::test]
async fn health_probe_cleans_up_on_success() {
use trusty_common::memory_core::Drawer;
let state = test_state();
ensure_health_probe_palace(&state).expect("ensure_health_probe_palace");
let handle = state
.registry
.open_palace(&state.data_root, &PalaceId::new(HEALTH_PROBE_PALACE))
.expect("open probe palace");
let result = run_health_round_trip_inner(handle.clone(), move |h, _query| async move {
let drawers = h.drawers.read();
let last = drawers
.last()
.cloned()
.unwrap_or_else(|| Drawer::new(Uuid::new_v4(), "stub"));
drop(drawers);
Ok(vec![RecallResult {
drawer: last,
score: 1.0,
layer: 1,
}])
})
.await;
assert!(
result.is_ok(),
"successful round-trip should return Ok; got {result:?}"
);
let drawer_count = handle.drawers.read().len();
assert_eq!(
drawer_count, 0,
"probe palace must have zero drawers after a successful round-trip (got {drawer_count})"
);
}
#[tokio::test]
async fn health_probe_cleans_up_on_recall_miss() {
let state = test_state();
ensure_health_probe_palace(&state).expect("ensure_health_probe_palace");
let handle = state
.registry
.open_palace(&state.data_root, &PalaceId::new(HEALTH_PROBE_PALACE))
.expect("open probe palace");
let result = run_health_round_trip_inner(handle.clone(), |_h, _q| async move {
Ok(Vec::new())
})
.await;
assert!(
matches!(result, Err(HealthProbeError::ProbeMissing(_))),
"recall miss must surface as ProbeMissing; got {result:?}"
);
let drawer_count = handle.drawers.read().len();
assert_eq!(
drawer_count, 0,
"probe palace must be empty after a recall miss (got {drawer_count})"
);
}
#[tokio::test]
async fn health_probe_cleans_up_on_recall_error() {
let state = test_state();
ensure_health_probe_palace(&state).expect("ensure_health_probe_palace");
let handle = state
.registry
.open_palace(&state.data_root, &PalaceId::new(HEALTH_PROBE_PALACE))
.expect("open probe palace");
let result = run_health_round_trip_inner(handle.clone(), |_h, _q| async move {
Err(HealthProbeError::Recall("simulated failure".to_string()))
})
.await;
assert!(
matches!(result, Err(HealthProbeError::Recall(_))),
"recall error must surface as Recall; got {result:?}"
);
let drawer_count = handle.drawers.read().len();
assert_eq!(
drawer_count, 0,
"probe palace must be empty after a recall error (got {drawer_count})"
);
}
#[tokio::test]
async fn health_probe_self_heals_after_migration_wipe() {
let state = test_state();
ensure_health_probe_palace(&state).expect("create probe palace");
let handle = state
.registry
.open_palace(&state.data_root, &PalaceId::new(HEALTH_PROBE_PALACE))
.expect("open probe palace");
assert_eq!(
handle.drawers.read().len(),
0,
"palace must be empty before self-heal"
);
let seeded = seed_probe_sentinel_if_absent(&handle)
.await
.expect("seed_probe_sentinel_if_absent");
assert!(
seeded,
"first call must report that the sentinel was seeded"
);
{
let drawers = handle.drawers.read();
assert_eq!(
drawers.len(),
1,
"sentinel must be seeded when palace is empty (issue #1142)"
);
assert_eq!(
drawers[0].content, PROBE_SENTINEL_CONTENT,
"seeded drawer must carry the well-known sentinel content"
);
}
let seeded_again = seed_probe_sentinel_if_absent(&handle)
.await
.expect("seed_probe_sentinel_if_absent idempotent");
assert!(
!seeded_again,
"second call must report sentinel already present"
);
let drawer_count = handle.drawers.read().len();
assert_eq!(
drawer_count, 1,
"seed_probe_sentinel_if_absent must be idempotent (got {drawer_count})"
);
}