app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 0fe7930b924c4bea37dce2cfcbdc9f6bef29aa7e
parent d70851bdcb5dcde1d0f88eb775d077d15a48e09d
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 18:43:09 +0000

app: add keystore health check

- add keystore health check helper for nostr stores
- treat empty keystore results as ok status
- add keystore tests with async stub coverage
- export helper and add async-trait dev dependency

Diffstat:
MCargo.toml | 1+
Mapp/Cargo.toml | 1+
Mapp/src/health.rs | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/src/lib.rs | 1+
4 files changed, 71 insertions(+), 0 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -23,6 +23,7 @@ wasm-bindgen = "=0.2.100" serde = { version = "1", features = ["derive"] } serde_json = "1" futures = "0.3" +async-trait = "0.1.89" getrandom = "0.2" js-sys = "0.3.77" gloo-timers = "0.3" diff --git a/app/Cargo.toml b/app/Cargo.toml @@ -17,3 +17,4 @@ serde.workspace = true [dev-dependencies] futures.workspace = true +async-trait.workspace = true diff --git a/app/src/health.rs b/app/src/health.rs @@ -80,6 +80,7 @@ use crate::{ AppKeyMapConfig, }; use radroots_app_core::datastore::RadrootsClientDatastore; +use radroots_app_core::keystore::{RadrootsClientKeystoreError, RadrootsClientKeystoreNostr}; pub fn app_health_check_key_maps(key_maps: &AppKeyMapConfig) -> AppHealthCheckResult { match app_key_maps_validate(key_maps) { @@ -133,6 +134,16 @@ pub async fn app_health_check_datastore_roundtrip<T: RadrootsClientDatastore>( AppHealthCheckResult::ok() } +pub async fn app_health_check_keystore_access<T: RadrootsClientKeystoreNostr>( + keystore: &T, +) -> AppHealthCheckResult { + match keystore.keys().await { + Ok(_) => AppHealthCheckResult::ok(), + Err(RadrootsClientKeystoreError::NostrNoResults) => AppHealthCheckResult::ok(), + Err(err) => AppHealthCheckResult::error(err.to_string()), + } +} + #[cfg(test)] mod tests { use super::{ @@ -140,12 +151,20 @@ mod tests { app_health_check_bootstrap_app_data, app_health_check_bootstrap_config, app_health_check_datastore_roundtrip, + app_health_check_keystore_access, AppHealthCheckResult, AppHealthCheckStatus, AppHealthReport, }; use crate::AppKeyMapConfig; + use async_trait::async_trait; use radroots_app_core::datastore::RadrootsClientWebDatastore; + use radroots_app_core::keystore::{ + RadrootsClientKeystoreError, + RadrootsClientKeystoreNostr, + RadrootsClientKeystoreResult, + RadrootsClientWebKeystoreNostr, + }; #[test] fn health_status_as_str() { @@ -209,4 +228,53 @@ mod tests { futures::executor::block_on(app_health_check_datastore_roundtrip(&datastore)); assert_eq!(result.status, AppHealthCheckStatus::Error); } + + struct TestKeystore { + result: RadrootsClientKeystoreResult<Vec<String>>, + } + + #[async_trait(?Send)] + impl RadrootsClientKeystoreNostr for TestKeystore { + async fn generate(&self) -> RadrootsClientKeystoreResult<String> { + Err(RadrootsClientKeystoreError::IdbUndefined) + } + + async fn add(&self, _secret_key: &str) -> RadrootsClientKeystoreResult<String> { + Err(RadrootsClientKeystoreError::IdbUndefined) + } + + async fn read(&self, _public_key: &str) -> RadrootsClientKeystoreResult<String> { + Err(RadrootsClientKeystoreError::IdbUndefined) + } + + async fn keys(&self) -> RadrootsClientKeystoreResult<Vec<String>> { + self.result.clone() + } + + async fn remove(&self, _public_key: &str) -> RadrootsClientKeystoreResult<String> { + Err(RadrootsClientKeystoreError::IdbUndefined) + } + + async fn reset(&self) -> RadrootsClientKeystoreResult<()> { + Err(RadrootsClientKeystoreError::IdbUndefined) + } + } + + #[test] + fn health_check_keystore_maps_empty_ok() { + let keystore = TestKeystore { + result: Err(RadrootsClientKeystoreError::NostrNoResults), + }; + let result = + futures::executor::block_on(app_health_check_keystore_access(&keystore)); + assert_eq!(result.status, AppHealthCheckStatus::Ok); + } + + #[test] + fn health_check_keystore_maps_idb_errors() { + let keystore = RadrootsClientWebKeystoreNostr::new(None); + let result = + futures::executor::block_on(app_health_check_keystore_access(&keystore)); + assert_eq!(result.status, AppHealthCheckStatus::Error); + } } diff --git a/app/src/lib.rs b/app/src/lib.rs @@ -23,6 +23,7 @@ pub use health::{ app_health_check_bootstrap_app_data, app_health_check_bootstrap_config, app_health_check_datastore_roundtrip, + app_health_check_keystore_access, app_health_check_key_maps, AppHealthCheckResult, AppHealthCheckStatus,