Files
improvise/tests/records_wide_smoke.rs
Claude (agent-headless-import) 8bd4b1a77b fix(import): headless import refuses instead of silently succeeding (improvise-8jk6.4, improvise-8jk6.5, improvise-8jk6.6)
- 8jk6.4: the CSV parser yields source text (CsvRecords), and only the
  role reads it as a number. A forced category keeps 00565796 and
  220944e2 verbatim; measures and analysis read the text through
  csv_parser::number_in. CsvRecords reaches a pipeline only through
  ImportPipeline::from_csv, so handing CSV text to the JSON constructor
  no longer compiles.
- 8jk6.5: an import formula that does not parse is refused by name with
  its parse error: the build refuses (headless exits non-zero and writes
  nothing), and the wizard keeps it out of the list with the text still
  in the buffer.
- 8jk6.6: an import that kept no record refuses and says why, from the
  build and from the wizard's review step. A partial skip stays a
  warning with a count and exit 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-24 21:54:39 -07:00

68 lines
2.4 KiB
Rust

//! End-to-end check that a real imported model renders records mode in wide
//! format: CSV → import → save → load → records grid (improvise-c5v).
use improvise::import::wizard::ImportPipeline;
use improvise::persistence;
use improvise::view::{Axis, GridLayout};
/// Import a two-record CSV, round-trip it through `.improv`, and read the
/// records grid off the loaded model.
#[test]
fn imported_model_shows_one_row_per_record_with_measure_columns() {
let dir = tempfile::tempdir().expect("tempdir");
let csv = dir.path().join("w.csv");
std::fs::write(
&csv,
"region,product,revenue,cost\nEast,Shirts,100,40\nWest,Pants,200,80\n",
)
.expect("write csv");
let records = improvise::import::csv_parser::parse_csv(&csv).expect("csv parses");
let pipeline = ImportPipeline::from_csv(records);
let workbook = pipeline.build_model().expect("model builds");
let saved = dir.path().join("w.improv");
persistence::save(&workbook, &saved).expect("saves");
let mut wb = persistence::load(&saved).expect("loads");
let v = wb.active_view_mut();
v.set_axis("_Index", Axis::Row);
v.set_axis("_Dim", Axis::Column);
for cat in ["region", "product", "_Measure"] {
v.set_axis(cat, Axis::None);
}
let layout = GridLayout::new(&wb.model, wb.active_view());
assert!(layout.is_records_mode());
let cols: Vec<String> = (0..layout.col_count())
.map(|i| layout.col_label(i))
.collect();
assert_eq!(
cols,
vec!["product", "region", "+cat", "cost", "revenue", "+"],
"categories, the new-category column, one column per measure, then \
the new-measure column — each `+` sits next to what it adds to \
(improvise-2zq)"
);
assert_eq!(layout.row_count(), 2, "two source records, not four cells");
// Row order follows the store; the contract under test is that each
// source record is one row carrying both its measures side by side.
let mut rows: Vec<Vec<String>> = (0..layout.row_count())
.map(|r| {
(0..layout.col_count())
.map(|c| layout.records_display(r, c).unwrap())
.collect()
})
.collect();
rows.sort();
assert_eq!(
rows,
vec![
vec!["Pants", "West", "", "80", "200", ""],
vec!["Shirts", "East", "", "40", "100", ""],
],
"got {rows:?}"
);
}