myc

Self-custodial remote signer for Radroots apps
git clone https://radroots.dev/git/myc.git
Log | Files | Refs | README | LICENSE

commit 565b1c6b147dbff6712df07e4688665b1bc1bf2d
parent 9608edf9735169a70a68fbad0a6f48b46a2579b1
Author: triesap <tyson@radroots.org>
Date:   Thu,  2 Apr 2026 00:56:32 +0000

tests: prove real external-command timeout path

- add a unix-only helper-backed timeout proof for MycProcessCommandExecutor
- assert the production helper path returns TimedOut within a bounded window
- keep the coverage isolated to custody tests without widening runtime behavior
- revalidate myc with nix check and nix test on an isolated target dir during workspace contention

Diffstat:
Msrc/custody.rs | 33+++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+), 0 deletions(-)

diff --git a/src/custody.rs b/src/custody.rs @@ -1370,8 +1370,12 @@ impl MycIdentityStatusOutput { #[cfg(test)] mod tests { + use std::fs; + #[cfg(unix)] + use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::sync::Mutex; + use std::time::Instant; use radroots_identity::RadrootsIdentity; use radroots_nostr_accounts::prelude::{ @@ -1398,6 +1402,15 @@ mod tests { } } + #[cfg(unix)] + fn write_timeout_helper(path: &Path) { + let script = "#!/bin/sh\nwhile :; do\n :\ndone\n"; + fs::write(path, script).expect("write helper"); + let mut permissions = fs::metadata(path).expect("helper metadata").permissions(); + permissions.set_mode(0o755); + fs::set_permissions(path, permissions).expect("helper permissions"); + } + #[derive(Debug)] struct FakeExternalCommandExecutor { identity: RadrootsIdentity, @@ -1917,4 +1930,24 @@ mod tests { } if role == "signer" && path == &PathBuf::from("/tmp/signer-helper") )); } + + #[cfg(unix)] + #[test] + fn process_executor_times_out_and_kills_real_helper() { + let temp = tempfile::tempdir().expect("tempdir"); + let helper_path = temp.path().join("timeout-helper.sh"); + write_timeout_helper(&helper_path); + + let executor = MycProcessCommandExecutor; + let started_at = Instant::now(); + let err = executor + .execute(&helper_path, b"{\"operation\":\"describe\"}", Duration::from_millis(100)) + .expect_err("timeout"); + + assert!(matches!(err, MycExternalCommandExecuteError::TimedOut)); + assert!( + started_at.elapsed() < Duration::from_secs(2), + "timeout path should stay bounded" + ); + } }