Files
khadhroony-solana-project/crates/ksp-app-wallet-desk/unit_tests/wallet_inventory.rs
2026-08-21 17:15:40 +02:00

137 lines
7.3 KiB
Rust

// file: crates/ksp-app-wallet-desk/unit_tests/wallet_inventory.rs
// version: 2
const VALID_WALLET: &[u8] = include_bytes!("../../ksp-wallet-lib/tests/fixtures/kspwallet_v1_full_vector.json");
#[test]
fn inventory_lists_regular_wallets_reports_invalid_candidates_and_ignores_other_entries() {
let root = fixture_path("inventory");
cleanup_fixture(root.as_path());
let created = std::fs::create_dir_all(root.as_path());
assert!(created.is_ok(), "inventory fixture root should be creatable: {created:?}");
if created.is_ok() {
assert!(std::fs::write(root.join("beta.kspwallet"), VALID_WALLET).is_ok());
assert!(std::fs::write(root.join("alpha.kspwallet"), b"not-json").is_ok());
assert!(std::fs::write(root.join("ignored.txt"), VALID_WALLET).is_ok());
assert!(std::fs::write(root.join("upper.KSPWALLET"), VALID_WALLET).is_ok());
assert!(std::fs::write(root.join("backup.kspwallet.bak"), VALID_WALLET).is_ok());
assert!(std::fs::create_dir(root.join("directory.kspwallet")).is_ok());
assert!(std::fs::create_dir(root.join("nested")).is_ok());
assert!(std::fs::write(root.join("nested/hidden.kspwallet"), VALID_WALLET).is_ok());
let runtime = tokio::runtime::Builder::new_current_thread().build();
assert!(runtime.is_ok(), "test runtime should be constructible: {runtime:?}");
if let std::result::Result::Ok(runtime) = runtime {
let inventory = runtime.block_on(crate::list_wallet_inventory(root.as_path()));
assert!(inventory.is_ok(), "inventory should complete: {inventory:?}");
if let std::result::Result::Ok(entries) = inventory {
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].filename, "alpha.kspwallet");
assert_eq!(entries[0].inspection_status, crate::WalletInspectionStatusDto::Invalid);
assert_eq!(entries[0].state, crate::WalletInventoryStateDto::Error);
assert!(entries[0].diagnostic.is_some());
assert_eq!(entries[1].filename, "beta.kspwallet");
assert_eq!(entries[1].inspection_status, crate::WalletInspectionStatusDto::Valid);
assert_eq!(entries[1].state, crate::WalletInventoryStateDto::Locked);
assert_eq!(entries[1].format_version, std::option::Option::Some(1));
assert!(entries[1].view_enabled.is_some());
assert!(entries[1].diagnostic.is_none());
}
}
}
cleanup_fixture(root.as_path());
}
#[test]
fn selection_rejects_traversal_and_reinspects_one_valid_locked_wallet() {
let root = fixture_path("selection");
cleanup_fixture(root.as_path());
let created = std::fs::create_dir_all(root.as_path());
assert!(created.is_ok(), "selection fixture root should be creatable: {created:?}");
if created.is_ok() {
assert!(std::fs::write(root.join("selected.kspwallet"), VALID_WALLET).is_ok());
let runtime = tokio::runtime::Builder::new_current_thread().build();
assert!(runtime.is_ok(), "test runtime should be constructible: {runtime:?}");
if let std::result::Result::Ok(runtime) = runtime {
let rejected = runtime
.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "../selected.kspwallet".to_owned() }));
assert!(rejected.is_err());
if let std::result::Result::Err(error) = rejected {
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
}
let rejected_backslash = runtime
.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "..\\selected.kspwallet".to_owned() }));
assert!(rejected_backslash.is_err());
if let std::result::Result::Err(error) = rejected_backslash {
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
}
for wallet_id in ["", "/selected.kspwallet", "nested/selected.kspwallet", "selected.kspwallet.bak"] {
let rejected =
runtime.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: wallet_id.to_owned() }));
assert!(rejected.is_err(), "invalid wallet selection should be rejected: {wallet_id}");
if let std::result::Result::Err(error) = rejected {
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_INVALID);
}
}
let selected =
runtime.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "selected.kspwallet".to_owned() }));
assert!(selected.is_ok(), "valid locked Wallet should be selectable: {selected:?}");
if let std::result::Result::Ok(selected) = selected {
assert_eq!(selected.wallet_id, "selected.kspwallet");
assert_eq!(selected.filename, "selected.kspwallet");
assert_eq!(selected.format_version, 1);
}
}
}
cleanup_fixture(root.as_path());
}
#[cfg(unix)]
#[test]
fn inventory_and_selection_exclude_symbolic_link_wallet_entries() {
let root = fixture_path("symlink-inventory");
let outside = fixture_path("symlink-wallet");
cleanup_fixture(root.as_path());
cleanup_fixture(outside.as_path());
let root_created = std::fs::create_dir_all(root.as_path());
let outside_written = std::fs::write(outside.as_path(), VALID_WALLET);
assert!(root_created.is_ok());
assert!(outside_written.is_ok());
if root_created.is_ok() && outside_written.is_ok() {
let linked = std::os::unix::fs::symlink(outside.as_path(), root.join("linked.kspwallet"));
assert!(linked.is_ok());
if linked.is_ok() {
let runtime = tokio::runtime::Builder::new_current_thread().build();
assert!(runtime.is_ok());
if let std::result::Result::Ok(runtime) = runtime {
let inventory = runtime.block_on(crate::list_wallet_inventory(root.as_path()));
assert_eq!(inventory.as_ref().ok().map(std::vec::Vec::len), std::option::Option::Some(0));
let selected = runtime
.block_on(crate::select_locked_wallet(root.as_path(), crate::WalletSelectionRequestDto { wallet_id: "linked.kspwallet".to_owned() }));
assert!(selected.is_err());
if let std::result::Result::Err(error) = selected {
assert_eq!(error.code(), crate::ERROR_CODE_WALLET_SELECTION_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(_) => {},
}
}