// file: crates/ksp-config-lib/unit_tests/packaging.rs // version: 2 fn write_packaged_registry(resource_root: &std::path::Path, marker: &str) -> std::io::Result<()> { let registry = crate::ConfigFileRegistry::defaults(); assert!(registry.is_ok(), "Config registry fixture should be constructible: {registry:?}"); if let std::result::Result::Ok(registry) = registry { for descriptor in registry.descriptors() { let root = match descriptor.kind() { crate::ConfigFileKind::Config => resource_root.join(crate::DEFAULT_CFG_PATH), crate::ConfigFileKind::Schema => resource_root.join(crate::DEFAULT_SCHEMA_PATH), }; let create = std::fs::create_dir_all(root.as_path()); if let std::result::Result::Err(error) = create { return std::result::Result::Err(error); } let path = root.join(descriptor.filename()); let content = format!("{marker}:{}", descriptor.file_id().as_str()); let write = std::fs::write(path, content.as_bytes()); if let std::result::Result::Err(error) = write { return std::result::Result::Err(error); } } } return std::result::Result::Ok(()); } #[test] fn packaged_runtime_seeds_configs_and_synchronizes_schemas_without_overwriting_user_config() { let resource = tempfile::tempdir(); assert!(resource.is_ok(), "resource fixture should be creatable: {resource:?}"); let runtime = tempfile::tempdir(); assert!(runtime.is_ok(), "runtime fixture should be creatable: {runtime:?}"); if let (std::result::Result::Ok(resource), std::result::Result::Ok(runtime)) = (resource, runtime) { let packaged = write_packaged_registry(resource.path(), "first"); assert!(packaged.is_ok(), "packaged Config fixture should be writable: {packaged:?}"); let first = super::prepare_packaged_runtime_at(resource.path(), runtime.path()); assert!(first.is_ok(), "first packaged runtime should prepare: {first:?}"); if let std::result::Result::Ok(first) = first { assert_eq!(first.runtime_root(), runtime.path()); assert_eq!(first.cfg_path(), runtime.path().join(crate::DEFAULT_CFG_PATH)); assert_eq!(first.schema_path(), runtime.path().join(crate::DEFAULT_SCHEMA_PATH)); let user_logging = first.cfg_path().join(crate::DEFAULT_STD_LOGGING_FILENAME); let user_write = std::fs::write(user_logging.as_path(), b"user-owned-config"); assert!(user_write.is_ok(), "Config fixture should be replaceable: {user_write:?}"); let packaged = write_packaged_registry(resource.path(), "second"); assert!(packaged.is_ok(), "updated packaged fixture should be writable: {packaged:?}"); let second = super::prepare_packaged_runtime_at(resource.path(), runtime.path()); assert!(second.is_ok(), "second packaged runtime should prepare: {second:?}"); let retained = std::fs::read(user_logging.as_path()); assert!(retained.is_ok(), "retained Config fixture should remain readable: {retained:?}"); if let std::result::Result::Ok(retained) = retained { assert_eq!(retained, b"user-owned-config"); } let schema = first.schema_path().join(crate::DEFAULT_STD_LOGGING_SCHEMA_FILENAME); let synchronized = std::fs::read_to_string(schema.as_path()); assert!(synchronized.is_ok(), "synchronized schema fixture should be readable: {synchronized:?}"); if let std::result::Result::Ok(synchronized) = synchronized { assert!(synchronized.starts_with("second:")); } } } } #[cfg(unix)] #[test] fn packaged_runtime_rejects_symlink_destination() { let resource = tempfile::tempdir(); assert!(resource.is_ok(), "resource fixture should be creatable: {resource:?}"); let runtime = tempfile::tempdir(); assert!(runtime.is_ok(), "runtime fixture should be creatable: {runtime:?}"); if let (std::result::Result::Ok(resource), std::result::Result::Ok(runtime)) = (resource, runtime) { let packaged = write_packaged_registry(resource.path(), "packaged"); assert!(packaged.is_ok(), "packaged Config fixture should be writable: {packaged:?}"); let cfg = runtime.path().join(crate::DEFAULT_CFG_PATH); let create = std::fs::create_dir_all(cfg.as_path()); assert!(create.is_ok(), "runtime Config root should be creatable: {create:?}"); let outside = runtime.path().join("outside.json"); let outside_write = std::fs::write(outside.as_path(), b"outside"); assert!(outside_write.is_ok(), "symlink target should be creatable: {outside_write:?}"); let link = cfg.join(crate::DEFAULT_STD_LOGGING_FILENAME); let link_result = std::os::unix::fs::symlink(outside.as_path(), link.as_path()); assert!(link_result.is_ok(), "symlink fixture should be creatable: {link_result:?}"); let result = super::prepare_packaged_runtime_at(resource.path(), runtime.path()); assert!(result.is_err(), "symlink destination must be rejected"); if let std::result::Result::Err(error) = result { assert_eq!(error.code(), crate::ERROR_CODE_PACKAGED_RUNTIME_PREPARATION_FAILED); } } }