// file: crates/ksp-app-wallet-desk/unit_tests/wallet_config.rs // version: 2 #[test] fn directory_preparation_creates_missing_root_and_nested_profile_path() { let root = fixture_path("create"); cleanup_fixture(root.as_path()); let root_created = super::ensure_global_wallet_root(root.as_path()); assert_eq!(root_created.as_ref().ok().copied(), std::option::Option::Some(true)); let profile_created = super::ensure_profile_wallet_directory(root.as_path(), std::path::Path::new("tests/ephemeral")); assert_eq!(profile_created.as_ref().ok().copied(), std::option::Option::Some(true)); assert!(root.join("tests/ephemeral").is_dir()); let root_again = super::ensure_global_wallet_root(root.as_path()); assert_eq!(root_again.as_ref().ok().copied(), std::option::Option::Some(false)); let profile_again = super::ensure_profile_wallet_directory(root.as_path(), std::path::Path::new("tests/ephemeral")); assert_eq!(profile_again.as_ref().ok().copied(), std::option::Option::Some(false)); cleanup_fixture(root.as_path()); } #[test] fn directory_preparation_rejects_existing_non_directory_root() { let root = fixture_path("file-root"); cleanup_fixture(root.as_path()); let parent = root.parent(); assert!(parent.is_some()); if let std::option::Option::Some(parent) = parent { let created = std::fs::create_dir_all(parent); assert!(created.is_ok(), "fixture parent should be creatable: {created:?}"); let written = std::fs::write(root.as_path(), b"not-a-directory"); assert!(written.is_ok(), "fixture file should be writable: {written:?}"); let result = super::ensure_global_wallet_root(root.as_path()); assert!(result.is_err(), "non-directory Wallet root should fail: {result:?}"); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_WALLET_DIRECTORY_INVALID); } } cleanup_fixture(root.as_path()); } #[cfg(unix)] #[test] fn global_wallet_root_rejects_symbolic_link() { let root = fixture_path("root-symlink"); let outside = fixture_path("root-symlink-outside"); cleanup_fixture(root.as_path()); cleanup_fixture(outside.as_path()); let outside_created = std::fs::create_dir_all(outside.as_path()); assert!(outside_created.is_ok()); if outside_created.is_ok() { let linked = std::os::unix::fs::symlink(outside.as_path(), root.as_path()); assert!(linked.is_ok()); if linked.is_ok() { let result = super::ensure_global_wallet_root(root.as_path()); assert!(result.is_err()); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_WALLET_DIRECTORY_INVALID); } } } cleanup_fixture(root.as_path()); cleanup_fixture(outside.as_path()); } #[cfg(unix)] #[test] fn profile_directory_preparation_rejects_symbolic_link_components() { let root = fixture_path("symlink-root"); let outside = fixture_path("symlink-outside"); cleanup_fixture(root.as_path()); cleanup_fixture(outside.as_path()); let root_created = std::fs::create_dir_all(root.as_path()); let outside_created = std::fs::create_dir_all(outside.as_path()); assert!(root_created.is_ok(), "root fixture should be creatable: {root_created:?}"); assert!(outside_created.is_ok(), "outside fixture should be creatable: {outside_created:?}"); if root_created.is_ok() && outside_created.is_ok() { let linked = std::os::unix::fs::symlink(outside.as_path(), root.join("tests")); assert!(linked.is_ok(), "symlink fixture should be creatable: {linked:?}"); let result = super::ensure_profile_wallet_directory(root.as_path(), std::path::Path::new("tests/ephemeral")); assert!(result.is_err(), "profile path must reject symlink traversal: {result:?}"); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_WALLET_DIRECTORY_INVALID); } } cleanup_fixture(root.as_path()); cleanup_fixture(outside.as_path()); } fn fixture_path(name: &str) -> std::path::PathBuf { return std::env::temp_dir().join(std::format!("ksp-wallet-desk-{name}-{}", std::process::id())); } fn cleanup_fixture(path: &std::path::Path) { let metadata = std::fs::symlink_metadata(path); match metadata { std::result::Result::Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => { let _ = std::fs::remove_dir_all(path); }, std::result::Result::Ok(_) => { let _ = std::fs::remove_file(path); }, std::result::Result::Err(_) => {}, } }