diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index bb54fe0..a812c65 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -196,8 +196,11 @@ fn cell_set(s: &mut Session, p: &Json) -> Result { Some(c) => c, None => s.doc()?.selection.active, }; - let input = - str_param(p, "input").or_else(|| str_param(p, "value")).map(str::to_string).or_else(|| p.get("value").map(json_to_input)).unwrap_or_default(); + let input = str_param(p, "input") + .or_else(|| str_param(p, "value")) + .map(str::to_string) + .or_else(|| p.get("value").map(json_to_input)) + .ok_or_else(|| bad("cell.set", "missing `input` (or `value`)"))?; let array = bool_param(p, "array").unwrap_or(false); let protected = s.doc()?.wb.sheet(sheet).is_some_and(|sh| sh.is_protected() && s.doc().is_ok_and(|d| d.wb.styles.get(sh.style_id(at)).protection.locked)); diff --git a/crates/mcp/src/tests.rs b/crates/mcp/src/tests.rs index cf590be..1d4abb4 100644 --- a/crates/mcp/src/tests.rs +++ b/crates/mcp/src/tests.rs @@ -339,6 +339,18 @@ fn serve_loop_over_buffers() { assert!(lines[2]["result"]["content"][0]["text"].as_str().unwrap().contains("42")); } +#[test] +fn cell_set_without_input_is_error() { + let mut s = server(); + ok(&mut s, "set_cell", json!({"cell": "B2", "input": "keep"})); + fails(&mut s, "execute_command", json!({"command": "cell.set", "params": {"cell": "B2"}})); + fails(&mut s, "execute_command", json!({"command": "cell.set", "params": {}})); + assert_eq!(ok(&mut s, "get_cell", json!({"cell": "B2"}))["text"], "keep"); + // An explicit empty string still clears. + ok(&mut s, "execute_command", json!({"command": "cell.set", "params": {"cell": "B2", "input": ""}})); + assert_eq!(ok(&mut s, "get_cell", json!({"cell": "B2"}))["text"], ""); +} + #[test] fn remote_backend_over_tcp() { // A fake app: answers engine.execute by running a headless session; closes after the first