Merge pull request #322 from fiddlerwoaroof/feat/8jk6.3-date-hierarchy
feat(import): a date column imports as one hierarchical category (improvise-8jk6.3)
This commit is contained in:
@@ -665,6 +665,167 @@ fn a_headless_import_keeps_duplicates_separate_when_told_to() {
|
||||
assert!(!doc.contains("= 30"), "{doc}");
|
||||
}
|
||||
|
||||
/// Dates across two years, one per month, so nothing collides.
|
||||
fn dated_csv(scratch: &Scratch) -> PathBuf {
|
||||
let csv = scratch.path("dated.csv");
|
||||
std::fs::write(
|
||||
&csv,
|
||||
"Date,Revenue\n2024-01-15,1\n2024-04-02,10\n2025-01-03,100\n",
|
||||
)
|
||||
.unwrap();
|
||||
csv
|
||||
}
|
||||
|
||||
/// improvise-8jk6.3, headless: `--extract` builds ONE hierarchical category by
|
||||
/// default, as the wizard does, and it is written with its `>` lines.
|
||||
#[test]
|
||||
fn a_headless_date_import_is_one_hierarchical_category() {
|
||||
let scratch = Scratch::new("date_hierarchy");
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&[
|
||||
"--extract",
|
||||
"Date:Year",
|
||||
"--extract",
|
||||
"Date:Quarter",
|
||||
"--extract",
|
||||
"Date:Month",
|
||||
],
|
||||
);
|
||||
assert!(out.status.success(), "{}", stderr_of(&out));
|
||||
let doc = std::fs::read_to_string(&out_path).unwrap();
|
||||
assert!(!doc.contains("Date_Month"), "{doc}");
|
||||
assert!(doc.contains("- |2024-01|[|2024-Q1|]"), "{doc}");
|
||||
assert!(doc.contains("> |2024-Q1|[|2024|]"), "{doc}");
|
||||
assert!(!doc.contains("expanded"), "it opens folded: {doc}");
|
||||
}
|
||||
|
||||
/// The flat split stays available headless, as `s` makes it in the wizard.
|
||||
#[test]
|
||||
fn a_headless_date_import_splits_when_told_to() {
|
||||
let scratch = Scratch::new("date_split");
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&["--extract", "Date:Month", "--split-date", "Date"],
|
||||
);
|
||||
assert!(out.status.success(), "{}", stderr_of(&out));
|
||||
let doc = std::fs::read_to_string(&out_path).unwrap();
|
||||
assert!(doc.contains("## Category: Date_Month"), "{doc}");
|
||||
assert!(doc.contains("2024-01-15"), "the raw dates stay: {doc}");
|
||||
}
|
||||
|
||||
/// improvise-8jk6.3 review: a `--split-date` that names nothing splittable
|
||||
/// said nothing and exited 0.
|
||||
#[test]
|
||||
fn split_date_naming_an_unknown_or_non_date_field_is_refused() {
|
||||
let scratch = Scratch::new("split_date_unknown");
|
||||
for field in ["Nope", "Revenue"] {
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&["--extract", "Date:Month", "--split-date", field],
|
||||
);
|
||||
assert!(!out.status.success(), "{field}: {}", stderr_of(&out));
|
||||
let err = stderr_of(&out);
|
||||
assert!(err.contains("--split-date") && err.contains(field), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
/// improvise-8jk6.3 review: `--axis Date_Month:column` names a category the
|
||||
/// hierarchical import no longer builds, and was dropped without a word.
|
||||
#[test]
|
||||
fn an_axis_naming_a_category_the_import_did_not_build_is_refused() {
|
||||
let scratch = Scratch::new("axis_unknown");
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&["--extract", "Date:Month", "--axis", "Date_Month:column"],
|
||||
);
|
||||
assert!(!out.status.success(), "{}", stderr_of(&out));
|
||||
let err = stderr_of(&out);
|
||||
assert!(
|
||||
err.contains("Date_Month") && err.contains("--split-date"),
|
||||
"{err}"
|
||||
);
|
||||
}
|
||||
|
||||
/// `--time` on a column with no date format makes it a time category without
|
||||
/// dates, so there is nothing to split.
|
||||
#[test]
|
||||
fn split_date_on_a_time_field_with_no_date_format_is_refused() {
|
||||
let scratch = Scratch::new("split_date_no_format");
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&["--time", "Revenue", "--split-date", "Revenue"],
|
||||
);
|
||||
assert!(!out.status.success(), "{}", stderr_of(&out));
|
||||
assert!(
|
||||
stderr_of(&out).contains("--split-date"),
|
||||
"{}",
|
||||
stderr_of(&out)
|
||||
);
|
||||
}
|
||||
|
||||
/// A name that is no date part gets the categories that exist, never the date
|
||||
/// hint (the README's old `--axis Measure:column` was this).
|
||||
#[test]
|
||||
fn an_absent_axis_category_that_is_no_date_part_names_what_was_built() {
|
||||
let scratch = Scratch::new("axis_unknown_plain");
|
||||
let out_path = scratch.path("out.improv");
|
||||
let out = run_import(
|
||||
&dated_csv(&scratch),
|
||||
&out_path,
|
||||
&["--axis", "Measure:column"],
|
||||
);
|
||||
assert!(!out.status.success(), "{}", stderr_of(&out));
|
||||
let err = stderr_of(&out);
|
||||
assert!(
|
||||
err.contains("'Measure'") && err.contains("_Measure"),
|
||||
"{err}"
|
||||
);
|
||||
assert!(!err.contains("--split-date"), "no date hint: {err}");
|
||||
}
|
||||
|
||||
/// The README's import recipe runs as printed.
|
||||
#[test]
|
||||
fn the_readmes_import_recipe_runs() {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let readme = std::fs::read_to_string(root.join("README.md")).unwrap();
|
||||
let start = readme.find("improvise import examples/demo.csv").unwrap();
|
||||
let recipe = readme[start..]
|
||||
.split("```")
|
||||
.next()
|
||||
.unwrap()
|
||||
.replace("\\\n", " ");
|
||||
let mut args: Vec<String> = vec![];
|
||||
for (i, part) in recipe.split('"').enumerate() {
|
||||
match i % 2 {
|
||||
1 => args.push(part.to_string()),
|
||||
_ => args.extend(part.split_whitespace().map(String::from)),
|
||||
}
|
||||
}
|
||||
let at = args.iter().position(|a| a == "-o").unwrap();
|
||||
args.truncate(at);
|
||||
let scratch = Scratch::new("readme_recipe");
|
||||
let out = Command::new(bin())
|
||||
.arg("import")
|
||||
.arg(root.join("examples/demo.csv"))
|
||||
.args(args.iter().skip(3))
|
||||
.arg("-o")
|
||||
.arg(scratch.path("out.improv"))
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(out.status.success(), "{}", stderr_of(&out));
|
||||
}
|
||||
|
||||
/// 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");
|
||||
|
||||
Reference in New Issue
Block a user