scud/backend/
simulated.rs1use anyhow::Result;
7use async_trait::async_trait;
8use tokio::sync::mpsc;
9use tokio_util::sync::CancellationToken;
10
11use super::{AgentBackend, AgentEvent, AgentHandle, AgentRequest, AgentResult, AgentStatus};
12
13pub struct SimulatedBackend;
15
16#[async_trait]
17impl AgentBackend for SimulatedBackend {
18 async fn execute(&self, req: AgentRequest) -> Result<AgentHandle> {
19 let (tx, rx) = mpsc::channel(16);
20 let cancel = CancellationToken::new();
21
22 let prompt_preview = if req.prompt.len() > 100 {
23 format!("{}...", &req.prompt[..97])
24 } else {
25 req.prompt.clone()
26 };
27
28 let response_text = format!("[Simulated] Response for: {}", prompt_preview);
29
30 tokio::spawn(async move {
31 let _ = tx
32 .send(AgentEvent::TextDelta(response_text.clone()))
33 .await;
34 let _ = tx
35 .send(AgentEvent::Complete(AgentResult {
36 text: response_text,
37 status: AgentStatus::Completed,
38 tool_calls: vec![],
39 usage: None,
40 }))
41 .await;
42 });
43
44 Ok(AgentHandle { events: rx, cancel })
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[tokio::test]
53 async fn test_simulated_response_contains_prompt() {
54 let backend = SimulatedBackend;
55 let req = AgentRequest {
56 prompt: "Write a test".into(),
57 ..Default::default()
58 };
59 let handle = backend.execute(req).await.unwrap();
60 let result = handle.result().await.unwrap();
61 assert!(result.text.contains("Write a test"));
62 assert!(result.text.starts_with("[Simulated]"));
63 }
64
65 #[tokio::test]
66 async fn test_simulated_truncates_long_prompt() {
67 let backend = SimulatedBackend;
68 let long_prompt = "x".repeat(200);
69 let req = AgentRequest {
70 prompt: long_prompt,
71 ..Default::default()
72 };
73 let handle = backend.execute(req).await.unwrap();
74 let result = handle.result().await.unwrap();
75 assert!(result.text.contains("..."));
76 assert!(result.text.len() < 200);
77 }
78}