v0.2.6-pre.016
This commit is contained in:
18
crates/ksp-wallet-lib/unit_tests/format.rs
Normal file
18
crates/ksp-wallet-lib/unit_tests/format.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/format.rs
|
||||
// version: 1
|
||||
|
||||
#[test]
|
||||
fn default_and_latest_are_explicit_and_currently_v2() {
|
||||
assert_eq!(crate::DEFAULT_WALLET_FORMAT, crate::WalletFormat::V2);
|
||||
assert_eq!(crate::LATEST_SUPPORTED_WALLET_FORMAT, crate::WalletFormat::V2);
|
||||
assert_eq!(crate::DEFAULT_WALLET_FORMAT.version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detector_distinguishes_v1_json_and_v2_binary_without_crypto() -> ksp_core_lib::Result<()> {
|
||||
let v1 = include_bytes!("../tests/fixtures/kspwallet_v1_wire_only.json");
|
||||
let v2 = include_bytes!("../tests/fixtures/kspwallet_v2_wire_only.bin");
|
||||
assert_eq!(crate::detect_wallet_format(v1)?, crate::WalletFormat::V1);
|
||||
assert_eq!(crate::detect_wallet_format(v2)?, crate::WalletFormat::V2);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/transfer.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import
|
||||
@@ -144,6 +144,41 @@ fn cli_json_import_creates_new_no_clobber_wallet_with_imported_identity_and_meta
|
||||
source.zeroize();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_transfer_import_creates_v2_without_removing_explicit_v1_import() {
|
||||
let directory = tempfile::tempdir().expect("Wallet transfer test directory must be creatable");
|
||||
let default_destination = directory.path().join("default-v2.kspwallet");
|
||||
let explicit_v1_destination = directory.path().join("explicit-v1.kspwallet");
|
||||
let keypair = test_keypair();
|
||||
let mut source = cli_json(&keypair);
|
||||
let runtime = runtime();
|
||||
let default_owner = runtime
|
||||
.block_on(crate::import_wallet_transfer(
|
||||
default_destination.as_path(),
|
||||
source.as_slice(),
|
||||
crate::WalletTransferFormat::SolanaCliJson,
|
||||
crate::OwnerPassword::new(std::string::String::from("pre016-default-import-owner")),
|
||||
std::option::Option::None,
|
||||
crate::WalletCreateMetadata::default(),
|
||||
))
|
||||
.expect("default transfer import must succeed");
|
||||
assert_eq!(default_owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
let default_bytes = std::fs::read(default_destination).expect("default imported Wallet must be readable");
|
||||
assert_eq!(crate::detect_wallet_format(default_bytes.as_slice()).expect("default imported Wallet must detect"), crate::WalletFormat::V2);
|
||||
let explicit_owner = runtime
|
||||
.block_on(crate::import_wallet_transfer_v1(
|
||||
explicit_v1_destination.as_path(),
|
||||
source.as_slice(),
|
||||
crate::WalletTransferFormat::SolanaCliJson,
|
||||
crate::OwnerPassword::new(std::string::String::from("pre016-explicit-v1-import-owner")),
|
||||
std::option::Option::None,
|
||||
crate::WalletCreateMetadataV1::default(),
|
||||
))
|
||||
.expect("explicit V1 transfer import must remain available");
|
||||
assert_eq!(explicit_owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V1);
|
||||
source.zeroize();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transfer_file_import_is_non_destructive_and_bounded() {
|
||||
let directory = tempfile::tempdir().expect("Wallet transfer test directory must be creatable");
|
||||
|
||||
126
crates/ksp-wallet-lib/unit_tests/wallet_v2.rs
Normal file
126
crates/ksp-wallet-lib/unit_tests/wallet_v2.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
// file: crates/ksp-wallet-lib/unit_tests/wallet_v2.rs
|
||||
// version: 1
|
||||
|
||||
const V1_FULL_VECTOR: &[u8] = include_bytes!("../tests/fixtures/kspwallet_v1_full_vector.json");
|
||||
|
||||
fn runtime() -> tokio::runtime::Runtime {
|
||||
return tokio::runtime::Builder::new_current_thread().build().expect("Wallet V2 test runtime must build");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_create_open_and_generic_dispatch_preserve_authorized_identity() {
|
||||
let runtime = runtime();
|
||||
let owner_password_text = std::string::String::from("pre016-v2-owner-password");
|
||||
let view_password_text = std::string::String::from("pre016-v2-view-password");
|
||||
let owner = runtime
|
||||
.block_on(crate::create_wallet_v2(
|
||||
crate::OwnerPassword::new(owner_password_text.clone()),
|
||||
std::option::Option::Some(crate::ViewPassword::new(view_password_text.clone())),
|
||||
crate::WalletCreateMetadataV1::new(std::option::Option::Some(std::string::String::from("pre016-v2")), std::vec::Vec::new()),
|
||||
))
|
||||
.expect("V2 wallet creation must succeed");
|
||||
assert_eq!(owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
let pubkey = *owner.pubkey();
|
||||
let bytes = owner.to_native_bytes().expect("V2 owner must serialize native bytes");
|
||||
assert_eq!(crate::detect_wallet_format(bytes.as_slice()).expect("V2 framing must detect"), crate::WalletFormat::V2);
|
||||
assert!(owner.to_json_bytes().is_err());
|
||||
let view = runtime
|
||||
.block_on(crate::open_wallet_view(bytes.as_slice(), crate::ViewPassword::new(view_password_text)))
|
||||
.expect("generic VIEW open must dispatch V2");
|
||||
assert_eq!(view.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
assert_eq!(*view.pubkey(), pubkey);
|
||||
assert_eq!(view.alias(), std::option::Option::Some("pre016-v2"));
|
||||
let reopened = runtime
|
||||
.block_on(crate::open_wallet_owner(bytes.as_slice(), crate::OwnerPassword::new(owner_password_text)))
|
||||
.expect("generic OWNER open must dispatch V2");
|
||||
assert_eq!(reopened.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
assert_eq!(*reopened.pubkey(), pubkey);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_dispatch_keeps_v1_read_compatibility() {
|
||||
let runtime = runtime();
|
||||
assert_eq!(crate::detect_wallet_format(V1_FULL_VECTOR).expect("V1 framing must detect"), crate::WalletFormat::V1);
|
||||
let locked = crate::inspect_locked_wallet(V1_FULL_VECTOR).expect("generic inspect must keep V1 compatibility");
|
||||
assert_eq!(locked.format_version(), crate::KSPWALLET_FORMAT_VERSION_V1);
|
||||
let owner = runtime
|
||||
.block_on(crate::open_wallet_owner(V1_FULL_VECTOR, crate::OwnerPassword::new(std::string::String::from("pre005-owner-password"))))
|
||||
.expect("generic OWNER open must dispatch V1");
|
||||
assert_eq!(owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_file_creation_is_v2_and_v2_administration_stays_v2() {
|
||||
let directory = tempfile::Builder::new().prefix("ksp-pre016-v2-").tempdir().expect("V2 test directory must be creatable");
|
||||
let path = directory.path().join("default.kspwallet");
|
||||
let runtime = runtime();
|
||||
let mut owner = runtime
|
||||
.block_on(crate::create_wallet_file(
|
||||
path.as_path(),
|
||||
crate::OwnerPassword::new(std::string::String::from("pre016-default-owner")),
|
||||
std::option::Option::Some(crate::ViewPassword::new(std::string::String::from("pre016-default-view"))),
|
||||
crate::WalletCreateMetadataV1::default(),
|
||||
))
|
||||
.expect("default file creation must succeed");
|
||||
assert_eq!(owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
let locked = runtime.block_on(crate::inspect_locked_wallet_file(path.as_path())).expect("generic inspect must read default file");
|
||||
assert_eq!(locked.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
runtime
|
||||
.block_on(owner.update_alias(path.as_path(), std::option::Option::Some(std::string::String::from("pre016-updated"))))
|
||||
.expect("V2 metadata update must persist in V2");
|
||||
runtime
|
||||
.block_on(owner.rotate_owner_password(path.as_path(), crate::OwnerPassword::new(std::string::String::from("pre016-owner-rotated"))))
|
||||
.expect("V2 OWNER rotation must persist in V2");
|
||||
runtime
|
||||
.block_on(owner.rotate_view_password(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-rotated"))))
|
||||
.expect("V2 VIEW rotation by OWNER must persist in V2");
|
||||
let reopened_owner = runtime
|
||||
.block_on(crate::open_wallet_owner_file(path.as_path(), crate::OwnerPassword::new(std::string::String::from("pre016-owner-rotated"))))
|
||||
.expect("rotated V2 OWNER must reopen through generic dispatch");
|
||||
assert_eq!(reopened_owner.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
assert_eq!(reopened_owner.alias(), std::option::Option::Some("pre016-updated"));
|
||||
let reopened_view = runtime
|
||||
.block_on(crate::open_wallet_view_file(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-rotated"))))
|
||||
.expect("rotated V2 VIEW must reopen through generic dispatch");
|
||||
assert_eq!(reopened_view.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
assert_eq!(reopened_view.alias(), std::option::Option::Some("pre016-updated"));
|
||||
runtime.block_on(owner.disable_view(path.as_path())).expect("V2 strong VIEW disable must persist");
|
||||
let disabled = runtime.block_on(crate::inspect_locked_wallet_file(path.as_path())).expect("disabled V2 must remain inspectable");
|
||||
assert!(!disabled.view_enabled());
|
||||
let old_view = runtime.block_on(crate::open_wallet_view_file(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-rotated"))));
|
||||
assert_eq!(old_view.expect_err("disabled V2 VIEW must not reopen").code(), crate::ERROR_CODE_CAPABILITY_INSUFFICIENT);
|
||||
runtime
|
||||
.block_on(owner.recreate_view(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-recreated"))))
|
||||
.expect("V2 strong VIEW recreation must persist");
|
||||
let mut recreated_view = runtime
|
||||
.block_on(crate::open_wallet_view_file(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-recreated"))))
|
||||
.expect("recreated V2 VIEW must open");
|
||||
runtime
|
||||
.block_on(recreated_view.rotate_view_password(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-self-rotated"))))
|
||||
.expect("V2 VIEW self-rotation must persist");
|
||||
let final_view = runtime
|
||||
.block_on(crate::open_wallet_view_file(path.as_path(), crate::ViewPassword::new(std::string::String::from("pre016-view-self-rotated"))))
|
||||
.expect("self-rotated V2 VIEW must reopen");
|
||||
assert_eq!(final_view.format_version(), crate::KSPWALLET_FORMAT_VERSION_V2);
|
||||
let bytes = std::fs::read(path).expect("V2 file must remain readable for framing canary");
|
||||
assert_eq!(crate::detect_wallet_format(bytes.as_slice()).expect("administered file must still be V2"), crate::WalletFormat::V2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_versioned_readers_reject_the_other_native_wire_format() {
|
||||
let runtime = runtime();
|
||||
let v1_as_v2 =
|
||||
runtime.block_on(crate::open_wallet_owner_v2(V1_FULL_VECTOR, crate::OwnerPassword::new(std::string::String::from("irrelevant-before-v2-parse"))));
|
||||
assert_eq!(v1_as_v2.expect_err("explicit V2 reader must reject V1 framing").code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
let owner = runtime
|
||||
.block_on(crate::create_wallet_v2(
|
||||
crate::OwnerPassword::new(std::string::String::from("pre016-strict-v2-owner")),
|
||||
std::option::Option::None,
|
||||
crate::WalletCreateMetadata::default(),
|
||||
))
|
||||
.expect("strict-reader V2 fixture creation must succeed");
|
||||
let bytes = owner.to_native_bytes().expect("strict-reader V2 fixture must serialize");
|
||||
let v2_as_v1 =
|
||||
runtime.block_on(crate::open_wallet_owner_v1(bytes.as_slice(), crate::OwnerPassword::new(std::string::String::from("irrelevant-before-v1-parse"))));
|
||||
assert_eq!(v2_as_v1.expect_err("explicit V1 reader must reject V2 framing").code(), crate::ERROR_CODE_FORMAT_INVALID);
|
||||
}
|
||||
Reference in New Issue
Block a user