v0.2.6-pre.003
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/registry.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
#[test]
|
||||
fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
|
||||
@@ -7,29 +7,21 @@ fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
|
||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||
if let std::result::Result::Ok(registry) = registry {
|
||||
let descriptors: std::vec::Vec<&crate::ConfigFileDescriptor> = registry.descriptors().collect();
|
||||
assert_eq!(descriptors.len(), 5);
|
||||
assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_STD_LOGGING);
|
||||
assert_eq!(descriptors[0].kind(), crate::ConfigFileKind::Config);
|
||||
assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_STD_LOGGING_FILENAME));
|
||||
let logging_schema_file_id = descriptors[0].schema_file_id();
|
||||
assert!(logging_schema_file_id.is_some(), "logging descriptor should expose its validation schema");
|
||||
if let std::option::Option::Some(schema_file_id) = logging_schema_file_id {
|
||||
assert_eq!(schema_file_id.as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||
}
|
||||
assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT);
|
||||
assert_eq!(descriptors[1].kind(), crate::ConfigFileKind::Config);
|
||||
assert_eq!(descriptors[1].filename(), std::path::Path::new(crate::DEFAULT_STD_TRANSPORT_FILENAME));
|
||||
let transport_schema_file_id = descriptors[1].schema_file_id();
|
||||
assert!(transport_schema_file_id.is_some(), "transport descriptor should expose its validation schema");
|
||||
if let std::option::Option::Some(schema_file_id) = transport_schema_file_id {
|
||||
assert_eq!(schema_file_id.as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||
}
|
||||
assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE);
|
||||
assert_eq!(descriptors[2].kind(), crate::ConfigFileKind::Schema);
|
||||
assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||
assert_eq!(descriptors[3].kind(), crate::ConfigFileKind::Schema);
|
||||
assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||
assert_eq!(descriptors[4].kind(), crate::ConfigFileKind::Schema);
|
||||
assert_eq!(descriptors.len(), 8);
|
||||
assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
|
||||
assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_WALLET_DESK_FILENAME));
|
||||
assert_eq!(descriptors[0].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE));
|
||||
assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_STD_LOGGING);
|
||||
assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT);
|
||||
assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_STD_WALLET);
|
||||
assert_eq!(descriptors[3].filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME));
|
||||
assert_eq!(descriptors[3].schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_STD_WALLET));
|
||||
assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE);
|
||||
assert_eq!(descriptors[5].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||
assert_eq!(descriptors[6].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||
assert_eq!(descriptors[7].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_WALLET);
|
||||
assert!(descriptors[0..4].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Config));
|
||||
assert!(descriptors[4..8].iter().all(|descriptor| return descriptor.kind() == crate::ConfigFileKind::Schema));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +110,39 @@ fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defaults_register_wallet_document_schema_and_wallet_desk_composite() {
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||
if let std::result::Result::Ok(registry) = registry {
|
||||
let wallet_id = crate::ConfigFileId::new(crate::FILE_ID_STD_WALLET);
|
||||
let wallet_schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_WALLET);
|
||||
let composite_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
|
||||
assert!(wallet_id.is_ok());
|
||||
assert!(wallet_schema_id.is_ok());
|
||||
assert!(composite_id.is_ok());
|
||||
if let (std::result::Result::Ok(wallet_id), std::result::Result::Ok(wallet_schema_id), std::result::Result::Ok(composite_id)) =
|
||||
(wallet_id, wallet_schema_id, composite_id)
|
||||
{
|
||||
let wallet = registry.descriptor(&wallet_id);
|
||||
let wallet_schema = registry.descriptor(&wallet_schema_id);
|
||||
let composite = registry.descriptor(&composite_id);
|
||||
assert!(wallet.is_ok());
|
||||
assert!(wallet_schema.is_ok());
|
||||
assert!(composite.is_ok());
|
||||
if let (std::result::Result::Ok(wallet), std::result::Result::Ok(wallet_schema), std::result::Result::Ok(composite)) =
|
||||
(wallet, wallet_schema, composite)
|
||||
{
|
||||
assert_eq!(wallet.filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_FILENAME));
|
||||
assert_eq!(wallet.schema_file_id(), std::option::Option::Some(&wallet_schema_id));
|
||||
assert_eq!(wallet_schema.filename(), std::path::Path::new(crate::DEFAULT_STD_WALLET_SCHEMA_FILENAME));
|
||||
assert_eq!(composite.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_KSP_APP_WALLET_DESK_FILENAME));
|
||||
assert_eq!(composite.schema_file_id().map(crate::ConfigFileId::as_str), std::option::Option::Some(crate::FILE_ID_SCHEMA_COMPOSITE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_path_uses_descriptor_kind_to_select_bootstrap_root() {
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
|
||||
200
crates/ksp-config-lib/unit_tests/wallet.rs
Normal file
200
crates/ksp-config-lib/unit_tests/wallet.rs
Normal file
@@ -0,0 +1,200 @@
|
||||
// file: crates/ksp-config-lib/unit_tests/wallet.rs
|
||||
// version: 1
|
||||
|
||||
#[test]
|
||||
fn default_wallet_config_resolves_global_root_without_subdirectory() {
|
||||
let engine = workspace_engine();
|
||||
assert!(engine.is_ok(), "workspace engine should be constructible: {engine:?}");
|
||||
if let std::result::Result::Ok(engine) = engine {
|
||||
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||
let resolved = engine.load_resolved_wallet_config(std::option::Option::Some("default"), &environment);
|
||||
assert!(resolved.is_ok(), "default Wallet profile should resolve: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert_eq!(resolved.file_id().as_str(), crate::FILE_ID_STD_WALLET);
|
||||
assert_eq!(resolved.profile_id(), "default");
|
||||
assert!(resolved.wallets_subdirectory().is_none());
|
||||
assert!(resolved.wallets_directory().is_absolute());
|
||||
assert_eq!(resolved.effective_wallets_directory(), resolved.wallets_directory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_profile_appends_relative_subdirectory() {
|
||||
let engine = workspace_engine();
|
||||
assert!(engine.is_ok(), "workspace engine should be constructible: {engine:?}");
|
||||
if let std::result::Result::Ok(engine) = engine {
|
||||
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||
let resolved = engine.load_resolved_wallet_config(std::option::Option::Some("tests"), &environment);
|
||||
assert!(resolved.is_ok(), "tests Wallet profile should resolve: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert_eq!(resolved.wallets_subdirectory(), std::option::Option::Some(std::path::Path::new("tests")));
|
||||
let expected = resolved.wallets_directory().join("tests");
|
||||
assert_eq!(resolved.effective_wallets_directory(), expected.as_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_directory_environment_override_uses_config_precedence() {
|
||||
let engine = workspace_engine();
|
||||
assert!(engine.is_ok(), "workspace engine should be constructible: {engine:?}");
|
||||
if let std::result::Result::Ok(engine) = engine {
|
||||
let mut process = std::collections::BTreeMap::<String, String>::new();
|
||||
process.insert("KSP_WALLETS_DIRECTORY".to_owned(), "process-wallets".to_owned());
|
||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||
dotenv.insert("KSP_WALLETS_DIRECTORY".to_owned(), "dotenv-wallets".to_owned());
|
||||
let environment = crate::ConfigEnvironment::from_maps(process, dotenv);
|
||||
let resolved = engine.load_resolved_wallet_config(std::option::Option::Some("default"), &environment);
|
||||
assert!(resolved.is_ok(), "Wallet environment override should resolve: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert!(resolved.wallets_directory().ends_with("process-wallets"));
|
||||
assert!(resolved.effective().provenance_at("/wallets_directory").is_some());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsafe_wallet_subdirectories_are_rejected() {
|
||||
for (name, value) in [("absolute", "/escape"), ("parent", "../escape"), ("nested-parent", "tests/../escape"), ("current", "./tests")] {
|
||||
let fixture = fixture_roots(name);
|
||||
let prepared = prepare_wallet_fixture(&fixture, value);
|
||||
assert!(prepared.is_ok(), "wallet fixture should be writable: {prepared:?}");
|
||||
if prepared.is_ok() {
|
||||
let engine = fixture_engine(&fixture);
|
||||
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_WALLET);
|
||||
assert!(engine.is_ok(), "fixture engine should be constructible: {engine:?}");
|
||||
if let (std::result::Result::Ok(engine), std::result::Result::Ok(file_id)) = (engine, file_id) {
|
||||
let result = engine.load_validated_document(&file_id);
|
||||
assert!(result.is_err(), "unsafe wallets_subdirectory must be rejected: {name} {result:?}");
|
||||
if let std::result::Result::Err(error) = result {
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_DOCUMENT_SEMANTIC_INVALID);
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanup_fixture(&fixture);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn composite_selected_wallet_profile_preserves_composite_provenance() {
|
||||
let engine = workspace_engine();
|
||||
assert!(engine.is_ok(), "workspace engine should be constructible: {engine:?}");
|
||||
if let std::result::Result::Ok(engine) = engine {
|
||||
let composite_file_id = crate::ConfigFileId::new(crate::FILE_ID_COMPOSITE_KSP_APP_WALLET_DESK);
|
||||
assert!(composite_file_id.is_ok(), "Wallet Desk composite file_id should be valid: {composite_file_id:?}");
|
||||
if let std::result::Result::Ok(composite_file_id) = composite_file_id {
|
||||
let composite = engine.load_resolved_composite(&composite_file_id, std::option::Option::None);
|
||||
assert!(composite.is_ok(), "Wallet Desk composite should resolve: {composite:?}");
|
||||
if let std::result::Result::Ok(composite) = composite {
|
||||
let component = composite.component("wallet");
|
||||
assert!(component.is_some(), "Wallet Desk composite should expose wallet component");
|
||||
if let std::option::Option::Some(component) = component {
|
||||
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||
let resolved = engine.resolve_wallet_config_profile(component.resolved(), &environment);
|
||||
assert!(resolved.is_ok(), "composite Wallet profile should map through the runtime adapter: {resolved:?}");
|
||||
if let std::result::Result::Ok(resolved) = resolved {
|
||||
assert_eq!(resolved.profile_id(), "default");
|
||||
assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::Composite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn secret_wallet_directory_placeholder_is_rejected_by_effective_adapter() {
|
||||
let fixture = fixture_roots("secret-root");
|
||||
let prepared = prepare_wallet_root_fixture(&fixture, "${KSP_SECRET_WALLET_DIRECTORY}", std::option::Option::None);
|
||||
assert!(prepared.is_ok(), "wallet fixture should be writable: {prepared:?}");
|
||||
if prepared.is_ok() {
|
||||
let engine = fixture_engine(&fixture);
|
||||
assert!(engine.is_ok(), "fixture engine should be constructible: {engine:?}");
|
||||
if let std::result::Result::Ok(engine) = engine {
|
||||
let mut process = std::collections::BTreeMap::<String, String>::new();
|
||||
process.insert("KSP_SECRET_WALLET_DIRECTORY".to_owned(), "hidden-wallets".to_owned());
|
||||
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
||||
let result = engine.load_resolved_wallet_config(std::option::Option::Some("default"), &environment);
|
||||
assert!(result.is_err(), "Wallet path Config must reject Secret environment sensitivity: {result:?}");
|
||||
if let std::result::Result::Err(error) = result {
|
||||
assert_eq!(error.code(), crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID);
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanup_fixture(&fixture);
|
||||
}
|
||||
|
||||
fn workspace_engine() -> ksp_core_lib::Result<crate::ConfigDocumentEngine> {
|
||||
let root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let bootstrap = crate::ConfigBootstrapOptions::from_paths(root.join("config"), root.join("config/schemas"));
|
||||
let bootstrap = match bootstrap {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
return match registry {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(crate::ConfigDocumentEngine::new(bootstrap, value)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
fn fixture_engine(fixture: &FixtureRoots) -> ksp_core_lib::Result<crate::ConfigDocumentEngine> {
|
||||
let bootstrap = crate::ConfigBootstrapOptions::from_paths(fixture.config.as_path(), fixture.schemas.as_path());
|
||||
let bootstrap = match bootstrap {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let registry = crate::ConfigFileRegistry::defaults();
|
||||
return match registry {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(crate::ConfigDocumentEngine::new(bootstrap, value)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
fn prepare_wallet_fixture(fixture: &FixtureRoots, subdirectory: &str) -> std::io::Result<()> {
|
||||
return prepare_wallet_root_fixture(fixture, "wallets", std::option::Option::Some(subdirectory));
|
||||
}
|
||||
|
||||
fn prepare_wallet_root_fixture(fixture: &FixtureRoots, root: &str, subdirectory: std::option::Option<&str>) -> std::io::Result<()> {
|
||||
cleanup_fixture(fixture);
|
||||
let config = std::fs::create_dir_all(fixture.config.as_path());
|
||||
if let std::result::Result::Err(error) = config {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let schemas = std::fs::create_dir_all(fixture.schemas.as_path());
|
||||
if let std::result::Result::Err(error) = schemas {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let schema = std::fs::copy(workspace.join("config/schemas/std.wallet.schema.json"), fixture.schemas.join(crate::DEFAULT_STD_WALLET_SCHEMA_FILENAME));
|
||||
if let std::result::Result::Err(error) = schema {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let profile = match subdirectory {
|
||||
std::option::Option::Some(value) => format!(r#"{{"profile_id":"default","wallets_subdirectory":"{value}"}}"#),
|
||||
std::option::Option::None => "{\"profile_id\":\"default\"}".to_owned(),
|
||||
};
|
||||
let source = format!(r#"{{"format_version":1,"wallets_directory":"{root}","default_profile":"default","profiles":[{profile}]}}"#);
|
||||
return std::fs::write(fixture.config.join(crate::DEFAULT_STD_WALLET_FILENAME), source);
|
||||
}
|
||||
|
||||
fn fixture_roots(name: &str) -> FixtureRoots {
|
||||
let root = std::env::temp_dir().join(format!("ksp-config-wallet-{name}-{}", std::process::id()));
|
||||
return FixtureRoots { config: root.join("config"), root: root.clone(), schemas: root.join("schemas") };
|
||||
}
|
||||
|
||||
fn cleanup_fixture(fixture: &FixtureRoots) {
|
||||
let removal = std::fs::remove_dir_all(fixture.root.as_path());
|
||||
if let std::result::Result::Err(error) = removal
|
||||
&& error.kind() != std::io::ErrorKind::NotFound
|
||||
{
|
||||
eprintln!("unable to cleanup Config Wallet fixture {}: {error}", fixture.root.display());
|
||||
}
|
||||
}
|
||||
|
||||
struct FixtureRoots {
|
||||
config: std::path::PathBuf,
|
||||
root: std::path::PathBuf,
|
||||
schemas: std::path::PathBuf,
|
||||
}
|
||||
Reference in New Issue
Block a user