Skip to main content

devsper_providers/
mock.rs

1use devsper_core::{LlmProvider, LlmRequest, LlmResponse, StopReason};
2use anyhow::Result;
3use async_trait::async_trait;
4
5/// Mock provider for tests — returns canned responses without HTTP calls.
6pub struct MockProvider {
7    pub response: String,
8}
9
10impl MockProvider {
11    pub fn new(response: impl Into<String>) -> Self {
12        Self {
13            response: response.into(),
14        }
15    }
16}
17
18#[async_trait]
19impl LlmProvider for MockProvider {
20    async fn generate(&self, req: LlmRequest) -> Result<LlmResponse> {
21        Ok(LlmResponse {
22            content: self.response.clone(),
23            tool_calls: vec![],
24            input_tokens: req
25                .messages
26                .iter()
27                .map(|m| m.content.len() as u32 / 4)
28                .sum(),
29            output_tokens: self.response.len() as u32 / 4,
30            model: req.model.clone(),
31            stop_reason: StopReason::EndTurn,
32        })
33    }
34
35    fn name(&self) -> &str {
36        "mock"
37    }
38
39    fn supports_model(&self, model: &str) -> bool {
40        model.starts_with("mock")
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use devsper_core::{LlmMessage, LlmRole};
48
49    #[tokio::test]
50    async fn mock_returns_canned_response() {
51        let p = MockProvider::new("hello world");
52        let req = LlmRequest {
53            model: "mock".to_string(),
54            messages: vec![LlmMessage {
55                role: LlmRole::User,
56                content: "hi".to_string(),
57            }],
58            tools: vec![],
59            max_tokens: None,
60            temperature: None,
61            system: None,
62        };
63        let res = p.generate(req).await.unwrap();
64        assert_eq!(res.content, "hello world");
65        assert_eq!(res.model, "mock");
66    }
67}