test(model,ui): add tests for precision and cat tree

Add unit tests for model precision and category tree rendering:

- `src/model/types.rs` : Added `formula_chain_preserves_full_precision` to
  ensure formulas use full `f64` precision for calculations, even when
  display is rounded.
- `src/ui/cat_tree.rs` : Added comprehensive tests for `build_cat_tree` ,
  covering empty models (virtual categories), expanded/collapsed states,
  and item rendering.

Co-Authored-By: fiddlerwoaroof/git-smart-commit (unsloth/gemma-4-26B-A4B-it-GGUF:UD-Q5_K_XL)
This commit is contained in:
Edward Langley
2026-04-08 22:27:37 -07:00
parent 433a20928a
commit da076eadc8
2 changed files with 162 additions and 0 deletions

View File

@ -1136,6 +1136,54 @@ mod formula_tests {
);
}
// ── Precision guarantee: formulas compute at full f64 precision ────────
/// Display rounds to the view's decimal setting, but the underlying
/// model must keep full f64 precision so chained calculations don't
/// accumulate rounding errors.
#[test]
fn formula_chain_preserves_full_precision() {
let mut m = Model::new("Test");
m.add_category("Measure").unwrap();
if let Some(cat) = m.category_mut("Measure") {
cat.add_item("Price");
cat.add_item("Tax");
cat.add_item("Total");
}
// Price = 10.0, Tax = Price * 0.075 = 0.75, Total = Price + Tax = 10.75
m.set_cell(coord(&[("Measure", "Price")]), CellValue::Number(10.0));
m.add_formula(parse_formula("Tax = Price * 0.075", "Measure").unwrap());
m.add_formula(parse_formula("Total = Price + Tax", "Measure").unwrap());
let tax = m
.evaluate(&coord(&[("Measure", "Tax")]))
.and_then(|v| v.as_f64())
.unwrap();
let total = m
.evaluate(&coord(&[("Measure", "Total")]))
.and_then(|v| v.as_f64())
.unwrap();
// Full precision: Tax is exactly 0.75, Total is exactly 10.75
assert!(approx_eq(tax, 0.75), "Tax should be 0.75 (full prec), got {tax}");
assert!(
approx_eq(total, 10.75),
"Total should be 10.75 (full prec), got {total}"
);
// If display rounded Tax to 0 decimals (showing "1"), and the formula
// used that rounded value, Total would be 11 instead of 10.75.
// This proves the formula uses the raw f64, not the display string.
use crate::format::format_f64;
let tax_display = format_f64(tax, true, 0);
assert_eq!(tax_display, "1", "display rounds 0.75 → 1");
// But the computed Total is 10.75, not 10 + 1 = 11
assert!(
approx_eq(total, 10.75),
"Total must use full-precision Tax (0.75), not display-rounded (1)"
);
}
/// Bug: remove_formula matches by target name alone, so removing "Profit"
/// in "Measure" also destroys the "Profit" formula in "KPI".
/// After targeted removal, the other category's formula must survive.