137 lines
7.3 KiB
Rust
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(_) => {},
|
|
}
|
|
}
|