use std::path::{Path, PathBuf};
use crate::config::ServerConfig;
use crate::state::{BOOT_REQUIRED_FIELD_DEFAULTS, boot_required_probe};
use super::{HealOutcome, heal_config_file, heal_discovered};
type TestResult = Result<(), Box<dyn std::error::Error>>;
const HEAL_VERSION: &str = env!("CARGO_PKG_VERSION");
const NO_ENV: &[(String, String)] = &[];
const COMPLETE_HAEMATITE: &str = "\
# the operator's own leading comment\n\
[server]\n\
listen_address = \"127.0.0.1:18080\" # odd spacing, kept on purpose\n\
\n\
[store]\n\
backend = \"haematite\"\n\
node_cache_budget = \"unlimited\"\n\
\n\
[observability]\n\
max_batch_events = 64\n\
max_batch_hold_ms = 200\n";
fn canonical_tempdir() -> Result<(tempfile::TempDir, PathBuf), Box<dyn std::error::Error>> {
let sandbox = crate::test_support::private_tempdir()?;
let canonical = sandbox.path().canonicalize()?;
Ok((sandbox, canonical))
}
fn write_config(dir: &Path, name: &str, text: &str) -> Result<PathBuf, std::io::Error> {
let path = dir.join(name);
std::fs::write(&path, text)?;
Ok(path)
}
fn strip_key(text: &str, section: &str, key: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut document: toml_edit::DocumentMut = text.parse()?;
let table = document
.get_mut(section)
.and_then(toml_edit::Item::as_table_mut)
.ok_or_else(|| format!("fixture has no [{section}] table"))?;
table
.remove(key)
.ok_or_else(|| format!("fixture has no {section}.{key} to strip"))?;
Ok(document.to_string())
}
fn parse(bytes: &[u8]) -> Result<ServerConfig, Box<dyn std::error::Error>> {
Ok(ServerConfig::parse_unresolved(bytes)?)
}
fn probe_message(config: &ServerConfig) -> Result<String, Box<dyn std::error::Error>> {
let error = boot_required_probe(config)
.err()
.ok_or("the stripped config must fail the boot-required probe")?;
match error {
crate::error::ServerError::Config { message } => Ok(message),
other => Err(format!("expected a config refusal, got: {other}").into()),
}
}
#[test]
fn every_declared_field_is_required_and_healed_one_at_a_time() -> TestResult {
for entry in &BOOT_REQUIRED_FIELD_DEFAULTS {
let (_sandbox, root) = canonical_tempdir()?;
let stripped = strip_key(COMPLETE_HAEMATITE, entry.section, entry.key)?;
let path = write_config(&root, "config.toml", &stripped)?;
let message = probe_message(&parse(stripped.as_bytes())?)?;
assert_eq!(
message, entry.requirement_message,
"stripping {} must trip its own requirement message",
entry.path
);
let outcome = heal_config_file(&path, NO_ENV)?;
let inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
assert_eq!(
inserted,
vec![entry.path],
"the heal must insert exactly the stripped field"
);
assert_eq!(outcome.config_path.as_deref(), Some(path.as_path()));
let healed = std::fs::read(&path)?;
boot_required_probe(&parse(&healed)?)?;
let healed_text = String::from_utf8(healed)?;
let first_teaching_line = entry
.teaching_comment
.first()
.ok_or("every declared default carries a teaching comment")?;
assert!(
healed_text.contains(&format!("# {first_teaching_line}")),
"healed file must carry the teaching comment for {}",
entry.path
);
assert!(
healed_text.contains(&format!("# added by aion {HEAL_VERSION} config heal")),
"healed file must carry the heal marker for {}",
entry.path
);
let backup = outcome
.backup_path
.ok_or("a written heal must preserve the previous file")?;
assert_eq!(
backup,
path.with_file_name(format!("config.toml.pre-{HEAL_VERSION}")),
"the backup name must carry the version the file predates"
);
assert_eq!(
std::fs::read(&backup)?,
stripped.as_bytes(),
"the backup must hold the pre-heal bytes"
);
}
Ok(())
}
#[test]
fn stripping_every_declared_field_at_once_heals_the_file_whole() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let mut stripped = COMPLETE_HAEMATITE.to_owned();
for entry in &BOOT_REQUIRED_FIELD_DEFAULTS {
stripped = strip_key(&stripped, entry.section, entry.key)?;
}
let path = write_config(&root, "config.toml", &stripped)?;
let outcome = heal_config_file(&path, NO_ENV)?;
let mut inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
inserted.sort_unstable();
let mut declared: Vec<&str> = BOOT_REQUIRED_FIELD_DEFAULTS
.iter()
.map(|entry| entry.path)
.collect();
declared.sort_unstable();
assert_eq!(
inserted, declared,
"with every declared field stripped, the heal must insert every one"
);
let healed = parse(&std::fs::read(&path)?)?;
boot_required_probe(&healed)?;
assert!(
matches!(
healed.store.node_cache_budget,
Some(haematite::NodeCacheBudget::Unlimited)
),
"store.node_cache_budget must heal to \"unlimited\""
);
assert_eq!(healed.observability.max_batch_events, Some(64));
assert_eq!(healed.observability.max_batch_hold_ms, Some(200));
Ok(())
}
#[test]
fn a_complete_config_passes_byte_untouched() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let path = write_config(&root, "config.toml", COMPLETE_HAEMATITE)?;
let outcome = heal_config_file(&path, NO_ENV)?;
assert!(outcome.inserted.is_empty(), "nothing to heal");
assert!(outcome.config_path.is_none());
assert!(outcome.backup_path.is_none());
assert_eq!(
std::fs::read(&path)?,
COMPLETE_HAEMATITE.as_bytes(),
"a complete config must pass through byte-untouched"
);
let mut entries: Vec<String> = std::fs::read_dir(&root)?
.map(|entry| Ok(entry?.file_name().to_string_lossy().into_owned()))
.collect::<Result<_, std::io::Error>>()?;
entries.sort();
assert_eq!(entries, vec!["config.toml".to_owned()]);
Ok(())
}
#[test]
fn a_missing_observability_section_is_created_whole() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let text = "\
[store]\n\
backend = \"haematite\"\n\
node_cache_budget = \"unlimited\"\n";
let path = write_config(&root, "config.toml", text)?;
let outcome = heal_config_file(&path, NO_ENV)?;
assert_eq!(outcome.inserted.len(), 2, "both flush-policy fields insert");
let healed_bytes = std::fs::read(&path)?;
boot_required_probe(&parse(&healed_bytes)?)?;
let healed_text = String::from_utf8(healed_bytes)?;
assert!(
healed_text.contains("[observability]"),
"the absent section must be created: {healed_text}"
);
Ok(())
}
#[test]
fn a_memory_backend_is_not_asked_for_a_node_cache_budget() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let text = "\
[store]\n\
backend = \"memory\"\n";
let path = write_config(&root, "config.toml", text)?;
let outcome = heal_config_file(&path, NO_ENV)?;
let inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
assert_eq!(
inserted,
vec![
"observability.max_batch_events",
"observability.max_batch_hold_ms"
],
"a memory backend needs only the transcript flush policy"
);
let healed = std::fs::read_to_string(&path)?;
assert!(
!healed.contains("node_cache_budget"),
"a memory deployment is not asked to rule on a cache it does not have"
);
boot_required_probe(&parse(healed.as_bytes())?)?;
Ok(())
}
#[test]
fn the_environments_backend_governs_which_requirements_apply() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let path = write_config(&root, "config.toml", "[store]\nbackend = \"memory\"\n")?;
let env = vec![("AION_STORE_BACKEND".to_owned(), "haematite".to_owned())];
let outcome = heal_config_file(&path, &env)?;
let mut inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
inserted.sort_unstable();
assert_eq!(
inserted,
vec![
"observability.max_batch_events",
"observability.max_batch_hold_ms",
"store.node_cache_budget",
],
"the environment-selected haematite backend requires the budget"
);
let mut healed = parse(&std::fs::read(&path)?)?;
healed.store.backend = crate::config::StoreBackend::Haematite;
boot_required_probe(&healed)?;
let (_sandbox, root) = canonical_tempdir()?;
let path = write_config(&root, "config.toml", "[store]\nbackend = \"haematite\"\n")?;
let env = vec![("AION_STORE_BACKEND".to_owned(), "memory".to_owned())];
let outcome = heal_config_file(&path, &env)?;
let inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
assert_eq!(
inserted,
vec![
"observability.max_batch_events",
"observability.max_batch_hold_ms"
],
"a memory-resolved boot is not asked for a node cache budget"
);
assert!(!std::fs::read_to_string(&path)?.contains("node_cache_budget"));
Ok(())
}
#[test]
fn an_explicit_zero_is_the_operators_value_and_stays_refused() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let text = "\
[store]\n\
backend = \"haematite\"\n\
node_cache_budget = \"unlimited\"\n\
\n\
[observability]\n\
max_batch_events = 0\n";
let path = write_config(&root, "config.toml", text)?;
let outcome = heal_config_file(&path, NO_ENV)?;
let inserted: Vec<&str> = outcome.inserted.iter().map(|field| field.path).collect();
assert_eq!(
inserted,
vec!["observability.max_batch_hold_ms"],
"only the ABSENT field is healed; the zero is the operator's value"
);
let healed = std::fs::read_to_string(&path)?;
assert!(
healed.contains("max_batch_events = 0"),
"the operator's explicit zero must survive the heal untouched"
);
let message = probe_message(&parse(healed.as_bytes())?)?;
assert_eq!(
message,
crate::config::OBSERVABILITY_MAX_BATCH_EVENTS_REQUIRED
);
Ok(())
}
#[test]
fn a_second_heal_never_clobbers_the_first_backup() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let first_original = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let path = write_config(&root, "config.toml", &first_original)?;
let first = heal_config_file(&path, NO_ENV)?;
let first_backup = first
.backup_path
.ok_or("the first heal must mint a backup")?;
assert_eq!(std::fs::read(&first_backup)?, first_original.as_bytes());
let healed_once = std::fs::read_to_string(&path)?;
let second_original = strip_key(&healed_once, "observability", "max_batch_events")?;
std::fs::write(&path, &second_original)?;
let second = heal_config_file(&path, NO_ENV)?;
let second_backup = second
.backup_path
.ok_or("the second heal must mint a backup")?;
assert_eq!(
second_backup,
path.with_file_name(format!("config.toml.pre-{HEAL_VERSION}.2")),
"the second backup claims the next free name"
);
assert_eq!(
std::fs::read(&first_backup)?,
first_original.as_bytes(),
"the first backup's bytes must be untouched by the second heal"
);
assert_eq!(std::fs::read(&second_backup)?, second_original.as_bytes());
Ok(())
}
#[cfg(unix)]
#[test]
fn the_heal_preserves_the_config_files_mode() -> TestResult {
use std::os::unix::fs::PermissionsExt;
let (_sandbox, root) = canonical_tempdir()?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let path = write_config(&root, "config.toml", &stripped)?;
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600))?;
let outcome = heal_config_file(&path, NO_ENV)?;
let backup = outcome.backup_path.ok_or("a heal must mint a backup")?;
assert_eq!(
std::fs::metadata(&path)?.permissions().mode() & 0o777,
0o600,
"the healed file keeps the original's owner-only mode"
);
assert_eq!(
std::fs::metadata(&backup)?.permissions().mode() & 0o777,
0o600,
"the backup keeps the original's owner-only mode"
);
Ok(())
}
#[cfg(unix)]
#[test]
fn an_unwritable_directory_refuses_the_boot_naming_the_missing_fields() -> TestResult {
use std::os::unix::fs::PermissionsExt;
let (_sandbox, root) = canonical_tempdir()?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let path = write_config(&root, "config.toml", &stripped)?;
std::fs::set_permissions(&root, std::fs::Permissions::from_mode(0o500))?;
let result = heal_config_file(&path, NO_ENV);
std::fs::set_permissions(&root, std::fs::Permissions::from_mode(0o700))?;
let error = result
.err()
.ok_or("a heal that cannot write must refuse the boot")?;
let message = error.to_string();
assert!(
message.contains("config heal failed"),
"the refusal must name the heal: {message}"
);
assert!(
message.contains("observability.max_batch_hold_ms"),
"the refusal must name the missing field: {message}"
);
assert!(
message.contains("config.toml"),
"the refusal must name the file: {message}"
);
assert_eq!(
std::fs::read(&path)?,
stripped.as_bytes(),
"a refused heal must leave the config untouched"
);
Ok(())
}
#[cfg(unix)]
#[test]
fn a_symlinked_config_is_healed_at_its_target() -> TestResult {
use std::os::unix::fs::symlink;
let (_sandbox, root) = canonical_tempdir()?;
let source_dir = root.join("managed-source");
let home = root.join("home");
std::fs::create_dir(&source_dir)?;
std::fs::create_dir(&home)?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let target = write_config(&source_dir, "real.toml", &stripped)?;
let link = home.join("config.toml");
symlink(&target, &link)?;
let outcome = heal_config_file(&link, NO_ENV)?;
assert_eq!(
outcome.config_path.as_deref(),
Some(target.as_path()),
"the heal must act on the resolved target"
);
let link_metadata = std::fs::symlink_metadata(&link)?;
assert!(
link_metadata.file_type().is_symlink(),
"the operator's link must survive the heal"
);
assert_eq!(std::fs::read_link(&link)?, target);
boot_required_probe(&parse(&std::fs::read(&target)?)?)?;
assert_eq!(
outcome.backup_path.as_deref(),
Some(
target
.with_file_name(format!("real.toml.pre-{HEAL_VERSION}"))
.as_path()
)
);
assert_eq!(
std::fs::read(target.with_file_name(format!("real.toml.pre-{HEAL_VERSION}")))?,
stripped.as_bytes()
);
let mut home_entries: Vec<String> = std::fs::read_dir(&home)?
.map(|entry| Ok(entry?.file_name().to_string_lossy().into_owned()))
.collect::<Result<_, std::io::Error>>()?;
home_entries.sort();
assert_eq!(home_entries, vec!["config.toml".to_owned()]);
Ok(())
}
#[cfg(unix)]
#[test]
fn a_dangling_config_link_refuses_naming_both_ends() -> TestResult {
use std::os::unix::fs::symlink;
let (_sandbox, root) = canonical_tempdir()?;
let link = root.join("config.toml");
let missing_target = root.join("gone-away.toml");
symlink(&missing_target, &link)?;
let error = heal_config_file(&link, NO_ENV)
.err()
.ok_or("a dangling config link must refuse the boot")?;
let message = error.to_string();
assert!(
message.contains("config.toml"),
"the refusal must name the link: {message}"
);
assert!(
message.contains("gone-away.toml"),
"the refusal must name the target: {message}"
);
Ok(())
}
#[cfg(unix)]
#[test]
fn a_planted_staging_symlink_is_not_followed() -> TestResult {
use std::os::unix::fs::symlink;
let (_sandbox, root) = canonical_tempdir()?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let path = write_config(&root, "config.toml", &stripped)?;
let victim = write_config(&root, "victim.txt", "must never be overwritten")?;
let planted = root.join(format!("config.toml.heal-staging-{}.1", std::process::id()));
symlink(&victim, &planted)?;
let outcome = heal_config_file(&path, NO_ENV)?;
assert_eq!(outcome.inserted.len(), 1, "the heal must still complete");
boot_required_probe(&parse(&std::fs::read(&path)?)?)?;
assert_eq!(
std::fs::read_to_string(&victim)?,
"must never be overwritten",
"a planted staging symlink must never route the healed bytes"
);
assert!(
std::fs::symlink_metadata(&planted)?
.file_type()
.is_symlink(),
"the planted name is not the heal's to remove"
);
Ok(())
}
#[test]
fn a_crlf_config_stays_crlf() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let crlf_original = stripped.replace('\n', "\r\n");
let path = write_config(&root, "config.toml", &crlf_original)?;
let outcome = heal_config_file(&path, NO_ENV)?;
assert_eq!(outcome.inserted.len(), 1);
let healed = std::fs::read_to_string(&path)?;
let bare_lf = healed
.as_bytes()
.iter()
.enumerate()
.filter(|(index, byte)| {
**byte == b'\n' && (*index == 0 || healed.as_bytes()[index - 1] != b'\r')
})
.count();
assert_eq!(
bare_lf, 0,
"every line of a CRLF document must stay CRLF: {healed:?}"
);
boot_required_probe(&parse(healed.as_bytes())?)?;
assert!(
healed.contains("max_batch_hold_ms = 200\r\n"),
"the inserted line itself carries the document's ending: {healed:?}"
);
for line in crlf_original.lines() {
assert!(
healed.contains(line),
"an original line went missing from the healed CRLF file: {line:?}"
);
}
Ok(())
}
#[test]
fn an_inline_observability_table_is_healed_without_comments() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let text = "\
observability = { max_event_bytes = 262144 }\n\
\n\
[store]\n\
backend = \"haematite\"\n\
node_cache_budget = \"unlimited\"\n";
let path = write_config(&root, "config.toml", text)?;
let outcome = heal_config_file(&path, NO_ENV)?;
assert_eq!(outcome.inserted.len(), 2);
let healed_text = std::fs::read_to_string(&path)?;
let healed = parse(healed_text.as_bytes())?;
boot_required_probe(&healed)?;
assert_eq!(healed.observability.max_batch_events, Some(64));
assert_eq!(healed.observability.max_batch_hold_ms, Some(200));
assert_eq!(healed.observability.max_event_bytes, 262_144);
assert!(
!healed_text.contains(" ,"),
"inline-table insertion must not leave a space before a comma: {healed_text:?}"
);
assert!(
healed_text.contains("max_event_bytes = 262144, max_batch_events = 64"),
"inline entries must read `key = value, key = value`: {healed_text:?}"
);
Ok(())
}
#[test]
fn the_heal_edits_exactly_the_file_discovery_will_load() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let home = root.join("home");
let working = root.join("working");
std::fs::create_dir(&home)?;
std::fs::create_dir(&working)?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
let home_config = write_config(&home, "config.toml", &stripped)?;
let project_config = write_config(&working, "aion.toml", &stripped)?;
let outcome = heal_discovered(None, &home, &working, NO_ENV)?;
assert_eq!(
outcome.config_path.as_deref(),
Some(project_config.as_path())
);
assert_eq!(
std::fs::read(&home_config)?,
stripped.as_bytes(),
"the home config must stay untouched while a project config wins"
);
boot_required_probe(&parse(&std::fs::read(&project_config)?)?)?;
let explicit = write_config(&root, "explicit.toml", &stripped)?;
let outcome = heal_discovered(Some(&explicit), &home, &working, NO_ENV)?;
assert_eq!(outcome.config_path.as_deref(), Some(explicit.as_path()));
boot_required_probe(&parse(&std::fs::read(&explicit)?)?)?;
assert_eq!(
std::fs::read(&home_config)?,
stripped.as_bytes(),
"the home config must stay untouched while an explicit config wins"
);
let empty_home = root.join("empty-home");
let empty_working = root.join("empty-working");
std::fs::create_dir(&empty_home)?;
std::fs::create_dir(&empty_working)?;
let outcome = heal_discovered(None, &empty_home, &empty_working, NO_ENV)?;
assert!(outcome.inserted.is_empty());
assert!(std::fs::read_dir(&empty_home)?.next().is_none());
Ok(())
}
#[test]
fn the_boot_log_names_each_minted_field_and_the_count() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let home = root.join("home");
let working = root.join("working");
std::fs::create_dir(&home)?;
std::fs::create_dir(&working)?;
let mut stripped = COMPLETE_HAEMATITE.to_owned();
for entry in &BOOT_REQUIRED_FIELD_DEFAULTS {
stripped = strip_key(&stripped, entry.section, entry.key)?;
}
write_config(&home, "config.toml", &stripped)?;
let (captured, outcome) = crate::test_support::CapturedLogs::capture(|| {
heal_discovered(None, &home, &working, NO_ENV)
});
let outcome = outcome?;
assert_eq!(outcome.inserted.len(), BOOT_REQUIRED_FIELD_DEFAULTS.len());
let logged = captured.text()?;
for field in &outcome.inserted {
assert!(
logged.contains(field.path),
"the boot log must name {}: {logged}",
field.path
);
assert!(
logged.contains(field.value.trim_matches('"')),
"the boot log must carry {}'s inserted value: {logged}",
field.path
);
}
assert!(
logged.contains(&format!(
"inserted_field_count={}",
BOOT_REQUIRED_FIELD_DEFAULTS.len()
)),
"the boot summary must name the count: {logged}"
);
assert!(
logged.contains(&format!("config.toml.pre-{HEAL_VERSION}")),
"the boot summary must name the preserved backup: {logged}"
);
Ok(())
}
#[test]
fn an_env_supplied_value_is_still_minted_with_a_warning() -> TestResult {
let (_sandbox, root) = canonical_tempdir()?;
let home = root.join("home");
let working = root.join("working");
std::fs::create_dir(&home)?;
std::fs::create_dir(&working)?;
let stripped = strip_key(
&strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_events")?,
"observability",
"max_batch_hold_ms",
)?;
let path = write_config(&home, "config.toml", &stripped)?;
let env = vec![(
"AION_OBSERVABILITY_MAX_BATCH_EVENTS".to_owned(),
"512".to_owned(),
)];
let (captured, outcome) =
crate::test_support::CapturedLogs::capture(|| heal_discovered(None, &home, &working, &env));
let outcome = outcome?;
assert_eq!(
outcome.inserted.len(),
2,
"both absent fields mint regardless of the override"
);
let healed = parse(&std::fs::read(&path)?)?;
assert_eq!(healed.observability.max_batch_events, Some(64));
let logged = captured.text()?;
let warning_line = logged
.lines()
.find(|line| line.contains("WARN") && line.contains("AION_OBSERVABILITY_MAX_BATCH_EVENTS"))
.ok_or_else(|| format!("no override warning in the boot log: {logged}"))?;
assert!(
warning_line.contains("512"),
"the warning must carry the effective runtime value: {warning_line}"
);
assert!(
warning_line.contains("64"),
"the warning must carry the minted file value: {warning_line}"
);
assert!(
!logged.contains("AION_OBSERVABILITY_MAX_BATCH_HOLD_MS"),
"no warning for a minted field with no override set: {logged}"
);
Ok(())
}
#[test]
fn the_heal_never_emits_the_retired_variable_warning() -> TestResult {
let env = vec![(
"AION_STORE_LOCK_ACQUISITION_PATIENCE_MS".to_owned(),
"60000".to_owned(),
)];
let (_sandbox, root) = canonical_tempdir()?;
let home = root.join("home");
let working = root.join("working");
std::fs::create_dir(&home)?;
std::fs::create_dir(&working)?;
write_config(&home, "config.toml", COMPLETE_HAEMATITE)?;
let (captured, outcome) =
crate::test_support::CapturedLogs::capture(|| heal_discovered(None, &home, &working, &env));
assert!(
outcome?.inserted.is_empty(),
"the complete config must pass byte-untouched"
);
let logged = captured.text()?;
assert!(
logged.is_empty(),
"a byte-untouched boot's log must gain nothing from the heal path: {logged}"
);
let (_sandbox, root) = canonical_tempdir()?;
let home = root.join("home");
let working = root.join("working");
std::fs::create_dir(&home)?;
std::fs::create_dir(&working)?;
let stripped = strip_key(COMPLETE_HAEMATITE, "observability", "max_batch_hold_ms")?;
write_config(&home, "config.toml", &stripped)?;
let (captured, outcome) =
crate::test_support::CapturedLogs::capture(|| heal_discovered(None, &home, &working, &env));
assert_eq!(
outcome?.inserted.len(),
1,
"the healing posture must actually heal"
);
let logged = captured.text()?;
assert!(
logged.contains("observability.max_batch_hold_ms"),
"the healing boot still logs its insertion: {logged}"
);
assert!(
!logged.contains("retired and ignored"),
"the retired-variable warning is the loader's line, never the heal's: {logged}"
);
Ok(())
}
#[test]
fn the_default_outcome_is_visibly_untouched() {
let outcome = HealOutcome::default();
assert!(outcome.config_path.is_none());
assert!(outcome.backup_path.is_none());
assert!(outcome.inserted.is_empty());
}