mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-08 17:14:49 +08:00
test(tools): end-to-end stall-detect and recovery loop coverage
Proves the clawhip restart/recover flow that gaebal-gajae flagged: 1. stall_detect_and_resolve_trust_end_to_end - Worker created without trusted_roots -> trust_auto_resolve=false - WorkerObserve with trust-prompt text -> status=trust_required, gate cleared=false - WorkerResolveTrust -> status=spawning, trust_gate_cleared=true - WorkerObserve with ready text -> status=ready_for_prompt Full resolve path verified end-to-end. 2. stall_detect_and_restart_recovery_end_to_end - Worker stalls at trust_required - WorkerRestart resets to spawning, trust_gate_cleared=false Documents the restart-then-re-acquire-trust flow. Note: seconds_since_update is in .claw/worker-state.json (state file), not in the Worker tool output struct. Staleness detection via state file is covered by emit_state_file_writes_worker_status_on_transition in worker_boot.rs tests. 87 tool tests passing, 0 failing.
This commit is contained in:
@@ -5618,6 +5618,101 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stall_detect_and_resolve_trust_end_to_end() {
|
||||||
|
// 1. Create worker WITHOUT trusted_roots so trust won't auto-resolve
|
||||||
|
let created = execute_tool(
|
||||||
|
"WorkerCreate",
|
||||||
|
&json!({"cwd": "/no/trusted/root/here"}),
|
||||||
|
)
|
||||||
|
.expect("WorkerCreate should succeed");
|
||||||
|
let created_output: serde_json::Value = serde_json::from_str(&created).expect("json");
|
||||||
|
let worker_id = created_output["worker_id"].as_str().expect("worker_id").to_string();
|
||||||
|
assert_eq!(created_output["trust_auto_resolve"], false);
|
||||||
|
|
||||||
|
// 2. Observe trust prompt screen text -> worker stalls at trust_required
|
||||||
|
let stalled = execute_tool(
|
||||||
|
"WorkerObserve",
|
||||||
|
&json!({
|
||||||
|
"worker_id": worker_id,
|
||||||
|
"screen_text": "Do you trust the files in this folder?\n[Allow] [Deny]"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.expect("WorkerObserve should succeed");
|
||||||
|
let stalled_output: serde_json::Value = serde_json::from_str(&stalled).expect("json");
|
||||||
|
assert_eq!(
|
||||||
|
stalled_output["status"], "trust_required",
|
||||||
|
"worker should stall at trust_required when trust prompt seen without allowlist"
|
||||||
|
);
|
||||||
|
assert_eq!(stalled_output["trust_gate_cleared"], false);
|
||||||
|
// 3. Clawhip calls WorkerResolveTrust to unblock
|
||||||
|
let resolved = execute_tool(
|
||||||
|
"WorkerResolveTrust",
|
||||||
|
&json!({"worker_id": worker_id}),
|
||||||
|
)
|
||||||
|
.expect("WorkerResolveTrust should succeed");
|
||||||
|
let resolved_output: serde_json::Value = serde_json::from_str(&resolved).expect("json");
|
||||||
|
assert_eq!(
|
||||||
|
resolved_output["status"], "spawning",
|
||||||
|
"worker should return to spawning after trust resolved"
|
||||||
|
);
|
||||||
|
assert_eq!(resolved_output["trust_gate_cleared"], true);
|
||||||
|
|
||||||
|
// 4. Ready screen text now advances worker normally
|
||||||
|
let ready = execute_tool(
|
||||||
|
"WorkerObserve",
|
||||||
|
&json!({
|
||||||
|
"worker_id": worker_id,
|
||||||
|
"screen_text": "Ready for input\n>"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.expect("WorkerObserve should succeed after trust resolved");
|
||||||
|
let ready_output: serde_json::Value = serde_json::from_str(&ready).expect("json");
|
||||||
|
assert_eq!(
|
||||||
|
ready_output["status"], "ready_for_prompt",
|
||||||
|
"worker should reach ready_for_prompt after trust resolved and ready screen seen"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stall_detect_and_restart_recovery_end_to_end() {
|
||||||
|
// Worker stalls at trust_required, clawhip restarts instead of resolving
|
||||||
|
let created = execute_tool(
|
||||||
|
"WorkerCreate",
|
||||||
|
&json!({"cwd": "/no/trusted/root/restart-test"}),
|
||||||
|
)
|
||||||
|
.expect("WorkerCreate should succeed");
|
||||||
|
let created_output: serde_json::Value = serde_json::from_str(&created).expect("json");
|
||||||
|
let worker_id = created_output["worker_id"].as_str().expect("worker_id").to_string();
|
||||||
|
|
||||||
|
// Force trust_required
|
||||||
|
let stalled = execute_tool(
|
||||||
|
"WorkerObserve",
|
||||||
|
&json!({
|
||||||
|
"worker_id": worker_id,
|
||||||
|
"screen_text": "trust this folder? [Yes] [No]"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.expect("WorkerObserve should succeed");
|
||||||
|
let stalled_output: serde_json::Value = serde_json::from_str(&stalled).expect("json");
|
||||||
|
assert_eq!(stalled_output["status"], "trust_required");
|
||||||
|
|
||||||
|
// WorkerRestart resets the worker
|
||||||
|
let restarted = execute_tool(
|
||||||
|
"WorkerRestart",
|
||||||
|
&json!({"worker_id": worker_id}),
|
||||||
|
)
|
||||||
|
.expect("WorkerRestart should succeed");
|
||||||
|
let restarted_output: serde_json::Value = serde_json::from_str(&restarted).expect("json");
|
||||||
|
assert_eq!(
|
||||||
|
restarted_output["status"], "spawning",
|
||||||
|
"restarted worker should be back at spawning"
|
||||||
|
);
|
||||||
|
assert_eq!(restarted_output["trust_gate_cleared"], false,
|
||||||
|
"restart clears trust — next observe loop must re-acquire trust"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn worker_terminate_on_unknown_id_returns_error() {
|
fn worker_terminate_on_unknown_id_returns_error() {
|
||||||
let result = execute_tool(
|
let result = execute_tool(
|
||||||
|
|||||||
Reference in New Issue
Block a user