lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 66ba26b430848ef63a8384ae7ff6cbd061984c75
parent 80a09e7d33a1efa53d63bb13852f107619fd9549
Author: triesap <tyson@radroots.org>
Date:   Thu,  5 Mar 2026 01:56:43 +0000

identity: close strict path-instantiation region gaps

Diffstat:
Mcrates/identity/tests/identity.rs | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+), 0 deletions(-)

diff --git a/crates/identity/tests/identity.rs b/crates/identity/tests/identity.rs @@ -283,6 +283,18 @@ fn from_secret_key_bytes_rejects_wrong_length() { } #[test] +fn from_secret_key_str_rejects_invalid_secret() { + let err = RadrootsIdentity::from_secret_key_str("not-a-secret-key").unwrap_err(); + assert!(matches!(err, IdentityError::InvalidSecretKey(_))); +} + +#[test] +fn from_secret_key_bytes_rejects_invalid_scalar() { + let err = RadrootsIdentity::from_secret_key_bytes(&[0u8; 32]).unwrap_err(); + assert!(matches!(err, IdentityError::InvalidSecretKey(_))); +} + +#[test] fn load_from_path_reports_not_found_and_read_errors() { let dir = tempfile::tempdir().unwrap(); let missing = dir.path().join("missing-identity.json"); @@ -330,6 +342,82 @@ fn load_from_json_file_without_public_key_succeeds() { } #[test] +fn load_from_json_file_rejects_invalid_secret_key_string() { + let payload = serde_json::json!({ + "secret_key": "invalid-secret-key", + "public_key": null, + }); + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("identity.json"); + std::fs::write(&path, payload.to_string()).unwrap(); + + let err = RadrootsIdentity::load_from_path_auto(&path).unwrap_err(); + assert!(matches!(err, IdentityError::InvalidSecretKey(_))); +} + +#[test] +fn load_from_json_file_rejects_invalid_public_key_value() { + let keys = nostr::Keys::generate(); + let identity = RadrootsIdentity::new(keys); + let mut file = identity.to_file(); + file.public_key = Some("invalid-public-key".to_string()); + let json = serde_json::to_string(&file).unwrap(); + + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("identity.json"); + std::fs::write(&path, json).unwrap(); + + let err = RadrootsIdentity::load_from_path_auto(&path).unwrap_err(); + assert!(matches!(err, IdentityError::InvalidPublicKey(_))); +} + +#[test] +fn save_json_rejects_directory_target() { + let identity = RadrootsIdentity::generate(); + let dir = tempfile::tempdir().unwrap(); + let err = identity.save_json(dir.path()).unwrap_err(); + assert!(matches!(err, IdentityError::Store(_))); +} + +#[cfg(unix)] +#[test] +fn save_json_reports_write_failure_on_read_only_directory() { + use std::os::unix::fs::PermissionsExt; + + let identity = RadrootsIdentity::generate(); + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("identity.json"); + identity.save_json(path.as_path()).unwrap(); + + std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o500)).unwrap(); + let err_path = identity.save_json(path.as_path()).unwrap_err(); + assert!(matches!(err_path, IdentityError::Store(_))); + let err_path_buf = identity.save_json(&path).unwrap_err(); + assert!(matches!(err_path_buf, IdentityError::Store(_))); + std::fs::set_permissions(dir.path(), std::fs::Permissions::from_mode(0o700)).unwrap(); +} + +#[cfg(unix)] +#[test] +fn load_or_generate_reports_save_failure_when_parent_not_writable() { + use std::os::unix::fs::PermissionsExt; + + let dir = tempfile::tempdir().unwrap(); + let parent = dir.path().join("readonly"); + std::fs::create_dir(&parent).unwrap(); + std::fs::set_permissions(&parent, std::fs::Permissions::from_mode(0o500)).unwrap(); + + let path = parent.join("identity.json"); + let err = + RadrootsIdentity::load_or_generate::<&std::path::Path>(Some(path.as_path()), true) + .unwrap_err(); + assert!(matches!(err, IdentityError::Store(_))); + let err_path_buf = RadrootsIdentity::load_or_generate(Some(&path), true).unwrap_err(); + assert!(matches!(err_path_buf, IdentityError::Store(_))); + std::fs::set_permissions(&parent, std::fs::Permissions::from_mode(0o700)).unwrap(); +} + +#[test] fn load_or_generate_uses_default_path_when_missing() { let original = std::env::current_dir().unwrap(); let dir = tempfile::tempdir().unwrap(); @@ -365,6 +453,23 @@ fn load_or_generate_prefers_existing_path() { } #[test] +fn path_ref_variants_cover_success_paths() { + let identity = RadrootsIdentity::generate(); + let dir = tempfile::tempdir().unwrap(); + + let saved_path = dir.path().join("saved.json"); + identity.save_json(saved_path.as_path()).unwrap(); + let loaded = RadrootsIdentity::load_from_path_auto(saved_path.as_path()).unwrap(); + assert_eq!(loaded.public_key(), identity.public_key()); + + let generated_path = dir.path().join("generated.json"); + let generated = RadrootsIdentity::load_or_generate(Some(generated_path.as_path()), true).unwrap(); + assert!(generated_path.exists()); + let roundtrip = RadrootsIdentity::load_from_path_auto(generated_path.as_path()).unwrap(); + assert_eq!(generated.public_key(), roundtrip.public_key()); +} + +#[test] fn generate_with_profile_retains_profile() { let profile = profile_with_identifier("runtime-user"); let identity = RadrootsIdentity::generate_with_profile(profile);