feat(import): import a folder of CSVs as one sparse model (improvise-lpzu, improvise-8jk6.2)

A folder argument expands to its *.csv documents through Storage::list,
and a record with no value for a category keeps its cell without that
coordinate instead of being skipped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Claude (agent-lpzu)
2026-09-24 20:43:31 -07:00
committed by Edward Langley
parent 1719f2cfad
commit e65d87fda9
4 changed files with 412 additions and 132 deletions
+84
View File
@@ -612,3 +612,87 @@ 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}");
// 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());
}