Merge commit 'c91cc055c8ad81afd8f055b71c74af6819020637' into fix/31oe-cmdlang

# Conflicts:
#	context/code/cli.md
#	context/test-evidence/cli.md
This commit is contained in:
Claude (agent-cmdlang)
2026-09-24 22:33:04 -07:00
committed by Edward Langley
17 changed files with 1502 additions and 174 deletions
+127
View File
@@ -664,3 +664,130 @@ fn a_headless_import_keeps_duplicates_separate_when_told_to() {
assert!(doc.contains("= 10") && doc.contains("= 20"), "{doc}");
assert!(!doc.contains("= 30"), "{doc}");
}
/// code-maat's reports: three schemas, a stray note, and room for the output.
fn code_maat_folder(scratch: &Scratch) -> PathBuf {
let dir = scratch.path("maat");
std::fs::create_dir_all(&dir).unwrap();
for (name, body) in [
(
"coupling.csv",
"entity,coupled,degree,average-revs\nsrc/a.rs,src/b.rs,80,5\n",
),
("age.csv", "entity,age-months\nsrc/a.rs,2\nsrc/b.rs,9\n"),
(
"abs-churn.csv",
"date,added,deleted,commits\n2026-01-02,10,3,2\n",
),
("README.txt", "not a report\n"),
] {
std::fs::write(dir.join(name), body).unwrap();
}
dir
}
/// improvise-lpzu: a folder argument is its `*.csv` files, merged sparsely and
/// in name order. Evidence: `context/test-evidence/cli.md`.
#[test]
fn a_headless_import_of_a_folder_merges_its_csv_files_and_repeats_byte_for_byte() {
let scratch = Scratch::new("folder_import");
let dir = code_maat_folder(&scratch);
let out_path = dir.join("maat.improv");
let first = run_import(&dir, &out_path, &[]);
assert!(first.status.success(), "{}", stderr_of(&first));
let doc = std::fs::read_to_string(&out_path).unwrap();
for line in [
"File=coupling, _Measure=degree, coupled=|src/b.rs|, entity=|src/a.rs| = 80",
"File=age, _Measure=age-months, entity=|src/b.rs| = 9",
"File=abs-churn, _Measure=added, date=|2026-01-02| = 10",
] {
assert!(doc.lines().any(|l| l == line), "{line} missing from\n{doc}");
}
assert!(!doc.contains("README"), "{doc}");
let views: Vec<&str> = doc.lines().filter(|l| l.starts_with("## View: ")).collect();
assert_eq!(
views,
[
"## View: Default",
"## View: abs-churn",
"## View: age",
"## View: coupling"
]
);
assert!(doc.lines().any(|l| l == "File: filter, coupling"), "{doc}");
assert!(doc.contains("Initial View: Default"), "{doc}");
// The output now sits in the folder; a second run must not read it.
let second = run_import(&dir, &out_path, &[]);
assert!(second.status.success(), "{}", stderr_of(&second));
assert_eq!(std::fs::read_to_string(&out_path).unwrap(), doc);
}
/// improvise-lpzu: a file and a folder together are one merge, and a folder
/// with nothing to import is named. Evidence: `context/test-evidence/cli.md`.
#[test]
fn a_headless_import_takes_files_and_folders_together_and_refuses_an_empty_folder() {
let scratch = Scratch::new("folder_mixed");
let dir = code_maat_folder(&scratch);
let extra = scratch.path("extra.csv");
std::fs::write(&extra, "entity,age-months\nsrc/z.rs,4\n").unwrap();
let out_path = scratch.path("out.improv");
let out = Command::new(bin())
.args(["import", "--no-wizard", "-o"])
.arg(&out_path)
.arg(&extra)
.arg(&dir)
.output()
.unwrap();
assert!(out.status.success(), "{}", stderr_of(&out));
let doc = std::fs::read_to_string(&out_path).unwrap();
assert!(
doc.contains("File=extra, _Measure=age-months, entity=|src/z.rs| = 4"),
"{doc}"
);
assert!(doc.contains("File=coupling"), "{doc}");
let empty = scratch.path("empty");
std::fs::create_dir_all(&empty).unwrap();
let refused = run_import(&empty, &scratch.path("never.improv"), &[]);
assert!(!refused.status.success());
let err = stderr_of(&refused);
assert!(
err.contains(&format!("no CSV files in folder {}", empty.display())),
"{err}"
);
assert!(!scratch.path("never.improv").exists());
}
/// improvise-lpzu, the user's ruling: a folder always merges, so even one CSV
/// keeps its file identity. Evidence: `context/test-evidence/cli.md`.
#[test]
fn a_folder_of_one_csv_still_gets_a_file_category_and_its_view_but_a_lone_file_does_not() {
let scratch = Scratch::new("folder_single");
let dir = scratch.path("one");
std::fs::create_dir_all(&dir).unwrap();
let csv = dir.join("age.csv");
std::fs::write(&csv, "entity,age-months\nsrc/a.rs,2\n").unwrap();
let from_folder = scratch.path("folder.improv");
let out = run_import(&dir, &from_folder, &[]);
assert!(out.status.success(), "{}", stderr_of(&out));
let doc = std::fs::read_to_string(&from_folder).unwrap();
assert!(doc.contains("## Category: File\n- age\n"), "{doc}");
assert!(doc.lines().any(|l| l == "File: filter, age"), "{doc}");
assert!(
doc.contains("File=age, _Measure=age-months, entity=|src/a.rs| = 2"),
"{doc}"
);
let from_file = scratch.path("file.improv");
let out = run_import(&csv, &from_file, &[]);
assert!(out.status.success(), "{}", stderr_of(&out));
let doc = std::fs::read_to_string(&from_file).unwrap();
assert!(
!doc.contains("File"),
"a lone file keeps today's shape: {doc}"
);
}