553 lines
25 KiB
Rust
553 lines
25 KiB
Rust
// file: crates/ksp-config-lib/unit_tests/management.rs
|
|
// version: 5
|
|
|
|
static NEXT_FIXTURE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
|
|
|
struct ManagementFixture {
|
|
root: std::path::PathBuf,
|
|
config_path: std::path::PathBuf,
|
|
dotenv_path: std::path::PathBuf,
|
|
management: crate::ConfigManagement,
|
|
}
|
|
|
|
#[test]
|
|
fn raw_management_read_remains_available_for_schema_invalid_source() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let invalid = "{\n \"format_version\": 1\n}\n";
|
|
let write = std::fs::write(fixture.config_path.as_path(), invalid.as_bytes());
|
|
assert!(write.is_ok(), "schema-invalid management fixture should be written");
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let raw = fixture.management.read_source(&file_id);
|
|
assert!(raw.is_ok(), "raw management read should not require schema validity: {raw:?}");
|
|
if let std::result::Result::Ok(raw) = raw {
|
|
assert_eq!(raw.content(), invalid);
|
|
assert_eq!(raw.file_id(), &file_id);
|
|
let debug = format!("{raw:?}");
|
|
assert!(!debug.contains("format_version"), "raw source content must not be exposed by Debug");
|
|
}
|
|
let typed = fixture.management.load_logging_document();
|
|
assert!(typed.is_err(), "typed management load must still require a valid source document");
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn valid_raw_source_candidate_is_persisted_exactly_and_reports_reload() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let original = std::fs::read_to_string(fixture.config_path.as_path());
|
|
let original = match original {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let candidate = format!("{original} \n");
|
|
assert_ne!(candidate, original, "raw source candidate fixture must change the persisted bytes");
|
|
let corrupt = std::fs::write(fixture.config_path.as_path(), b"{\n");
|
|
assert!(corrupt.is_ok(), "existing managed source should be corruptible for repair test");
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let saved = fixture.management.save_source_candidate(&file_id, candidate.as_str());
|
|
assert!(saved.is_ok(), "valid raw source candidate should persist: {saved:?}");
|
|
if let std::result::Result::Ok(saved) = saved {
|
|
assert!(saved.source_changed());
|
|
assert!(saved.reload_required());
|
|
}
|
|
assert_eq!(std::fs::read_to_string(fixture.config_path.as_path()).ok(), std::option::Option::Some(candidate.clone()));
|
|
let reloaded = fixture.management.load_logging_document();
|
|
assert!(reloaded.is_ok(), "raw source persistence must leave a valid managed document: {reloaded:?}");
|
|
let unchanged = fixture.management.save_source_candidate(&file_id, candidate.as_str());
|
|
assert!(unchanged.is_ok(), "identical raw source candidate should succeed: {unchanged:?}");
|
|
if let std::result::Result::Ok(unchanged) = unchanged {
|
|
assert!(!unchanged.source_changed());
|
|
assert!(!unchanged.reload_required());
|
|
}
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_json_source_candidate_is_rejected_without_modifying_existing_file() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let before = std::fs::read(fixture.config_path.as_path());
|
|
let before = match before {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let saved = fixture.management.save_source_candidate(&file_id, "{");
|
|
assert!(saved.is_err(), "invalid JSON candidate must be rejected");
|
|
if let std::result::Result::Err(error) = saved {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_JSON_SYNTAX_INVALID);
|
|
}
|
|
assert_eq!(std::fs::read(fixture.config_path.as_path()).ok(), std::option::Option::Some(before));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn schema_invalid_source_candidate_is_rejected_without_modifying_existing_file() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let before = std::fs::read(fixture.config_path.as_path());
|
|
let before = match before {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let saved = fixture.management.save_source_candidate(&file_id, "{\"format_version\":1}");
|
|
assert!(saved.is_err(), "schema-invalid candidate must be rejected");
|
|
if let std::result::Result::Err(error) = saved {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_SCHEMA_VALIDATION_FAILED);
|
|
}
|
|
assert_eq!(std::fs::read(fixture.config_path.as_path()).ok(), std::option::Option::Some(before));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn semantic_invalid_source_candidate_is_rejected_without_modifying_existing_file() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let before = std::fs::read_to_string(fixture.config_path.as_path());
|
|
let before = match before {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let candidate_value = serde_json::from_str::<serde_json::Value>(before.as_str());
|
|
let mut candidate_value = match candidate_value {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
candidate_value["default_profile"] = serde_json::Value::String("missing".to_owned());
|
|
let candidate = serde_json::to_string_pretty(&candidate_value);
|
|
let candidate = match candidate {
|
|
std::result::Result::Ok(value) => format!("{value}\n"),
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
assert_ne!(candidate, before, "semantic-invalid fixture must alter the default profile");
|
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
|
let file_id = match file_id {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let saved = fixture.management.save_source_candidate(&file_id, candidate.as_str());
|
|
assert!(saved.is_err(), "semantic-invalid candidate must be rejected");
|
|
if let std::result::Result::Err(error) = saved {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_DOCUMENT_SEMANTIC_INVALID);
|
|
}
|
|
assert_eq!(std::fs::read_to_string(fixture.config_path.as_path()).ok(), std::option::Option::Some(before));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn raw_source_candidate_rejects_schema_and_unknown_file_ids_without_filesystem_escape() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let schema_file_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_LOGGING);
|
|
if let std::result::Result::Ok(schema_file_id) = schema_file_id {
|
|
let saved = fixture.management.save_source_candidate(&schema_file_id, "{}");
|
|
assert!(saved.is_err(), "schema file_id must not be writable through Config source management");
|
|
if let std::result::Result::Err(error) = saved {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_MANAGEMENT_OPERATION_INVALID);
|
|
}
|
|
}
|
|
let unknown_file_id = crate::ConfigFileId::new("cfg.not.registered");
|
|
if let std::result::Result::Ok(unknown_file_id) = unknown_file_id {
|
|
let saved = fixture.management.save_source_candidate(&unknown_file_id, "{}");
|
|
assert!(saved.is_err(), "unknown Config file_id must not become an arbitrary filesystem write");
|
|
if let std::result::Result::Err(error) = saved {
|
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
|
}
|
|
}
|
|
assert!(!fixture.root.join("not.registered").exists());
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn logging_file_ansi_mutator_completes_typed_management_surface() {
|
|
let filter = crate::LoggingOutputFilterConfig::new("info", std::vec!["*".to_owned()], std::vec!["*".to_owned()]);
|
|
let mut file = crate::LoggingFileConfig::new("file.test", true, "test.log", "daily", "human", filter);
|
|
assert!(!file.ansi());
|
|
file.set_ansi(true);
|
|
assert!(file.ansi());
|
|
}
|
|
|
|
#[test]
|
|
fn typed_logging_document_can_be_mutated_validated_and_persisted_atomically() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let document = fixture.management.load_logging_document();
|
|
assert!(document.is_ok(), "committed Logging document should load through management: {document:?}");
|
|
let mut document = match document {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
assert_eq!(document.format_version(), 1);
|
|
assert!(!document.default_profile().is_empty());
|
|
assert!(!document.profiles().is_empty());
|
|
document.set_logs_directory("managed-logs");
|
|
if let std::option::Option::Some(profile) = document.profiles_mut().first_mut() {
|
|
profile.set_default_filter("info");
|
|
}
|
|
let report = fixture.management.save_logging_document(&document);
|
|
assert!(report.is_ok(), "valid typed Logging mutation should persist: {report:?}");
|
|
if let std::result::Result::Ok(report) = report {
|
|
assert!(report.source_changed());
|
|
assert!(report.reload_required());
|
|
}
|
|
let persisted = std::fs::read_to_string(fixture.config_path.as_path());
|
|
assert!(persisted.is_ok(), "persisted Logging document should remain readable");
|
|
if let std::result::Result::Ok(persisted) = persisted {
|
|
assert!(persisted.ends_with('\n'));
|
|
assert!(persisted.contains("\"logs_directory\": \"managed-logs\""));
|
|
assert!(persisted.contains("\"default_filter\": \"info\""));
|
|
}
|
|
let reloaded = fixture.management.load_logging_document();
|
|
assert!(reloaded.is_ok(), "persisted Logging document should remain valid: {reloaded:?}");
|
|
if let std::result::Result::Ok(reloaded) = reloaded {
|
|
assert_eq!(reloaded.logs_directory(), "managed-logs");
|
|
assert_eq!(reloaded.profiles()[0].default_filter(), "info");
|
|
}
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_logging_candidate_does_not_modify_existing_file() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let before = std::fs::read(fixture.config_path.as_path());
|
|
let before = match before {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let document = fixture.management.load_logging_document();
|
|
let mut document = match document {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
document.set_logs_directory("");
|
|
let save = fixture.management.save_logging_document(&document);
|
|
assert!(save.is_err(), "schema-invalid candidate must be rejected before persistence");
|
|
let after = std::fs::read(fixture.config_path.as_path());
|
|
assert_eq!(after.ok(), std::option::Option::Some(before));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn unchanged_logging_candidate_reports_no_reload() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let document = fixture.management.load_logging_document();
|
|
let document = match document {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => {
|
|
cleanup_fixture(&fixture);
|
|
return;
|
|
},
|
|
};
|
|
let first = fixture.management.save_logging_document(&document);
|
|
assert!(first.is_ok(), "normalization save should succeed: {first:?}");
|
|
let second = fixture.management.save_logging_document(&document);
|
|
assert!(second.is_ok(), "second identical save should succeed: {second:?}");
|
|
if let std::result::Result::Ok(second) = second {
|
|
assert!(!second.source_changed());
|
|
assert!(!second.reload_required());
|
|
}
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn dotenv_create_update_and_remove_round_trip_through_management() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let create = fixture.management.set_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE", "alpha");
|
|
assert!(create.is_ok(), "managed .env create should succeed: {create:?}");
|
|
if let std::result::Result::Ok(create) = create {
|
|
assert!(create.source_changed());
|
|
assert!(create.effective_changed());
|
|
assert!(!create.shadowed_by_process_environment());
|
|
assert!(create.reload_required());
|
|
}
|
|
let update = fixture.management.set_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE", "hello world # 2");
|
|
assert!(update.is_ok(), "managed .env update should support quoting: {update:?}");
|
|
let revealed = fixture.management.reveal_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE");
|
|
assert_eq!(revealed.ok(), std::option::Option::Some(std::option::Option::Some("hello world # 2".to_owned())));
|
|
#[cfg(unix)]
|
|
{
|
|
let metadata = std::fs::metadata(fixture.dotenv_path.as_path());
|
|
assert!(metadata.is_ok(), "new managed .env permissions should be inspectable");
|
|
if let std::result::Result::Ok(metadata) = metadata {
|
|
let mode = <std::fs::Permissions as std::os::unix::fs::PermissionsExt>::mode(&metadata.permissions());
|
|
assert_eq!(mode & 0o777, 0o600, "new .env must be private on Unix");
|
|
}
|
|
}
|
|
let content = std::fs::read_to_string(fixture.dotenv_path.as_path());
|
|
assert!(content.is_ok(), "managed .env should be readable");
|
|
if let std::result::Result::Ok(content) = content {
|
|
assert!(content.contains("KSP_PRE013_MANAGED_TEST_VALUE=\"hello world # 2\""));
|
|
}
|
|
let remove = fixture.management.remove_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE");
|
|
assert!(remove.is_ok(), "managed .env remove should succeed: {remove:?}");
|
|
if let std::result::Result::Ok(remove) = remove {
|
|
assert!(remove.source_changed());
|
|
assert!(remove.effective_changed());
|
|
assert!(remove.reload_required());
|
|
}
|
|
assert_eq!(fixture.management.reveal_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE").ok(), std::option::Option::Some(std::option::Option::None));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn dotenv_comments_and_unrelated_entries_survive_targeted_mutation() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let source = "# local header\nEXTERNAL_VALUE=keep\n# managed comment\nKSP_PRE013_MANAGED_TEST_VALUE=before\n";
|
|
let write = std::fs::write(fixture.dotenv_path.as_path(), source.as_bytes());
|
|
assert!(write.is_ok(), "dotenv preservation fixture should be written");
|
|
let update = fixture.management.set_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE", "after");
|
|
assert!(update.is_ok(), "targeted .env update should succeed: {update:?}");
|
|
let content = std::fs::read_to_string(fixture.dotenv_path.as_path());
|
|
assert!(content.is_ok(), "updated .env should remain readable");
|
|
if let std::result::Result::Ok(content) = content {
|
|
assert!(content.contains("# local header"));
|
|
assert!(content.contains("EXTERNAL_VALUE=keep"));
|
|
assert!(content.contains("# managed comment"));
|
|
assert!(content.contains("KSP_PRE013_MANAGED_TEST_VALUE=after"));
|
|
}
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_existing_dotenv_is_not_modified_by_management() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let invalid = "KSP_BROKEN\n";
|
|
let write = std::fs::write(fixture.dotenv_path.as_path(), invalid.as_bytes());
|
|
assert!(write.is_ok(), "invalid .env fixture should be written");
|
|
let update = fixture.management.set_dotenv_value("KSP_PRE013_MANAGED_TEST_VALUE", "value");
|
|
assert!(update.is_err(), "management must reject mutation when existing .env syntax is invalid");
|
|
assert_eq!(std::fs::read_to_string(fixture.dotenv_path.as_path()).ok(), std::option::Option::Some(invalid.to_owned()));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn unsupported_environment_name_is_rejected_without_creating_dotenv() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let update = fixture.management.set_dotenv_value("OTHER_TOKEN", "value");
|
|
assert!(update.is_err(), "non-KSP variable must be rejected");
|
|
assert!(!fixture.dotenv_path.exists(), "rejected mutation must not create .env");
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn management_report_redacts_secret_but_explicit_reveal_returns_real_value() {
|
|
let fixture = management_fixture();
|
|
let fixture = match fixture {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(_) => return,
|
|
};
|
|
let canary = "SECRET-CANARY-PRE013";
|
|
let write = std::fs::write(fixture.dotenv_path.as_path(), format!("KSP_SECRET_PRE013_MANAGED_TEST_TOKEN={canary}\n"));
|
|
assert!(write.is_ok(), "secret management fixture should be written");
|
|
let reports = fixture.management.environment_report();
|
|
assert!(reports.is_ok(), "safe environment report should load: {reports:?}");
|
|
if let std::result::Result::Ok(reports) = reports {
|
|
let report = reports.iter().find(|report| -> bool {
|
|
return report.variable_name() == "KSP_SECRET_PRE013_MANAGED_TEST_TOKEN";
|
|
});
|
|
assert!(report.is_some(), "secret entry should appear in management report");
|
|
if let std::option::Option::Some(report) = report {
|
|
assert_eq!(report.sensitivity(), crate::ConfigSensitivity::Secret);
|
|
assert_eq!(report.desired_safe_value(), std::option::Option::Some(crate::REDACTED_CONFIG_VALUE));
|
|
assert_eq!(report.effective_safe_value(), std::option::Option::Some(crate::REDACTED_CONFIG_VALUE));
|
|
assert!(!report.shadowed_by_process_environment());
|
|
assert!(!format!("{report:?}").contains(canary));
|
|
}
|
|
}
|
|
let reveal = fixture.management.reveal_effective_environment_value("KSP_SECRET_PRE013_MANAGED_TEST_TOKEN");
|
|
assert_eq!(reveal.ok(), std::option::Option::Some(std::option::Option::Some(canary.to_owned())));
|
|
cleanup_fixture(&fixture);
|
|
}
|
|
|
|
#[test]
|
|
fn process_shadowing_report_distinguishes_desired_and_effective_changes() {
|
|
let mut process_before = std::collections::BTreeMap::<String, String>::new();
|
|
process_before.insert("KSP_MODE".to_owned(), "process".to_owned());
|
|
let mut dotenv_before = std::collections::BTreeMap::<String, String>::new();
|
|
dotenv_before.insert("KSP_MODE".to_owned(), "desired-a".to_owned());
|
|
let before = crate::ConfigEnvironment::from_maps(process_before.clone(), dotenv_before);
|
|
let mut dotenv_after = std::collections::BTreeMap::<String, String>::new();
|
|
dotenv_after.insert("KSP_MODE".to_owned(), "desired-b".to_owned());
|
|
let after = crate::ConfigEnvironment::from_maps(process_before, dotenv_after);
|
|
let report = super::environment_change_report("KSP_MODE", &before, &after);
|
|
assert!(report.source_changed());
|
|
assert!(!report.effective_changed());
|
|
assert!(report.shadowed_by_process_environment());
|
|
assert!(!report.reload_required());
|
|
let reports = super::build_environment_reports(&after);
|
|
assert!(reports.is_ok(), "shadow report fixture should build: {reports:?}");
|
|
if let std::result::Result::Ok(reports) = reports {
|
|
assert_eq!(reports.len(), 1);
|
|
assert_eq!(reports[0].desired_safe_value(), std::option::Option::Some("desired-b"));
|
|
assert_eq!(reports[0].effective_safe_value(), std::option::Option::Some("process"));
|
|
assert_eq!(reports[0].effective_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Process));
|
|
assert!(reports[0].shadowed_by_process_environment());
|
|
}
|
|
}
|
|
|
|
fn management_fixture() -> ksp_core_lib::Result<ManagementFixture> {
|
|
let fixture_id = NEXT_FIXTURE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
let root = std::env::temp_dir().join(format!("ksp-pre013-management-{}-{fixture_id}", std::process::id()));
|
|
let cleanup = std::fs::remove_dir_all(root.as_path());
|
|
if let std::result::Result::Err(error) = cleanup
|
|
&& error.kind() != std::io::ErrorKind::NotFound
|
|
{
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_PERSISTENCE_WRITE_FAILED, "unable to cleanup previous management fixture").with_source(error),
|
|
);
|
|
}
|
|
let config_root = root.join("config");
|
|
let schema_root = config_root.join("schemas");
|
|
let create = std::fs::create_dir_all(schema_root.as_path());
|
|
if let std::result::Result::Err(error) = create {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_PERSISTENCE_WRITE_FAILED, "unable to create management fixture").with_source(error),
|
|
);
|
|
}
|
|
let workspace = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
|
let source_config = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("unit_tests/fixtures/std.logging.json");
|
|
let source_schema = workspace.join("config/schemas/std.logging.schema.json");
|
|
let config_path = config_root.join("std.logging.json");
|
|
let schema_path = schema_root.join("std.logging.schema.json");
|
|
let copy_config = std::fs::copy(source_config.as_path(), config_path.as_path());
|
|
if let std::result::Result::Err(error) = copy_config {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_PERSISTENCE_WRITE_FAILED, "unable to copy management Config fixture").with_source(error),
|
|
);
|
|
}
|
|
let copy_schema = std::fs::copy(source_schema.as_path(), schema_path.as_path());
|
|
if let std::result::Result::Err(error) = copy_schema {
|
|
return std::result::Result::Err(
|
|
ksp_core_lib::Error::new(crate::ERROR_CODE_PERSISTENCE_WRITE_FAILED, "unable to copy management schema fixture").with_source(error),
|
|
);
|
|
}
|
|
let bootstrap = crate::ConfigBootstrapOptions::from_paths(config_root.as_path(), schema_root.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();
|
|
let registry = match registry {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
let engine = crate::ConfigDocumentEngine::new(bootstrap, registry);
|
|
let dotenv_path = root.join(".env");
|
|
let management = crate::ConfigManagement::with_dotenv_path(engine, dotenv_path.clone());
|
|
return std::result::Result::Ok(ManagementFixture { root, config_path, dotenv_path, management });
|
|
}
|
|
|
|
fn cleanup_fixture(fixture: &ManagementFixture) {
|
|
let cleanup = std::fs::remove_dir_all(fixture.root.as_path());
|
|
if let std::result::Result::Err(error) = cleanup
|
|
&& error.kind() != std::io::ErrorKind::NotFound
|
|
{
|
|
eprintln!("unable to cleanup Config management fixture {}: {error}", fixture.root.display());
|
|
}
|
|
}
|