309 lines
17 KiB
Rust
309 lines
17 KiB
Rust
// file: crates/ksp-config-lib/unit_tests/registry.rs
|
|
// version: 5
|
|
|
|
#[test]
|
|
fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
if let std::result::Result::Ok(registry) = registry {
|
|
let descriptors: std::vec::Vec<&super::ConfigFileDescriptor> = registry.descriptors().collect();
|
|
assert_eq!(descriptors.len(), 5);
|
|
assert_eq!(descriptors[0].file_id().as_str(), super::FILE_ID_STD_LOGGING);
|
|
assert_eq!(descriptors[0].kind(), super::ConfigFileKind::Config);
|
|
assert_eq!(descriptors[0].filename(), std::path::Path::new(super::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(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
}
|
|
assert_eq!(descriptors[1].file_id().as_str(), super::FILE_ID_STD_TRANSPORT);
|
|
assert_eq!(descriptors[1].kind(), super::ConfigFileKind::Config);
|
|
assert_eq!(descriptors[1].filename(), std::path::Path::new(super::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(), super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
|
}
|
|
assert_eq!(descriptors[2].file_id().as_str(), super::FILE_ID_SCHEMA_COMPOSITE);
|
|
assert_eq!(descriptors[2].kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(descriptors[3].file_id().as_str(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
assert_eq!(descriptors[3].kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(descriptors[4].file_id().as_str(), super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
|
assert_eq!(descriptors[4].kind(), super::ConfigFileKind::Schema);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn descriptors_reflect_filename_overrides_without_changing_logical_metadata() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
|
let overridden = registry.with_filename_override(&file_id, "profiles/desktop.logging.json");
|
|
assert!(overridden.is_ok(), "filename override should remain valid: {overridden:?}");
|
|
if let std::result::Result::Ok(overridden) = overridden {
|
|
let mut found: std::option::Option<&super::ConfigFileDescriptor> = std::option::Option::None;
|
|
for descriptor in overridden.descriptors() {
|
|
if descriptor.file_id() == &file_id {
|
|
found = std::option::Option::Some(descriptor);
|
|
}
|
|
}
|
|
assert!(found.is_some(), "public descriptor inventory should retain the overridden logging descriptor");
|
|
if let std::option::Option::Some(descriptor) = found {
|
|
assert_eq!(descriptor.file_id(), &file_id);
|
|
assert_eq!(descriptor.kind(), super::ConfigFileKind::Config);
|
|
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/desktop.logging.json"));
|
|
let schema_file_id = descriptor.schema_file_id();
|
|
assert!(schema_file_id.is_some(), "filename override should preserve schema association");
|
|
if let std::option::Option::Some(schema_file_id) = schema_file_id {
|
|
assert_eq!(schema_file_id.as_str(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn defaults_register_logging_document_and_schema_with_distinct_roots() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
if let std::result::Result::Ok(registry) = registry {
|
|
let logging_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
|
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
assert!(logging_id.is_ok(), "logging file_id should be valid: {logging_id:?}");
|
|
assert!(schema_id.is_ok(), "logging schema file_id should be valid: {schema_id:?}");
|
|
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
|
let logging = registry.descriptor(&logging_id);
|
|
let schema = registry.descriptor(&schema_id);
|
|
assert!(logging.is_ok(), "logging descriptor should exist: {logging:?}");
|
|
assert!(schema.is_ok(), "logging schema descriptor should exist: {schema:?}");
|
|
if let (std::result::Result::Ok(logging), std::result::Result::Ok(schema)) = (logging, schema) {
|
|
assert_eq!(logging.kind(), super::ConfigFileKind::Config);
|
|
assert_eq!(logging.filename(), std::path::Path::new(super::DEFAULT_STD_LOGGING_FILENAME));
|
|
let logging_schema = logging.schema_file_id();
|
|
assert!(logging_schema.is_some(), "logging document should declare its validation schema");
|
|
if let std::option::Option::Some(logging_schema) = logging_schema {
|
|
assert_eq!(logging_schema, &schema_id);
|
|
}
|
|
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_STD_LOGGING_SCHEMA_FILENAME));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
if let std::result::Result::Ok(registry) = registry {
|
|
let transport_id = super::ConfigFileId::new(super::FILE_ID_STD_TRANSPORT);
|
|
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
|
assert!(transport_id.is_ok(), "transport file_id should be valid: {transport_id:?}");
|
|
assert!(schema_id.is_ok(), "transport schema file_id should be valid: {schema_id:?}");
|
|
if let (std::result::Result::Ok(transport_id), std::result::Result::Ok(schema_id)) = (transport_id, schema_id) {
|
|
let transport = registry.descriptor(&transport_id);
|
|
let schema = registry.descriptor(&schema_id);
|
|
assert!(transport.is_ok(), "transport descriptor should exist: {transport:?}");
|
|
assert!(schema.is_ok(), "transport schema descriptor should exist: {schema:?}");
|
|
if let (std::result::Result::Ok(transport), std::result::Result::Ok(schema)) = (transport, schema) {
|
|
assert_eq!(transport.kind(), super::ConfigFileKind::Config);
|
|
assert_eq!(transport.filename(), std::path::Path::new(super::DEFAULT_STD_TRANSPORT_FILENAME));
|
|
assert_eq!(transport.schema_file_id(), std::option::Option::Some(&schema_id));
|
|
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn resolve_path_uses_descriptor_kind_to_select_bootstrap_root() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
let bootstrap = crate::ConfigBootstrapOptions::from_paths("runtime-config", "runtime-schemas");
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
assert!(bootstrap.is_ok(), "bootstrap paths should be valid: {bootstrap:?}");
|
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(bootstrap)) = (registry, bootstrap) {
|
|
let logging_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
|
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
|
let logging = registry.resolve_path(&bootstrap, &logging_id);
|
|
let schema = registry.resolve_path(&bootstrap, &schema_id);
|
|
assert!(logging.is_ok(), "logging path should resolve: {logging:?}");
|
|
assert!(schema.is_ok(), "schema path should resolve: {schema:?}");
|
|
if let std::result::Result::Ok(logging) = logging {
|
|
assert_eq!(logging, std::path::PathBuf::from("runtime-config/std.logging.json"));
|
|
}
|
|
if let std::result::Result::Ok(schema) = schema {
|
|
assert_eq!(schema, std::path::PathBuf::from("runtime-schemas/std.logging.schema.json"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn cli_filemap_override_replaces_filename_and_last_value_wins() {
|
|
let args = [
|
|
std::ffi::OsString::from("ksp-app"),
|
|
std::ffi::OsString::from("--filemap=cfg.std.logging=first.logging.json"),
|
|
std::ffi::OsString::from("--other-option"),
|
|
std::ffi::OsString::from("--filemap=cfg.std.logging=profiles/custom.logging.json"),
|
|
];
|
|
let registry = super::ConfigFileRegistry::from_args(&args);
|
|
assert!(registry.is_ok(), "filemap overrides should parse: {registry:?}");
|
|
if let std::result::Result::Ok(registry) = registry {
|
|
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
|
if let std::result::Result::Ok(file_id) = file_id {
|
|
let descriptor = registry.descriptor(&file_id);
|
|
assert!(descriptor.is_ok(), "logging descriptor should remain registered: {descriptor:?}");
|
|
if let std::result::Result::Ok(descriptor) = descriptor {
|
|
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/custom.logging.json"));
|
|
assert_eq!(descriptor.kind(), super::ConfigFileKind::Config);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn programmatic_override_preserves_file_id_and_kind() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
let file_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
assert!(file_id.is_ok(), "schema file_id should be valid: {file_id:?}");
|
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
|
let overridden = registry.with_filename_override(&file_id, "alternate/logging.schema.json");
|
|
assert!(overridden.is_ok(), "programmatic override should be valid: {overridden:?}");
|
|
if let std::result::Result::Ok(overridden) = overridden {
|
|
let descriptor = overridden.descriptor(&file_id);
|
|
if let std::result::Result::Ok(descriptor) = descriptor {
|
|
assert_eq!(descriptor.file_id(), &file_id);
|
|
assert_eq!(descriptor.kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(descriptor.filename(), std::path::Path::new("alternate/logging.schema.json"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_file_id_override_is_rejected() {
|
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--filemap=cfg.unknown=unknown.json")];
|
|
let result = super::ConfigFileRegistry::from_args(&args);
|
|
assert!(result.is_err(), "unknown logical files must not be introduced by CLI override");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_file_ids_are_rejected() {
|
|
for value in ["", ".cfg", "cfg.", "cfg..logging", "CFG.logging", "cfg/logging"] {
|
|
let result = super::ConfigFileId::new(value);
|
|
assert!(result.is_err(), "invalid file_id must be rejected: {value}");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_INVALID);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn absolute_and_traversing_filenames_are_rejected() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
|
let traversal = registry.clone().with_filename_override(&file_id, "../outside.json");
|
|
let current = registry.clone().with_filename_override(&file_id, "./logging.json");
|
|
let absolute = registry.with_filename_override(&file_id, absolute_fixture_path());
|
|
assert_mapping_invalid(traversal);
|
|
assert_mapping_invalid(current);
|
|
assert_mapping_invalid(absolute);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn malformed_filemap_arguments_are_rejected() {
|
|
for argument in ["--filemap", "--filemap=cfg.std.logging", "--filemap==logging.json", "--filemap=cfg.std.logging="] {
|
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from(argument)];
|
|
let result = super::ConfigFileRegistry::from_args(&args);
|
|
assert!(result.is_err(), "malformed filemap argument must be rejected: {argument}");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_registry_ids_are_rejected() {
|
|
let first = super::ConfigFileDescriptor::new("cfg.duplicate", super::ConfigFileKind::Config, "first.json", std::option::Option::None);
|
|
let second = super::ConfigFileDescriptor::new("cfg.duplicate", super::ConfigFileKind::Config, "second.json", std::option::Option::None);
|
|
assert!(first.is_ok(), "first descriptor should be valid: {first:?}");
|
|
assert!(second.is_ok(), "second descriptor should be valid: {second:?}");
|
|
if let (std::result::Result::Ok(first), std::result::Result::Ok(second)) = (first, second) {
|
|
let result = super::build_registry([first, second]);
|
|
assert!(result.is_err(), "duplicate file_ids must be rejected");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_DUPLICATE);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn descriptor_kind_must_match_file_id_namespace() {
|
|
let result = super::ConfigFileDescriptor::new("schema.invalid-kind", super::ConfigFileKind::Config, "invalid.json", std::option::Option::None);
|
|
assert!(result.is_err(), "descriptor kind mismatch must be rejected");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn config_schema_association_must_reference_registered_schema_descriptor() {
|
|
let config = super::ConfigFileDescriptor::new("cfg.test", super::ConfigFileKind::Config, "test.json", std::option::Option::Some("schema.test"));
|
|
assert!(config.is_ok(), "config descriptor should be valid before registry association validation: {config:?}");
|
|
if let std::result::Result::Ok(config) = config {
|
|
let result = super::build_registry([config]);
|
|
assert!(result.is_err(), "registry must reject a missing schema association");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn assert_mapping_invalid<T: std::fmt::Debug>(result: ksp_core_lib::Result<T>) {
|
|
assert!(result.is_err(), "invalid filename must be rejected: {result:?}");
|
|
if let std::result::Result::Err(error) = result {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
|
}
|
|
}
|
|
|
|
fn absolute_fixture_path() -> std::path::PathBuf {
|
|
let mut path = std::env::temp_dir();
|
|
path.push("ksp-config-lib-absolute-mapping.json");
|
|
return path;
|
|
}
|
|
|
|
#[test]
|
|
fn defaults_register_generic_composite_schema_without_runtime_composite() {
|
|
let registry = super::ConfigFileRegistry::defaults();
|
|
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_COMPOSITE);
|
|
let runtime_id = super::ConfigFileId::new("cfg.composite.ksp-app-wallet-desk");
|
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
|
assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}");
|
|
assert!(runtime_id.is_ok(), "future composite runtime file_id syntax should be valid: {runtime_id:?}");
|
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(schema_id), std::result::Result::Ok(runtime_id)) = (registry, schema_id, runtime_id) {
|
|
let schema = registry.descriptor(&schema_id);
|
|
let runtime = registry.descriptor(&runtime_id);
|
|
assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}");
|
|
assert!(runtime.is_err(), "no fictitious runtime composite should be registered");
|
|
if let std::result::Result::Ok(schema) = schema {
|
|
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
|
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_COMPOSITE_SCHEMA_FILENAME));
|
|
}
|
|
if let std::result::Result::Err(error) = runtime {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
|
}
|
|
}
|
|
}
|