Commands: an earlier invocation's frontend failure isn't a later command's result (#696)

This commit is contained in:
tylerjenningsw
2026-10-12 22:03:01 -04:00
parent ecf485e375
commit e643457fa3
2 changed files with 34 additions and 3 deletions
+6 -3
View File
@@ -752,6 +752,8 @@ fn clipboard_note(s: &effectcraft_engine::Session) -> String {
}
pub(crate) fn run_engine(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, params: Value) -> Result<Value, String> {
// Events already queued belong to earlier invocations (`engine.execute` defers them).
let queued = app.session.events.len();
let mut r = app.session.execute(id, params).map_err(|e| e.to_string());
if let Err(e) = &r {
app.ui.status = e.clone();
@@ -759,14 +761,15 @@ pub(crate) fn run_engine(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str
ctx.copy_text(clipboard_note(&app.session));
}
let events = app.session.drain_events();
for ev in events {
for (index, ev) in events.into_iter().enumerate() {
match ev {
effectcraft_engine::Event::Frontend { command, params } => {
if let Err(e) = frontend(app, ctx, &command, params) {
app.ui.status = e.clone();
// A frontend-only command's own failure is the command's result (#696: a
// workspace save that didn't reach disk reported success).
if command == id && r.is_ok() {
// workspace save that didn't reach disk reported success). An earlier
// invocation's failure only sets the status.
if index >= queued && command == id && r.is_ok() {
r = Err(e);
}
}
@@ -438,3 +438,31 @@ fn saved_workspaces_reach_the_settings_folder_and_reload() {
assert_eq!(app.ui.dock, saved);
std::fs::remove_dir_all(dir).unwrap();
}
/// A frontend event left queued by an earlier invocation (`engine.execute` defers them) only
/// sets the status: its failure is not the result of a later command with the same id.
#[test]
fn an_earlier_queued_frontend_failure_is_not_a_later_commands_result() {
let store = Arc::new(MemoryConfig::default());
let mut app = EffectcraftApp::new(Session { config: Some(store.clone()), ..Default::default() });
let ctx = egui::Context::default();
let invoke = |app: &mut EffectcraftApp, id: &str, params| effectcraft_ui_egui::menus::invoke(app, &ctx, id, params);
// An earlier failure, then a success.
app.session.execute("window.workspace", json!({"name": "missing-old"})).unwrap();
invoke(&mut app, "window.workspace", json!({"name": "Minimal"})).unwrap();
assert_eq!(app.ui.workspace, "Minimal");
// An earlier failing delete, then a rename that reaches the store.
invoke(&mut app, "window.saveWorkspaceAs", json!({"name": "Original"})).unwrap();
app.session.execute("window.editWorkspaces", json!({"name": "missing-old", "delete": true})).unwrap();
invoke(&mut app, "window.editWorkspaces", json!({"name": "Original", "rename": "Renamed"})).unwrap();
assert!(store.read("workspaces.json").unwrap().contains("Renamed"));
// Both fail: the result is the current invocation's own failure.
app.session.execute("window.workspace", json!({"name": "missing-old"})).unwrap();
let error = invoke(&mut app, "window.workspace", json!({"name": "missing-new"})).unwrap_err();
assert!(error.contains("missing-new") && !error.contains("missing-old"), "{error}");
// An earlier failure of another command.
app.session.execute("window.workspace", json!({"name": "missing-old"})).unwrap();
invoke(&mut app, "window.saveWorkspaceAs", json!({"name": "Fine"})).unwrap();
// Its own failure with nothing queued is still the result.
assert!(invoke(&mut app, "window.workspace", json!({"name": "missing"})).is_err());
}