use crate::Message;
use crate::provider::LlmProvider;
use serde_json::json;
use std::sync::Arc;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
async fn create_all_provider_implementations(
) -> (MockServer, Vec<(String, Arc<dyn LlmProvider>)>) {
let mock_server = MockServer::start().await;
let openai_client =
crate::tests::integration::create_integration_openai_client(&mock_server).await;
let anthropic_client =
crate::tests::integration::create_integration_anthropic_client(&mock_server).await;
let lmstudio_client =
crate::tests::integration::create_integration_lmstudio_client(&mock_server).await;
(
mock_server,
vec![
(
"OpenAI".to_string(),
Arc::new(openai_client) as Arc<dyn LlmProvider>,
),
(
"Anthropic".to_string(),
Arc::new(anthropic_client) as Arc<dyn LlmProvider>,
),
(
"LM Studio".to_string(),
Arc::new(lmstudio_client) as Arc<dyn LlmProvider>,
),
],
)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_all_providers_basic_interface_compliance() {
let (_mock_server, providers) = create_all_provider_implementations().await;
for (expected_name, provider) in providers {
let provider_name = provider.provider_name();
assert!(
!provider_name.is_empty(),
"Provider {} must return non-empty provider name",
expected_name
);
assert!(
provider_name.len() > 2,
"Provider {} name should be meaningful, got: {}",
expected_name,
provider_name
);
}
}
#[tokio::test]
async fn test_all_providers_execute_llm_compliance() {
let (mock_server, providers) = create_all_provider_implementations().await;
Mock::given(method("POST"))
.and(path("/v1/chat/completions"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"choices": [{
"message": {
"role": "assistant",
"content": "Test response"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 5,
"total_tokens": 15
}
})))
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.and(path("/v1/messages"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "msg_01ABC123",
"type": "message",
"role": "assistant",
"content": [{"type": "text", "text": "Test response"}],
"model": "claude-3-5-sonnet-20241022",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 10,
"output_tokens": 5
}
})))
.mount(&mock_server)
.await;
for (provider_name, provider) in providers {
let messages = vec![Message::user("Test message")];
let result = provider.execute_llm(messages, None).await;
assert!(
result.is_ok(),
"Provider {} failed execute_llm compliance: {}",
provider_name,
result.unwrap_err()
);
let response = result.unwrap();
assert!(
!response.content.is_empty(),
"Provider {} should return non-empty content",
provider_name
);
}
}
#[tokio::test]
async fn test_all_providers_execute_structured_llm_compliance() {
let (mock_server, providers) = create_all_provider_implementations().await;
let test_schema = json!({
"type": "object",
"properties": {
"answer": {"type": "string"},
"confidence": {"type": "number"}
},
"required": ["answer"]
});
Mock::given(method("POST"))
.and(path("/v1/chat/completions"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"choices": [{
"message": {
"role": "assistant",
"content": "{\"answer\": \"Test answer\", \"confidence\": 0.9}"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 10,
"total_tokens": 25
}
})))
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.and(path("/v1/messages"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "msg_01ABC124",
"type": "message",
"role": "assistant",
"content": [{"type": "text", "text": "{\"answer\": \"Test answer\", \"confidence\": 0.9}"}],
"model": "claude-3-5-sonnet-20241022",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 15,
"output_tokens": 10
}
})))
.mount(&mock_server)
.await;
for (provider_name, provider) in providers {
let messages = vec![Message::user("Give me a structured answer")];
let result = provider
.execute_structured_llm(messages, test_schema.clone(), None)
.await;
assert!(
result.is_ok(),
"Provider {} failed execute_structured_llm compliance: {}",
provider_name,
result.unwrap_err()
);
let response = result.unwrap();
assert!(
response.structured_response.is_some(),
"Provider {} should return structured data",
provider_name
);
}
}
}