forked from 1b-agent/opencadstudio-upstream
fix(control): reject request id payload reuse
This commit is contained in:
1 parent
2a2d9b55d9
commit
f5a741fd4e
1 file changed
+60
-3
+60
-3
@@ -628,15 +628,32 @@ impl OpenCADStudio {
|
|||||||
if old == &req {
|
if old == &req {
|
||||||
result.clone()
|
result.clone()
|
||||||
} else {
|
} else {
|
||||||
failure("request_id_reused", "Request payload changed")
|
failure(
|
||||||
|
"request_id_reused",
|
||||||
|
format!(
|
||||||
|
"Request payload changed for request_id '{id}'; generate a new unique request_id or resend the original payload",
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
Task::none(),
|
Task::none(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(p) = &self.control.pending {
|
if let Some(p) = &self.control.pending {
|
||||||
if p.id == id && p.request == req {
|
// `cancel` is a reference to the pending request, not a
|
||||||
|
// replay of its payload. It must reach the interactive cancel
|
||||||
|
// branch below even when it uses the same request_id.
|
||||||
|
if p.id == id && op != "cancel" {
|
||||||
return (
|
return (
|
||||||
json!({"ok":true,"status":"running","request_id":id}),
|
if p.request == req {
|
||||||
|
json!({"ok":true,"status":"running","request_id":id})
|
||||||
|
} else {
|
||||||
|
failure(
|
||||||
|
"request_id_reused",
|
||||||
|
format!(
|
||||||
|
"Request payload changed for request_id '{id}'; generate a new unique request_id or resend the original payload",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
Task::none(),
|
Task::none(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1763,6 +1780,46 @@ mod tests {
|
|||||||
stale["request_id"] = json!("stale");
|
stale["request_id"] = json!("stale");
|
||||||
assert_eq!(app.control_request(stale).0["code"], "stale_state");
|
assert_eq!(app.control_request(stale).0["code"], "stale_state");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn pending_request_id_reuse_rejects_changed_payload() {
|
||||||
|
let mut app = OpenCADStudio::new_for_test();
|
||||||
|
app.main_window = Some(iced::window::Id::unique());
|
||||||
|
assert_eq!(request(&mut app, json!({"op":"new"}))["status"], "completed");
|
||||||
|
|
||||||
|
let pending = request(
|
||||||
|
&mut app,
|
||||||
|
json!({
|
||||||
|
"op":"user_select",
|
||||||
|
"request_id":"pending-reuse",
|
||||||
|
"prompt":"pick the first object"
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert_eq!(pending["status"], "running", "{pending}");
|
||||||
|
|
||||||
|
let changed = request(
|
||||||
|
&mut app,
|
||||||
|
json!({
|
||||||
|
"op":"user_select",
|
||||||
|
"request_id":"pending-reuse",
|
||||||
|
"prompt":"pick a different object"
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert_eq!(changed["code"], "request_id_reused", "{changed}");
|
||||||
|
assert!(
|
||||||
|
changed["error"]
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.contains("pending-reuse"),
|
||||||
|
"{changed}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let cancelled = request(
|
||||||
|
&mut app,
|
||||||
|
json!({"op":"cancel","request_id":"cancel-pending-reuse"}),
|
||||||
|
);
|
||||||
|
assert_eq!(cancelled["status"], "cancelled", "{cancelled}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn control_errors_and_missing_entities_do_not_report_success() {
|
fn control_errors_and_missing_entities_do_not_report_success() {
|
||||||
let mut app = OpenCADStudio::new_for_test();
|
let mut app = OpenCADStudio::new_for_test();
|
||||||
|
|||||||
Reference in new issue
Block a user