From 4277cf6f84872f0e2015f7d819866c4474a8ce6e Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Tue, 22 Sep 2026 21:28:58 -0700 Subject: [PATCH] y1av: seat the at-cursor tests off index 0 delete-formula-at-cursor, switch-view-at-cursor and delete-view-at-cursor were asserted with the cursor at 0 of a one-element list, where the item under the cursor and the first item are one item. A DeleteFormulaAtCursor hard-wired to index 0 passed the whole workspace. Now each cursor sits at index 1 of a longer list; the index-0 rivals fail. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze --- context/test-evidence/command.md | 29 +++++++-- .../src/command/cmd/panel.rs | 62 ++++++++++++++----- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/context/test-evidence/command.md b/context/test-evidence/command.md index fcbee537..34b87505 100644 --- a/context/test-evidence/command.md +++ b/context/test-evidence/command.md @@ -4721,17 +4721,38 @@ are now named, so moving the right cursor on the wrong panel fails. ### delete_formula_at_cursor_with_formulas -Asserts the `RemoveFormula` names `Profit` in `Type` — the formula the cursor is -on — rather than merely that a formula was removed. +Asserts the `RemoveFormula` names the formula the cursor is on, rather than merely +that a formula was removed. PR #148 named the field, but its fixture was **one** +formula with the cursor at **0**, where "the formula under the cursor" and "the +first formula" are the same formula (improvise-y1av). The rival it could not see — +`DeleteFormulaAtCursor` reading index `0` instead of `ctx.formula_cursor` — passed +the **whole workspace**, so `d` in the formula panel deleting the wrong formula had +no guard anywhere. Now two formulas, cursor at **1**: it must remove `Tax` and must +not name `Profit`. Rival `let cursor = 0usize.min(..)` injected, full suite run: +this is the only test that fails. ### switch_view_at_cursor_with_valid_cursor -Names `Default`, the view under the cursor. +Names the view under the cursor. Same fixture defect as above (improvise-y1av): +`two_cat_model` has one view, so the cursor at 0 named `Default` whether or not +the command read it. Now `Default, Other` with the cursor at **1**, asserting +`Other`. Rival `get(cursor).and(first())` — in bounds only, so the out-of-bounds +arm is unchanged and `switch_view_at_cursor_out_of_bounds_returns_empty` cannot be +what catches it — injected and run: fails here, in +`delete_view_at_cursor_deletes_the_view_under_a_nonzero_cursor`, and in the two +`wasm-client/tests/lockstep.rs` tests that were previously the only guard. + +### delete_view_at_cursor_deletes_the_view_under_a_nonzero_cursor + +improvise-y1av. `Default, Other, Third` with the cursor at **1**: deletes `Other` +and steps the panel cursor back to 0. The same rival as above fails it. ### delete_view_at_cursor_zero_does_not_adjust_cursor Names `Default` too. Its existing negative half — no `SetPanelCursor` at -position 0 — is unchanged. +position 0 — is unchanged. Position 0 is this test's subject, so it cannot +separate "the view under the cursor" from "the first view" and does not try to; +the test above does. ### commit_category_add_with_name_produces_add_effect diff --git a/crates/improvise-command/src/command/cmd/panel.rs b/crates/improvise-command/src/command/cmd/panel.rs index 1d27a42f..7c17c7f1 100644 --- a/crates/improvise-command/src/command/cmd/panel.rs +++ b/crates/improvise-command/src/command/cmd/panel.rs @@ -395,39 +395,50 @@ mod tests { assert!(effects.is_empty()); } + /// Evidence: `context/test-evidence/command.md`. #[test] fn delete_formula_at_cursor_with_formulas() { let mut m = two_cat_model(); - m.model.add_formula(crate::formula::ast::Formula { - raw: "Profit = Revenue - Cost".to_string(), - target: "Profit".to_string(), - target_category: "Type".to_string(), - expr: crate::formula::ast::Expr::Number(0.0), - filter: None, - format: None, - }); + for (target, raw) in [("Profit", "Profit = 1"), ("Tax", "Tax = 2")] { + m.model.add_formula(crate::formula::ast::Formula { + raw: raw.to_string(), + target: target.to_string(), + target_category: "Type".to_string(), + expr: crate::formula::ast::Expr::Number(0.0), + filter: None, + format: None, + }); + } let layout = make_layout(&m); let reg = make_registry(); - let ctx = make_ctx(&m, &layout, ®); + let mut ctx = make_ctx(&m, &layout, ®); + ctx.formula_cursor = 1; let effects = DeleteFormulaAtCursor.execute(&ctx); let dbg = effects_debug(&effects); assert!( - dbg.contains("RemoveFormula { target: \"Profit\", target_category: \"Type\" }"), - "must remove the formula AT THE CURSOR, got: {dbg}" + dbg.contains("RemoveFormula { target: \"Tax\", target_category: \"Type\" }"), + "must remove the formula AT THE CURSOR (Tax, index 1), got: {dbg}" + ); + assert!( + !dbg.contains("\"Profit\""), + "removed the first formula, not the one under the cursor: {dbg}" ); } + /// Evidence: `context/test-evidence/command.md`. #[test] fn switch_view_at_cursor_with_valid_cursor() { - let m = two_cat_model(); + let mut m = two_cat_model(); + m.create_view("Other"); let layout = make_layout(&m); let reg = make_registry(); - let ctx = make_ctx(&m, &layout, ®); + let mut ctx = make_ctx(&m, &layout, ®); + ctx.view_panel_cursor = 1; let effects = SwitchViewAtCursor.execute(&ctx); let dbg = effects_debug(&effects); assert!( - dbg.contains("SwitchView(\"Default\")"), - "must switch to the view UNDER THE CURSOR, got: {dbg}" + dbg.contains("SwitchView(\"Other\")"), + "must switch to the view UNDER THE CURSOR (Other, index 1), got: {dbg}" ); assert!(dbg.contains("Normal"), "Expected Normal mode, got: {dbg}"); } @@ -504,6 +515,27 @@ mod tests { ); } + /// Evidence: `context/test-evidence/command.md`. + #[test] + fn delete_view_at_cursor_deletes_the_view_under_a_nonzero_cursor() { + let mut m = two_cat_model(); + m.create_view("Other"); + m.create_view("Third"); + let layout = make_layout(&m); + let reg = make_registry(); + let mut ctx = make_ctx(&m, &layout, ®); + ctx.view_panel_cursor = 1; + let dbg = effects_debug(&DeleteViewAtCursor.execute(&ctx)); + assert!( + dbg.contains("DeleteView(\"Other\")"), + "must delete the view UNDER THE CURSOR (Other, index 1), got: {dbg}" + ); + assert!( + dbg.contains("SetPanelCursor { panel: View, cursor: 0 }"), + "a delete off position 0 steps the cursor back, got: {dbg}" + ); + } + #[test] fn delete_view_at_cursor_zero_does_not_adjust_cursor() { let m = two_cat_model();