fix(commands): deny list during BEDIT

Refuse LAYOUT, VPORTS, MVIEW, WBLOCK, PURGE, RENAME, REFEDIT,
REFEDIT_BEGIN and PLOT from the command line while a block editor
session is open, matching the reference product's
"<CMD> command not allowed in block editor" behaviour (spec section 8,
based on the V7 hands-on check). XREF, NEW, OPEN and SAVEAS stay
allowed. The service-level RENAME guard stays as defense in depth.
This commit is contained in:
Karim Jerbi committed 2026-10-15 20:06:41 +01:00
1 parent 43cca2094b
commit 4143a01ff4
3 files changed
+139 -3

No files matched your search

+109
View File
@@ -1318,6 +1318,115 @@ mod bedit_characterization_tests {
assert!(app.tabs[tab].block_edits.is_empty());
}
/// V7 (reference check): while a block editor session is open this
/// command set is refused outright with "not allowed in block editor";
/// the refusal pushes no undo entry and starts no command.
#[test]
fn locked_deny_list_refuses_while_session_open() {
let mut app = OpenCADStudio::new_for_test();
let (insert, _) = setup_block_and_insert(&mut app);
let tab = app.active_tab;
let _ = app.dispatch_command(&format!("BEDIT_BEGIN:{}", insert.value()));
assert!(
app.tabs[tab].active_block_edit.is_some(),
"setup: BEDIT_BEGIN should open a session"
);
let undo_depth = app.tabs[tab].history.undo_stack.len();
for verb in [
"LAYOUT",
"VPORTS",
"MVIEW",
"WBLOCK",
"PURGE",
"RENAME",
"REFEDIT",
"REFEDIT_BEGIN",
"PLOT",
] {
let revision = app.command_line.error_revision;
let _ = app.dispatch_command(verb);
assert_eq!(
app.command_line.error_revision,
revision.wrapping_add(1),
"{verb} must refuse with a command-line error"
);
assert!(
app.command_line
.last_error
.as_deref()
.unwrap_or_default()
.contains("command not allowed in block editor"),
"{verb}: got {:?}",
app.command_line.last_error
);
assert!(
app.tabs[tab].active_block_edit.is_some(),
"{verb} must keep the session open"
);
assert!(
app.tabs[tab].active_cmd.is_none(),
"{verb} must not install a command"
);
assert_eq!(
app.tabs[tab].history.undo_stack.len(),
undo_depth,
"{verb} must push no undo entry"
);
}
}
/// Spec 8: a rejected command must not cancel a running command — the
/// deny hook sits before the running-command teardown.
#[test]
fn locked_deny_list_does_not_cancel_a_running_command() {
let mut app = OpenCADStudio::new_for_test();
let (insert, _) = setup_block_and_insert(&mut app);
let tab = app.active_tab;
let _ = app.dispatch_command(&format!("BEDIT_BEGIN:{}", insert.value()));
assert!(app.tabs[tab].active_block_edit.is_some());
let _ = app.dispatch_command("LINE");
assert_eq!(
app.tabs[tab].active_cmd.as_deref().map(|cmd| cmd.name()),
Some("LINE"),
"setup: LINE should be running inside the session"
);
let _ = app.dispatch_command("LAYOUT");
assert!(app
.command_line
.last_error
.as_deref()
.unwrap_or_default()
.contains("command not allowed in block editor"));
assert_eq!(
app.tabs[tab].active_cmd.as_deref().map(|cmd| cmd.name()),
Some("LINE"),
"the refused command must leave the running LINE untouched"
);
}
/// V7: the reference keeps XREF, NEW, OPEN, SAVEAS and BEDIT available
/// inside the editor — the deny list must not touch them.
#[test]
fn locked_deny_list_leaves_v7_allowed_commands_alone() {
for verb in ["XREF", "NEW", "OPEN", "SAVEAS", "BEDIT"] {
let mut app = OpenCADStudio::new_for_test();
let (insert, _) = setup_block_and_insert(&mut app);
let tab = app.active_tab;
let _ = app.dispatch_command(&format!("BEDIT_BEGIN:{}", insert.value()));
assert!(app.tabs[tab].active_block_edit.is_some());
let _ = app.dispatch_command(verb);
assert!(
!app.command_line
.last_error
.as_deref()
.unwrap_or_default()
.contains("not allowed in block editor"),
"{verb} is available in the reference session and must not be denied: {:?}",
app.command_line.last_error
);
}
}
// ---- documenting: today's behavior, expected to flip in Phase 1 ----
/// Discard removes local before-images before drawing undo resumes.