use super::mock_harness_guard::reject_mock_harness_scaffolding;
use super::*;
mod coverage;
#[test]
fn is_snippet_coverage_manifest_path_matches_only_the_exact_ledger_name() {
assert!(is_snippet_coverage_manifest_path(Path::new(
"docs/snippets/.alef-snippet-coverage.json"
)));
assert!(is_snippet_coverage_manifest_path(Path::new(COVERAGE_MANIFEST)));
assert!(
!is_snippet_coverage_manifest_path(Path::new("docs/snippets/.alef-snippet-coverage.json.bak")),
"a name that merely contains the ledger name must not match"
);
assert!(
!is_snippet_coverage_manifest_path(Path::new("packages/php/composer.json")),
"an unrelated unmarkable manifest must not match"
);
}
const SNIPPET_HEADER: &str = "<!-- This file is auto-generated by alef — DO NOT EDIT. -->\n\
<!-- To regenerate: alef e2e generate -->\n\
<!-- To verify freshness: alef verify -->\n\n";
struct FixtureExtension {
body: &'static str,
}
impl crate::Extension for FixtureExtension {
fn name(&self) -> &str {
"fixture"
}
fn render_e2e_snippet(
&self,
_fixture: &Fixture,
_e2e_config: &E2eConfig,
_config: &ResolvedCrateConfig,
_language: &str,
_type_defs: &[TypeDef],
_enums: &[EnumDef],
) -> Result<Option<String>> {
Ok(Some(self.body.to_string()))
}
}
fn documented_fixture() -> Fixture {
Fixture {
id: "extension_owned".into(),
description: "Extension-owned example".into(),
docs: Some(FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects: SideEffectClass::Safe,
coverage_exceptions: BTreeMap::new(),
}),
..Fixture::default()
}
}
#[test]
fn mock_harness_scaffolding_is_rejected_for_every_language() {
let fixture = Fixture {
id: "rate_limit_429".into(),
..Fixture::default()
};
let leaks = [
"var url = System.getenv(\"MOCK_SERVER_URL\") + \"/fixtures/rate_limit_429\";",
"let url = std.c.getenv(\"MOCK_SERVER_RATE_LIMIT_429\");",
"var url = System.getProperty(\"mockServerUrl\");",
"let base = System.getProperty(\"mockServer.rate_limit_429\");",
"let hosts = process.env.MOCK_SERVERS;",
"let url = \"https://api.example.com/fixtures/rate_limit_429\";",
];
for leak in leaks {
let error = reject_mock_harness_scaffolding(leak, &fixture, "zig")
.expect_err("mock-server scaffolding must not reach a published snippet");
let message = format!("{error:#}");
assert!(message.contains("rate_limit_429"), "error omits the fixture: {message}");
assert!(message.contains("zig"), "error omits the language: {message}");
}
}
#[test]
fn a_reader_facing_snippet_passes_the_mock_harness_guard() {
let fixture = Fixture {
id: "rate_limit_429".into(),
..Fixture::default()
};
let body = "var apiKey = System.getenv(\"API_KEY\");\nvar client = Sample.createClient(apiKey, null);";
assert!(reject_mock_harness_scaffolding(body, &fixture, "java").is_ok());
}
fn snippet_report_for(fixture: Fixture, languages: &[&str], body: &'static str) -> Result<SnippetGenerationReport> {
let extensions: Vec<Box<dyn crate::Extension>> = vec![Box::new(FixtureExtension { body })];
let e2e = E2eConfig::default();
let crate_config = ResolvedCrateConfig::default();
let snippet_config = SnippetConfig {
output: "docs/snippets".into(),
..SnippetConfig::default()
};
let context = SnippetRenderContext {
e2e: &e2e,
crate_config: &crate_config,
type_defs: &[],
enums: &[],
functions: &[],
errors: &[],
};
let languages: Vec<String> = languages.iter().map(|language| (*language).to_string()).collect();
generate_snippet_report_with_extensions(&[fixture], &languages, &snippet_config, &context, &extensions)
}
#[test]
fn a_guard_rejected_snippet_is_a_reported_failure_not_a_silent_absence() {
let leaking_body = "var url = System.getenv(\"MOCK_SERVER_URL\") + \"/fixtures/extension_owned\";";
let error = snippet_report_for(documented_fixture(), &["java", "rust"], leaking_body)
.expect_err("a guard-rejected snippet must abort generation");
let message = format!("{error:#}");
assert!(
message.contains("rejected by the mock-harness guard"),
"the failure must name the guard: {message}"
);
assert!(
message.contains("2 documentation snippet(s)"),
"the failure must count every rejection: {message}"
);
assert!(
message.contains("\n java (1):"),
"the failure must attribute per language: {message}"
);
assert!(
message.contains("\n rust (1):"),
"the failure must attribute per language: {message}"
);
assert!(
message.contains("`MOCK_SERVER_URL` (1): extension_owned"),
"the failure must attribute per reason and fixture: {message}"
);
}
#[test]
fn a_documented_coverage_exception_cannot_retire_a_guard_rejection() {
let mut fixture = documented_fixture();
fixture
.docs
.as_mut()
.expect("documented fixture has docs")
.coverage_exceptions
.insert(
"rust".into(),
crate::e2e::fixture::SnippetCoverageException {
reason: "the sample backend cannot express this recipe".into(),
documentation: "docs/limitations.md".into(),
},
);
let leaking_body = "let url = std::env::var(\"MOCK_SERVER_URL\").unwrap();";
let error = snippet_report_for(fixture, &["rust"], leaking_body)
.expect_err("a coverage exception must not absorb a guard rejection");
let message = format!("{error:#}");
assert!(
message.contains("rejected by the mock-harness guard"),
"the exception silently absorbed the rejection: {message}"
);
assert!(
message.contains("cannot retire a guard rejection"),
"the failure must explain why the exception did not apply: {message}"
);
}
#[test]
fn coverage_exception_declared_as_c_applies_to_ffi_backend() {
let mut fixture = documented_fixture();
fixture.requirements = vec!["feature:unavailable_in_c".into()];
fixture
.docs
.as_mut()
.expect("documented fixture has docs")
.coverage_exceptions
.insert(
"c".into(),
crate::e2e::fixture::SnippetCoverageException {
reason: "the C binding cannot express this recipe".into(),
documentation: "docs/limitations.md".into(),
},
);
let body = "int main(void) { return 0; }";
let report = snippet_report_for(fixture, &["ffi"], body)
.expect("a documented exception must retire the gap instead of failing the run");
assert!(
report.coverage.missing.is_empty(),
"the `c` exception must apply while the backend runs as `ffi`: {:?}",
report.coverage.missing
);
assert_eq!(report.coverage.documented_exceptions.len(), 1);
assert_eq!(
report.coverage.documented_exceptions[0].reason,
"the C binding cannot express this recipe"
);
}
#[test]
fn validate_coverage_exceptions_rejects_unknown_language() {
let mut fixture = documented_fixture();
fixture
.docs
.as_mut()
.expect("documented fixture has docs")
.coverage_exceptions
.insert(
"csharpp".into(),
crate::e2e::fixture::SnippetCoverageException {
reason: "typo'd language key".into(),
documentation: "docs/limitations.md".into(),
},
);
let error = validate_coverage_exceptions(&fixture).expect_err("an unknown language key must hard-fail");
let message = error.to_string();
assert!(message.contains("csharpp"), "error must name the bad key: {message}");
assert!(
message.contains("c (also accepted: c_ffi, ffi)"),
"error must name the C backend's accepted spellings: {message}"
);
assert!(
message.contains("rust (also accepted: core, rust_core)"),
"error must name the Rust backend's accepted spellings: {message}"
);
}
#[test]
fn validate_coverage_exceptions_accepts_alias_spellings() {
for alias in ["c", "c_ffi", "ffi", "rust", "core", "rust_core"] {
let mut fixture = documented_fixture();
fixture
.docs
.as_mut()
.expect("documented fixture has docs")
.coverage_exceptions
.insert(
alias.to_string(),
crate::e2e::fixture::SnippetCoverageException {
reason: "documented limitation".into(),
documentation: "docs/limitations.md".into(),
},
);
assert!(
validate_coverage_exceptions(&fixture).is_ok(),
"`{alias}` is an accepted spelling and must pass validation"
);
}
}
#[test]
fn a_clean_snippet_still_renders_while_the_guard_is_armed() {
let clean_body =
"let api_key = std::env::var(\"API_KEY\").unwrap();\nlet client = sample::create_client(api_key)?;";
let report =
snippet_report_for(documented_fixture(), &["rust"], clean_body).expect("a clean snippet must still render");
assert!(report.guard_rejections.is_empty());
assert!(report.coverage.missing.is_empty());
assert_eq!(report.coverage.generated, report.coverage.expected);
assert_eq!(report.snippets.len(), 1);
assert!(
report.snippets[0]
.file
.content
.contains("sample::create_client(api_key)?")
);
}
#[test]
fn capability_decision_reports_missing_requirements_deterministically() {
let fixture = Fixture {
requirements: vec!["service:api".into(), "feature:json".into()],
..Fixture::default()
};
let capabilities = BTreeSet::from(["feature:json".to_string()]);
assert_eq!(
snippet_inclusion(&fixture, &capabilities),
SnippetInclusion::Exclude {
missing_requirements: vec!["service:api".into()]
}
);
}
#[test]
fn snippet_generator_and_executable_suite_agree_on_a_call_level_language_skip() {
let cfg_str = r#"
[workspace]
languages = ["c"]
[[crates]]
name = "example-core"
sources = ["src/lib.rs"]
[crates.ffi]
prefix = "sample"
[crates.e2e]
fixtures = "fixtures"
[crates.e2e.call]
function = "scrape"
module = "example_api"
[crates.e2e.calls.batch_stream]
function = "batch_stream"
module = "example_api"
select_when = { category = "batch" }
skip_languages = ["c"]
[crates.e2e.calls.batch_stream.overrides.c]
function = "batch_stream"
result_type = "BatchResults"
raw_c_result_type = "BatchResults"
c_engine_factory = "EngineConfig"
"#;
let cfg: crate::core::config::NewAlefConfig = toml::from_str(cfg_str).expect("config parses");
let e2e = cfg.crates[0].e2e.clone().expect("e2e config");
let crate_config = cfg.resolve().expect("config resolves").remove(0);
let mut fixture = Fixture {
id: "batch_stream_basic".into(),
description: "Stream a batch of items".into(),
category: Some("batch".into()),
docs: Some(FixtureDocs {
topic: "batch".into(),
stem: Some("batch-stream-basic".into()),
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects: SideEffectClass::Safe,
coverage_exceptions: BTreeMap::new(),
}),
input: serde_json::json!({}),
..Fixture::default()
};
fixture.call = Some("batch_stream".into());
let suite_decision = crate::e2e::codegen::fixture_inclusion(&fixture, "c", &e2e);
assert_eq!(
suite_decision,
crate::e2e::codegen::InclusionDecision::Exclude("call skips language"),
"the executable suite must exclude a call-level language skip"
);
let snippet_config = SnippetConfig {
output: "docs/snippets".into(),
..SnippetConfig::default()
};
let context = SnippetRenderContext {
e2e: &e2e,
crate_config: &crate_config,
type_defs: &[],
enums: &[],
functions: &[],
errors: &[],
};
let report =
generate_snippet_report_with_extensions(&[fixture], &["c".to_string()], &snippet_config, &context, &[])
.expect("a call-level language skip must not reach the mock-harness guard");
assert!(
report.snippets.is_empty(),
"a call skipped for `c` must never render a snippet: {:?}",
report.snippets
);
assert!(
report.coverage.expected.is_empty(),
"a call skipped for `c` must never enter the coverage ledger as expected: {:?}",
report.coverage.expected
);
assert!(report.guard_rejections.is_empty());
}
mod path_resolution;
#[test]
fn frontmatter_fields_are_pinned_by_exact_equality() {
let render = |fixture: &Fixture, side_effects: SideEffectClass, target: &str| {
let docs = FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects,
coverage_exceptions: BTreeMap::new(),
};
render_snippet_markdown(
"example()",
fixture,
&docs,
target,
DocumentationLanguage::Binding(Language::Node),
)
};
let baseline = documented_fixture();
assert_eq!(
render(&baseline, SideEffectClass::Network, "node"),
format!(
"---\nid: fixture_node_extension_owned\nlanguage: typescript\ntarget: node\nlevel: typecheck\nrequires: []\nside_effect: network\n---\n\n{SNIPPET_HEADER}Extension-owned example\n\n```typescript title=\"TypeScript\"\nexample()\n```\n"
)
);
assert_eq!(
render(&baseline, SideEffectClass::Install, "node"),
format!(
"---\nid: fixture_node_extension_owned\nlanguage: typescript\ntarget: node\nlevel: typecheck\nrequires: []\nside_effect: install\n---\n\n{SNIPPET_HEADER}Extension-owned example\n\n```typescript title=\"TypeScript\"\nexample()\n```\n"
)
);
let required = Fixture {
requirements: vec!["feature:json".into(), "service:api".into()],
..documented_fixture()
};
assert_eq!(
render(&required, SideEffectClass::Safe, "node"),
format!(
"---\nid: fixture_node_extension_owned\nlanguage: typescript\ntarget: node\nrequires: [\"feature:json\",\"service:api\"]\nside_effect: safe\n---\n\n{SNIPPET_HEADER}Extension-owned example\n\n```typescript title=\"TypeScript\"\nexample()\n```\n"
)
);
}
#[test]
fn safe_side_effects_snippet_is_not_level_capped() {
let docs = FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects: SideEffectClass::Safe,
coverage_exceptions: BTreeMap::new(),
};
let rendered = render_snippet_markdown(
"example()",
&documented_fixture(),
&docs,
"node",
DocumentationLanguage::Binding(Language::Node),
);
assert!(
!rendered.contains("\nlevel:"),
"a safe snippet must declare no level cap at all -- the key is OMITTED rather than \
rendered `level: null`, because these are Astro content entries and Astro types \
`level` as an optional STRING: an absent key validates, an explicit YAML null does \
not. Both spellings deserialise to `SnippetMetadata::level == None` on alef's side. \
got: {rendered}"
);
let front_matter = rendered
.split("---\n")
.nth(1)
.expect("rendered snippet has front matter");
let metadata: crate::snippets::types::SnippetMetadata =
serde_yaml::from_str(front_matter).expect("front matter is valid YAML");
assert_eq!(metadata.level, None, "safe snippet must resolve to no declared level");
}
#[test]
fn unsafe_side_effects_snippet_keeps_the_typecheck_cap() {
for side_effects in [
SideEffectClass::Network,
SideEffectClass::Process,
SideEffectClass::Install,
SideEffectClass::Server,
] {
let docs = FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects,
coverage_exceptions: BTreeMap::new(),
};
let rendered = render_snippet_markdown(
"example()",
&documented_fixture(),
&docs,
"node",
DocumentationLanguage::Binding(Language::Node),
);
assert!(
rendered.contains("\nlevel: typecheck\n"),
"unsafe snippet ({side_effects:?}) must keep the typecheck cap, got: {rendered}"
);
let front_matter = rendered
.split("---\n")
.nth(1)
.expect("rendered snippet has front matter");
let metadata: crate::snippets::types::SnippetMetadata =
serde_yaml::from_str(front_matter).expect("front matter is valid YAML");
assert_eq!(
metadata.level,
Some(crate::snippets::types::ValidationLevel::TypeCheck),
"unsafe snippet ({side_effects:?}) must resolve to the typecheck cap"
);
}
}
fn rendered_snippet() -> String {
let docs = FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects: SideEffectClass::Safe,
coverage_exceptions: BTreeMap::new(),
};
render_snippet_markdown(
"example()",
&documented_fixture(),
&docs,
"python",
DocumentationLanguage::Binding(Language::Python),
)
}
fn rendered_snippet_without_header() -> String {
let rendered = rendered_snippet();
let stripped = rendered.replace(SNIPPET_HEADER, "");
assert_ne!(stripped, rendered, "control must actually remove the header");
stripped
}
#[test]
fn no_front_matter_key_renders_an_explicit_yaml_null() {
for side_effects in [SideEffectClass::Safe, SideEffectClass::Server] {
let docs = FixtureDocs {
topic: "api".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects,
coverage_exceptions: BTreeMap::new(),
};
let rendered = render_snippet_markdown(
"example()",
&documented_fixture(),
&docs,
"python",
DocumentationLanguage::Binding(Language::Python),
);
let front_matter = rendered.split("---\n").nth(1).expect("front matter");
let offenders: Vec<&str> = front_matter
.lines()
.filter(|line| line.trim_end().ends_with(": null"))
.collect();
assert!(
offenders.is_empty(),
"{side_effects:?} snippet emits an explicit YAML null, which Astro's content schema \
rejects and which fails the consumer's entire docs build: {offenders:?}"
);
}
}
#[test]
fn rendered_snippet_carries_a_marker_the_read_side_recognises() {
assert!(crate::core::hash::content_has_alef_marker(&rendered_snippet()));
}
#[test]
fn read_side_does_not_recognise_a_snippet_without_the_header() {
assert!(!crate::core::hash::content_has_alef_marker(
&rendered_snippet_without_header()
));
}
#[test]
fn snippet_marker_lands_inside_the_read_side_scan_window() {
let rendered = rendered_snippet();
let marker_index = rendered
.lines()
.position(|line| line.contains("auto-generated by alef"))
.expect("rendered snippet carries the marker");
assert_eq!(
marker_index, 8,
"a snippet that omits `level:` has a 7-line front matter, leaving one line of slack"
);
let with_level = rendered.replacen("\ntarget: ", "\nlevel: typecheck\ntarget: ", 1);
let level_marker_index = with_level
.lines()
.position(|line| line.contains("auto-generated by alef"))
.expect("rendered snippet carries the marker");
assert_eq!(
level_marker_index, 9,
"a snippet declaring `level:` must still land on the last line of the scan window"
);
assert!(
crate::core::hash::content_has_alef_marker(&with_level),
"the zero-slack case must still be recognised"
);
let widened = with_level.replacen("\nlevel: typecheck\n", "\nlevel: typecheck\nextra: value\n", 1);
assert!(
!crate::core::hash::content_has_alef_marker(&widened),
"one extra front-matter line beyond the level-carrying case must push the marker out \
of the scan window -- the budget this test guards is exactly zero lines"
);
}
#[test]
fn a_body_ending_in_a_newline_does_not_open_a_blank_line_before_the_closing_fence() {
let docs = FixtureDocs {
topic: "smoke".into(),
stem: None,
paths: BTreeMap::new(),
title: None,
description: None,
input: None,
shows: Vec::new(),
error: None,
presentation: None,
client: None,
side_effects: SideEffectClass::Safe,
coverage_exceptions: BTreeMap::new(),
};
let rendered = render_snippet_markdown(
"example()\n",
&documented_fixture(),
&docs,
"python",
DocumentationLanguage::Binding(Language::Python),
);
assert!(
rendered.ends_with("example()\n```\n"),
"closing fence must follow the last code line directly: {rendered:?}"
);
let blocks = crate::snippets::parser::extract_fenced_blocks(&rendered);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].code, "example()");
}
#[test]
fn snippet_header_preserves_frontmatter_and_fence_structure() {
let rendered = rendered_snippet();
assert!(rendered.starts_with("---\nid: fixture_python_extension_owned\n"));
assert_eq!(
crate::snippets::parser::frontmatter_status(&rendered),
crate::snippets::parser::FrontmatterStatus::Present
);
let blocks = crate::snippets::parser::extract_fenced_blocks(&rendered);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].lang, "python");
assert_eq!(blocks[0].code, "example()");
assert_eq!(
blocks[0].preceding_comment, None,
"the provenance block must not be read as a snippet annotation"
);
}
#[test]
fn snippet_marker_survives_hash_stamping() {
let rendered = rendered_snippet();
let stamped = crate::core::hash::inject_hash_line(&rendered, "abc123");
assert!(crate::core::hash::content_has_alef_marker(&stamped));
assert_eq!(crate::core::hash::extract_hash(&stamped).as_deref(), Some("abc123"));
assert!(stamped.contains("<!-- alef:hash:abc123 -->"));
assert_eq!(crate::core::hash::strip_hash_line(&stamped), rendered);
}
#[test]
fn generated_snippet_file_is_claimed_by_the_stamping_pass() {
let file = crate::core::backend::GeneratedFile {
path: PathBuf::from("docs/snippets/python/api/example.md"),
content: rendered_snippet(),
generated_header: false,
};
assert!(file.carries_alef_marker());
}
#[test]
fn write_guard_accepts_a_marked_snippet_and_refuses_an_unmarked_one() {
let relative = PathBuf::from("docs/snippets/python/api/example.md");
let updated = rendered_snippet().replace("example()", "updated_example()");
let write_over = |existing: &str| {
let directory = tempfile::tempdir().expect("temporary output directory");
let full_path = directory.path().join(&relative);
std::fs::create_dir_all(full_path.parent().expect("snippet parent")).expect("snippet directory");
std::fs::write(&full_path, existing).expect("pre-existing snippet");
let report = crate::cli::pipeline::write_scaffold_files_report(
&[crate::core::backend::GeneratedFile {
path: relative.clone(),
content: updated.clone(),
generated_header: false,
}],
directory.path(),
true,
)
.expect("scaffold write report");
(
report.changed_paths.contains(&full_path),
report.refused_paths.contains(&full_path),
std::fs::read_to_string(&full_path).expect("snippet still readable"),
)
};
let (marked_written, marked_refused, marked_content) = write_over(&rendered_snippet());
assert!(marked_written, "a marked snippet must be regenerable");
assert!(!marked_refused);
assert!(marked_content.contains("updated_example()"));
let unmarked_existing = rendered_snippet_without_header();
let (unmarked_written, unmarked_refused, unmarked_content) = write_over(&unmarked_existing);
assert!(!unmarked_written, "an unmarked snippet has no proof of authorship");
assert!(unmarked_refused);
assert_eq!(
unmarked_content, unmarked_existing,
"a refused file must be left byte-identical"
);
}
#[test]
fn write_guard_accepts_an_unmarked_snippet_the_previous_run_recorded_in_the_ledger() {
let relative = PathBuf::from("docs/snippets/python/api/example.md");
let sibling = PathBuf::from("docs/snippets/python/api/hand-written.md");
let updated = rendered_snippet().replace("example()", "updated_example()");
let existing = rendered_snippet_without_header();
let directory = tempfile::tempdir().expect("temporary output directory");
let root = directory.path().join("docs/snippets");
for path in [&relative, &sibling] {
let full = directory.path().join(path);
std::fs::create_dir_all(full.parent().expect("snippet parent")).expect("snippet directory");
std::fs::write(&full, &existing).expect("pre-existing snippet");
}
let key = SnippetCoverageKey {
fixture_id: "example".into(),
language: "python".into(),
};
let ledger = SnippetCoverageLedger {
format_version: COVERAGE_MANIFEST_VERSION,
generated_paths: vec![PathBuf::from("python/api/example.md")],
generated_metadata: vec![GeneratedSnippetMetadata {
key: key.clone(),
path: PathBuf::from("python/api/example.md"),
language: "python".into(),
target: "python".into(),
session: "python".into(),
requires: Vec::new(),
side_effect: SideEffectClass::Safe,
}],
expected: vec![key.clone()],
generated: vec![key],
missing: Vec::new(),
documented_exceptions: Vec::new(),
};
std::fs::write(
root.join(COVERAGE_MANIFEST),
serde_json::to_string(&ledger).expect("serialize ledger"),
)
.expect("write ledger");
super::ownership::snapshot_pre_run_ledger(&root);
let report = crate::cli::pipeline::write_scaffold_files_report(
&[
crate::core::backend::GeneratedFile {
path: relative.clone(),
content: updated.clone(),
generated_header: false,
},
crate::core::backend::GeneratedFile {
path: sibling.clone(),
content: updated.clone(),
generated_header: false,
},
],
directory.path(),
true,
)
.expect("scaffold write report");
let recorded = directory.path().join(&relative);
let unrecorded = directory.path().join(&sibling);
assert!(
report.changed_paths.contains(&recorded),
"a snippet the previous run recorded must be regenerable"
);
assert!(
report.refused_paths.contains(&unrecorded),
"a hand-written sibling under the same root has no record and must stay refused"
);
assert_eq!(
std::fs::read_to_string(&unrecorded).expect("sibling still readable"),
existing,
"a refused file must be left byte-identical"
);
}