commit 76f987ecb6b4fb3334658731a32071471a9bc791
parent 7a800dff8d5989703b4f5955009ec177a376342a
Author: triesap <triesap@radroots.dev>
Date: Mon, 19 Jan 2026 21:23:34 +0000
app: add tangle health check stub
- add tangle health check and report field
- include tangle status in aggregate health report
- render tangle status row in UI
- add unit tests for tangle health behavior
Diffstat:
3 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/app/src/app.rs b/app/src/app.rs
@@ -23,6 +23,7 @@ use crate::{
AppInitError,
AppInitStage,
AppNotifications,
+ AppTangleClientStub,
};
fn health_status_color(status: AppHealthCheckStatus) -> &'static str {
@@ -67,10 +68,12 @@ fn spawn_health_checks(
Some(config.keystore.nostr_store),
);
let notifications = AppNotifications::new(None);
+ let tangle = AppTangleClientStub::new();
let report = app_health_check_all(
&datastore,
&keystore,
¬ifications,
+ &tangle,
&config.datastore.key_maps,
)
.await;
@@ -296,6 +299,16 @@ pub fn App() -> impl IntoView {
<span
style=move || format!(
"display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
+ health_status_color(health_report.get().tangle.status)
+ )
+ ></span>
+ <span>"tangle"</span>
+ <span>{move || health_result_label(&health_report.get().tangle)}</span>
+ </div>
+ <div style="display: flex; align-items: center; gap: 8px;">
+ <span
+ style=move || format!(
+ "display:inline-block;width:10px;height:10px;border-radius:50%;background:{};",
health_status_color(health_report.get().datastore_roundtrip.status)
)
></span>
diff --git a/app/src/health.rs b/app/src/health.rs
@@ -53,6 +53,7 @@ pub struct AppHealthReport {
pub bootstrap_app_data: AppHealthCheckResult,
pub app_data_active_key: AppHealthCheckResult,
pub notifications: AppHealthCheckResult,
+ pub tangle: AppHealthCheckResult,
pub datastore_roundtrip: AppHealthCheckResult,
pub keystore: AppHealthCheckResult,
}
@@ -65,6 +66,7 @@ impl Default for AppHealthReport {
bootstrap_app_data: AppHealthCheckResult::skipped(),
app_data_active_key: AppHealthCheckResult::skipped(),
notifications: AppHealthCheckResult::skipped(),
+ tangle: AppHealthCheckResult::skipped(),
datastore_roundtrip: AppHealthCheckResult::skipped(),
keystore: AppHealthCheckResult::skipped(),
}
@@ -84,6 +86,7 @@ use crate::{
app_datastore_read_app_data,
app_key_maps_validate,
AppNotifications,
+ AppTangleClient,
AppKeyMapConfig,
};
use radroots_app_core::datastore::{RadrootsClientDatastore, RadrootsClientDatastoreError};
@@ -159,6 +162,13 @@ pub async fn app_health_check_notifications(
}
}
+pub fn app_health_check_tangle<T: AppTangleClient>(tangle: &T) -> AppHealthCheckResult {
+ match tangle.init() {
+ Ok(()) => AppHealthCheckResult::ok(),
+ Err(err) => AppHealthCheckResult::error(err.to_string()),
+ }
+}
+
const APP_HEALTH_TEMP_KEY: &str = "radroots.health.temp";
pub async fn app_health_check_datastore_roundtrip<T: RadrootsClientDatastore>(
@@ -205,10 +215,11 @@ pub async fn app_health_check_keystore_access<T: RadrootsClientDatastore, K: Rad
}
}
-pub async fn app_health_check_all<T: RadrootsClientDatastore, K: RadrootsClientKeystoreNostr>(
+pub async fn app_health_check_all<T: RadrootsClientDatastore, K: RadrootsClientKeystoreNostr, G: AppTangleClient>(
datastore: &T,
keystore: &K,
notifications: &AppNotifications,
+ tangle: &G,
key_maps: &AppKeyMapConfig,
) -> AppHealthReport {
AppHealthReport {
@@ -217,6 +228,7 @@ pub async fn app_health_check_all<T: RadrootsClientDatastore, K: RadrootsClientK
bootstrap_app_data: app_health_check_bootstrap_app_data(datastore, key_maps).await,
app_data_active_key: app_health_check_app_data_active_key(datastore, key_maps).await,
notifications: app_health_check_notifications(notifications).await,
+ tangle: app_health_check_tangle(tangle),
datastore_roundtrip: app_health_check_datastore_roundtrip(datastore).await,
keystore: app_health_check_keystore_access(datastore, keystore, key_maps).await,
}
@@ -233,6 +245,7 @@ mod tests {
app_health_check_datastore_roundtrip,
app_health_check_keystore_access,
app_health_check_notifications,
+ app_health_check_tangle,
AppHealthCheckResult,
AppHealthCheckStatus,
AppHealthReport,
@@ -282,6 +295,7 @@ mod tests {
assert_eq!(report.bootstrap_app_data.status, AppHealthCheckStatus::Skipped);
assert_eq!(report.app_data_active_key.status, AppHealthCheckStatus::Skipped);
assert_eq!(report.notifications.status, AppHealthCheckStatus::Skipped);
+ assert_eq!(report.tangle.status, AppHealthCheckStatus::Skipped);
assert_eq!(report.datastore_roundtrip.status, AppHealthCheckStatus::Skipped);
assert_eq!(report.keystore.status, AppHealthCheckStatus::Skipped);
}
@@ -587,11 +601,13 @@ mod tests {
let datastore = RadrootsClientWebDatastore::new(None);
let keystore = RadrootsClientWebKeystoreNostr::new(None);
let notifications = crate::AppNotifications::new(None);
+ let tangle = crate::AppTangleClientStub::new();
let key_maps = crate::app_key_maps_default();
let report = futures::executor::block_on(app_health_check_all(
&datastore,
&keystore,
¬ifications,
+ &tangle,
&key_maps,
));
assert_eq!(report.key_maps.status, AppHealthCheckStatus::Ok);
@@ -599,6 +615,7 @@ mod tests {
assert_eq!(report.bootstrap_app_data.status, AppHealthCheckStatus::Error);
assert_eq!(report.app_data_active_key.status, AppHealthCheckStatus::Error);
assert_eq!(report.notifications.status, AppHealthCheckStatus::Error);
+ assert_eq!(report.tangle.status, AppHealthCheckStatus::Error);
assert_eq!(report.datastore_roundtrip.status, AppHealthCheckStatus::Error);
assert_eq!(report.keystore.status, AppHealthCheckStatus::Error);
}
@@ -611,4 +628,12 @@ mod tests {
assert_eq!(result.status, AppHealthCheckStatus::Error);
assert_eq!(result.message.as_deref(), Some("unavailable"));
}
+
+ #[test]
+ fn health_check_tangle_reports_not_implemented() {
+ let tangle = crate::AppTangleClientStub::new();
+ let result = app_health_check_tangle(&tangle);
+ assert_eq!(result.status, AppHealthCheckStatus::Error);
+ assert_eq!(result.message.as_deref(), Some("error.app.tangle.not_implemented"));
+ }
}
diff --git a/app/src/lib.rs b/app/src/lib.rs
@@ -31,6 +31,7 @@ pub use health::{
app_health_check_datastore_roundtrip,
app_health_check_keystore_access,
app_health_check_notifications,
+ app_health_check_tangle,
app_health_check_key_maps,
AppHealthCheckResult,
AppHealthCheckStatus,