#![allow(
dead_code,
unused_imports,
clippy::cast_possible_truncation,
clippy::significant_drop_tightening,
clippy::doc_markdown,
clippy::redundant_clone,
clippy::uninlined_format_args,
clippy::manual_let_else,
clippy::match_wildcard_for_single_variants
)]
mod harness;
use harness::{assert_valid_chat_request, fixtures, model_test_cases, parameter_validation_tests};
use openai_client_base::models::ChatCompletionRequestUserMessageContentPart;
use openai_ergonomic::{
builders::{
chat::{image_url_part, system_user, text_part, tool_function, ChatCompletionBuilder},
responses::{
responses_simple, responses_system_user, responses_tool_function, ResponsesBuilder,
},
Builder,
},
errors::Error,
Detail,
};
use serde_json::json;
use std::time::Duration;
#[test]
fn test_chat_builder_comprehensive() {
let basic_builder = ChatCompletionBuilder::new("gpt-4").user("Hello, world!");
let request = assert_builder_success!(basic_builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "gpt-4");
assert_eq!(request.messages.len(), 1);
}
#[test]
fn test_chat_builder_all_parameters() {
let tool = tool_function(
"test_tool",
"A test tool",
json!({
"type": "object",
"properties": {
"param": {"type": "string"}
}
}),
);
let builder = ChatCompletionBuilder::new("gpt-4")
.system("You are a helpful assistant")
.user("Help me with testing")
.temperature(0.7)
.max_tokens(500)
.max_completion_tokens(400)
.top_p(0.9)
.frequency_penalty(0.1)
.presence_penalty(0.1)
.n(1)
.stop(vec!["STOP".to_string(), "END".to_string()])
.tools(vec![tool])
.user_id("test-user")
.seed(12345);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "gpt-4");
assert_eq!(request.messages.len(), 2);
assert_eq!(request.temperature, Some(0.7));
assert_eq!(request.max_tokens, Some(500));
assert_eq!(request.max_completion_tokens, Some(400));
assert_eq!(request.top_p, Some(0.9));
assert_eq!(request.frequency_penalty, Some(0.1));
assert_eq!(request.presence_penalty, Some(0.1));
assert_eq!(request.n, Some(1));
assert_eq!(request.seed, Some(12345));
assert!(request.stop.is_some());
assert!(request.tools.is_some());
assert_eq!(request.user, Some("test-user".to_string()));
}
#[test]
fn test_responses_builder_comprehensive() {
let tool = responses_tool_function(
"search",
"Search for information",
json!({
"type": "object",
"properties": {
"query": {"type": "string"}
}
}),
);
let builder = responses_system_user(
"gpt-4",
"You are a search assistant",
"Find information about Rust",
)
.temperature(0.8)
.max_completion_tokens(500)
.tool(tool)
.json_mode()
.stream(true);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "gpt-4");
assert_eq!(request.messages.len(), 2);
assert_eq!(request.temperature, Some(0.8));
assert_eq!(request.max_completion_tokens, Some(500));
assert!(request.tools.is_some());
assert!(request.response_format.is_some());
assert_eq!(request.stream, Some(true));
}
#[test]
fn test_chat_builder_vision() {
let text_part = text_part("What do you see in this image?");
let image_part = image_url_part("https://example.com/image.jpg");
let image_with_detail = openai_ergonomic::builders::chat::image_url_part_with_detail(
"https://example.com/detailed.jpg",
Detail::High,
);
let builder = ChatCompletionBuilder::new("gpt-4-vision-preview").user_with_parts(vec![
text_part,
image_part,
image_with_detail,
]);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "gpt-4-vision-preview");
assert_eq!(request.messages.len(), 1);
if let Some(
openai_client_base::models::ChatCompletionRequestMessage::ChatCompletionRequestUserMessage(
user_msg,
),
) = request.messages.first()
{
match user_msg.content.as_ref() {
openai_client_base::models::ChatCompletionRequestUserMessageContent::ArrayOfContentParts(parts) => {
assert_eq!(parts.len(), 3);
match &parts[0] {
ChatCompletionRequestUserMessageContentPart::ChatCompletionRequestMessageContentPartText(text) => {
assert_eq!(text.text, "What do you see in this image?");
}
_ => panic!("Expected text part"),
}
for (i, part) in parts.iter().skip(1).enumerate() {
match part {
ChatCompletionRequestUserMessageContentPart::ChatCompletionRequestMessageContentPartImage(img) => {
assert!(img.image_url.url.starts_with("https://"));
if i == 1 {
assert_eq!(img.image_url.detail, Some(Detail::High));
}
}
_ => panic!("Expected image part"),
}
}
}
_ => panic!("Expected array content for multimodal message"),
}
} else {
panic!("Expected user message");
}
}
#[test]
fn test_base64_image_handling() {
let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==";
let media_type = "image/png";
let image_part = openai_ergonomic::builders::chat::image_base64_part(base64_data, media_type);
match image_part {
ChatCompletionRequestUserMessageContentPart::ChatCompletionRequestMessageContentPartImage(img) => {
assert!(img.image_url.url.starts_with("data:image/png;base64,"));
assert!(img.image_url.url.contains(base64_data));
}
_ => panic!("Expected image part"),
}
}
#[test]
fn test_json_schema_builder() {
let schema = json!({
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"},
"email": {"type": "string", "format": "email"}
},
"required": ["name", "age"]
});
let builder =
responses_simple("gpt-4", "Generate a person object").json_schema("person", schema.clone());
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert!(request.response_format.is_some());
let response_format = request.response_format.unwrap();
let schema_format = match response_format.as_ref() {
openai_client_base::models::CreateChatCompletionRequestAllOfResponseFormat::ResponseFormatJsonSchema(format) => format,
other => panic!("expected json schema response format, got {other:?}"),
};
assert_eq!(schema_format.json_schema.name, "person");
assert!(schema_format.json_schema.schema.is_some());
}
#[test]
fn test_reasoning_effort_parameter() {
let builder = ResponsesBuilder::new("o3-mini")
.user("Solve this complex problem")
.reasoning_effort("high");
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "o3-mini");
assert!(request.reasoning_effort.is_some());
}
#[test]
fn test_parameter_boundary_validation() {
let boundary_tests = parameter_validation_tests();
for (test_name, value, should_be_valid) in boundary_tests {
match test_name {
name if name.starts_with("temperature") => {
let builder = ChatCompletionBuilder::new("gpt-4")
.user("test")
.temperature(value);
if should_be_valid {
let request = assert_builder_success!(builder);
assert_eq!(request.temperature, Some(value));
} else {
let result = builder.build();
assert!(result.is_err(), "Temperature {value} should be invalid");
}
}
name if name.starts_with("top_p") => {
let builder = ChatCompletionBuilder::new("gpt-4")
.user("test")
.top_p(value);
if should_be_valid {
let request = assert_builder_success!(builder);
assert_eq!(request.top_p, Some(value));
} else {
let result = builder.build();
assert!(result.is_err(), "Top-p {value} should be invalid");
}
}
name if name.starts_with("frequency_penalty") => {
let builder = ChatCompletionBuilder::new("gpt-4")
.user("test")
.frequency_penalty(value);
if should_be_valid {
let request = assert_builder_success!(builder);
assert_eq!(request.frequency_penalty, Some(value));
} else {
let result = builder.build();
assert!(
result.is_err(),
"Frequency penalty {value} should be invalid"
);
}
}
name if name.starts_with("presence_penalty") => {
let builder = ChatCompletionBuilder::new("gpt-4")
.user("test")
.presence_penalty(value);
if should_be_valid {
let request = assert_builder_success!(builder);
assert_eq!(request.presence_penalty, Some(value));
} else {
let result = builder.build();
assert!(
result.is_err(),
"Presence penalty {value} should be invalid"
);
}
}
_ => {} }
}
}
#[test]
fn test_model_configurations() {
let model_tests = model_test_cases();
for (_test_name, model) in model_tests {
let builder = ChatCompletionBuilder::new(model).user("Test message");
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, model);
}
}
#[test]
fn test_builder_validation_errors() {
let empty_builder = ChatCompletionBuilder::new("gpt-4");
let result = empty_builder.build();
assert!(result.is_err());
match result {
Err(Error::InvalidRequest(msg)) => {
assert!(msg.contains("At least one message is required"));
}
_ => panic!("Expected InvalidRequest error"),
}
let empty_model_builder = ChatCompletionBuilder::new("").user("Test message");
let result = empty_model_builder.build();
assert!(result.is_err());
}
#[test]
fn test_helper_function_consistency() {
let function_name = "test_function";
let description = "A test function";
let parameters = json!({"type": "object", "properties": {}});
let chat_tool = tool_function(function_name, description, parameters.clone());
let responses_tool = responses_tool_function(function_name, description, parameters);
assert_eq!(chat_tool.function.name, responses_tool.function.name);
assert_eq!(
chat_tool.function.description,
responses_tool.function.description
);
assert_eq!(
chat_tool.function.parameters,
responses_tool.function.parameters
);
}
#[test]
fn test_complex_conversation_building() {
let mut builder = ChatCompletionBuilder::new("gpt-4").system("You are a helpful assistant");
builder = builder.user("What is 2+2?");
builder = builder.assistant("2+2 equals 4.");
builder = builder.user("What about 3+3?");
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.messages.len(), 4);
assert_eq!(request.model, "gpt-4");
}
#[test]
fn test_complex_function_calling() {
let complex_tool = tool_function(
"search_database",
"Search through a database with filters",
json!({
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query"
},
"filters": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["books", "movies", "music"]
},
"min_rating": {
"type": "number",
"minimum": 0,
"maximum": 10
}
}
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 10
}
},
"required": ["query"]
}),
);
let builder = system_user(
"gpt-4",
"You are a search assistant",
"Find me some good books",
)
.tools(vec![complex_tool]);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert!(request.tools.is_some());
let tools = request.tools.unwrap();
assert_eq!(tools.len(), 1);
}
#[test]
fn test_streaming_configuration() {
let builder = responses_simple("gpt-4", "Tell me a story")
.stream(true)
.max_tokens(200);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.stream, Some(true));
assert_eq!(request.max_tokens, Some(200));
}
#[test]
fn test_json_mode_configuration() {
let builder = responses_simple("gpt-4", "Generate a JSON object").json_mode();
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert!(request.response_format.is_some());
}
#[test]
fn test_stop_sequences() {
let stop_sequences = vec!["STOP".to_string(), "END".to_string(), "DONE".to_string()];
let builder = ChatCompletionBuilder::new("gpt-4")
.user("Count to 10")
.stop(stop_sequences.clone());
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert!(request.stop.is_some());
}
#[test]
fn test_request_serialization() {
let tool = responses_tool_function(
"calculate",
"Perform calculations",
json!({
"type": "object",
"properties": {
"expression": {"type": "string"}
}
}),
);
let builder = responses_system_user("gpt-4", "You are a math assistant", "Calculate 2+2")
.temperature(0.7)
.max_completion_tokens(100)
.tool(tool)
.json_mode();
let request = assert_builder_success!(builder);
let json_result = serde_json::to_string(&request);
assert!(json_result.is_ok());
let json_string = json_result.unwrap();
assert!(json_string.contains("gpt-4"));
assert!(json_string.contains("calculate"));
assert!(json_string.contains("temperature"));
}
#[test]
fn test_builder_performance() {
use harness::assert_performance;
let _request = assert_performance(
|| {
let tool = tool_function("test", "test", json!({"type": "object", "properties": {}}));
ChatCompletionBuilder::new("gpt-4")
.system("System message")
.user("User message")
.temperature(0.7)
.max_tokens(100)
.tools(vec![tool])
.build()
.unwrap()
},
Duration::from_millis(10),
"builder_creation_and_build",
);
}
#[test]
fn test_with_fixtures() {
let _chat_fixture = fixtures::chat_requests::with_functions();
let tool = tool_function(
"get_weather",
"Get current weather information",
json!({
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The location to get weather for"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units"
}
},
"required": ["location"]
}),
);
let builder = ChatCompletionBuilder::new("gpt-4")
.user("What's the weather in Paris?")
.tools(vec![tool]);
let request = assert_builder_success!(builder);
assert_valid_chat_request(&request);
assert_eq!(request.model, "gpt-4");
assert_eq!(request.messages.len(), 1);
assert!(request.tools.is_some());
assert_eq!(request.tools.as_ref().unwrap().len(), 1);
}
#[test]
fn test_builder_error_handling() {
let invalid_model_builder = ResponsesBuilder::new("").user("Test");
let result = invalid_model_builder.build();
assert!(result.is_err());
let no_messages_builder = ResponsesBuilder::new("gpt-4");
let result = no_messages_builder.build();
assert!(result.is_err());
}
#[test]
fn test_all_image_detail_levels() {
let details = vec![Detail::Auto, Detail::Low, Detail::High];
for detail in details {
let image_part = openai_ergonomic::builders::chat::image_url_part_with_detail(
"https://example.com/test.jpg",
detail,
);
match image_part {
ChatCompletionRequestUserMessageContentPart::ChatCompletionRequestMessageContentPartImage(img) => {
assert_eq!(img.image_url.detail, Some(detail));
assert!(img.image_url.url.starts_with("https://"));
}
_ => panic!("Expected image part"),
}
}
}