feat: add MockLlmProvider for integration testing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>master
parent
ecf95ffe35
commit
17e054c257
@ -0,0 +1,134 @@
|
||||
//! Mock LLM provider for integration testing.
|
||||
|
||||
use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{json, Value};
|
||||
use crate::errors::AppError;
|
||||
use super::LlmProvider;
|
||||
|
||||
/// A mock LLM provider that returns deterministic responses.
|
||||
pub struct MockLlmProvider {
|
||||
default_category: String,
|
||||
search_urls: Vec<String>,
|
||||
link_urls: Vec<String>,
|
||||
}
|
||||
|
||||
impl MockLlmProvider {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
default_category: "Autre".to_string(),
|
||||
search_urls: Vec::new(),
|
||||
link_urls: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_default_category(mut self, category: &str) -> Self {
|
||||
self.default_category = category.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_search_urls(mut self, urls: Vec<String>) -> Self {
|
||||
self.search_urls = urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_link_urls(mut self, urls: Vec<String>) -> Self {
|
||||
self.link_urls = urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_arc(self) -> Arc<dyn LlmProvider> {
|
||||
Arc::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LlmProvider for MockLlmProvider {
|
||||
fn provider_id(&self) -> &str {
|
||||
"mock"
|
||||
}
|
||||
|
||||
async fn call_llm(
|
||||
&self,
|
||||
_model: &str,
|
||||
system_prompt: &str,
|
||||
user_prompt: &str,
|
||||
_response_schema: &Value,
|
||||
) -> Result<Value, AppError> {
|
||||
let sys_lower = system_prompt.to_lowercase();
|
||||
|
||||
// Classify/summarize call
|
||||
if sys_lower.contains("classer") {
|
||||
let title = user_prompt
|
||||
.lines()
|
||||
.find(|l| l.starts_with("Titre : "))
|
||||
.map(|l| l.trim_start_matches("Titre : ").to_string())
|
||||
.unwrap_or_else(|| "Mock Article".to_string());
|
||||
|
||||
return Ok(json!({
|
||||
"title": title,
|
||||
"summary": format!("Mock summary for: {}", title),
|
||||
"category": self.default_category,
|
||||
}));
|
||||
}
|
||||
|
||||
// Link extraction call
|
||||
if sys_lower.contains("liens") {
|
||||
return Ok(json!({ "urls": self.link_urls }));
|
||||
}
|
||||
|
||||
// Search call
|
||||
if sys_lower.contains("precis") {
|
||||
let items: Vec<Value> = self.search_urls.iter().map(|url| {
|
||||
json!({
|
||||
"title": format!("Search result: {}", url),
|
||||
"url": url,
|
||||
"summary": format!("Mock search summary for {}", url),
|
||||
})
|
||||
}).collect();
|
||||
return Ok(json!({ "category_0": items }));
|
||||
}
|
||||
|
||||
Ok(json!({"result": "mock response"}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_provider_returns_classify_response() {
|
||||
let provider = MockLlmProvider::new().with_default_category("AI News");
|
||||
let result = provider
|
||||
.call_llm("model", "Tu dois classer l'article", "Titre : GPT-7\n\nContenu...", &json!({}))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(result["title"], "GPT-7");
|
||||
assert_eq!(result["category"], "AI News");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_provider_returns_search_response() {
|
||||
let provider = MockLlmProvider::new()
|
||||
.with_search_urls(vec!["http://example.com/a".into()]);
|
||||
let result = provider
|
||||
.call_llm("model", "Tu es un assistant IA precis", "Recherche...", &json!({}))
|
||||
.await
|
||||
.unwrap();
|
||||
let items = result["category_0"].as_array().unwrap();
|
||||
assert_eq!(items.len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mock_provider_returns_link_extraction() {
|
||||
let provider = MockLlmProvider::new()
|
||||
.with_link_urls(vec!["http://example.com/post-1".into()]);
|
||||
let result = provider
|
||||
.call_llm("model", "Tu dois identifier les liens", "Links...", &json!({}))
|
||||
.await
|
||||
.unwrap();
|
||||
let urls = result["urls"].as_array().unwrap();
|
||||
assert_eq!(urls.len(), 1);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue