#[test]
fn test_generate_request_full_deserialization() {
let json = r#"{
"prompt": "Hello world",
"max_tokens": 128,
"temperature": 0.7,
"stream": true,
"stop": [".", "!"]
}"#;
let req: GenerateRequest = serde_json::from_str(json).expect("value");
assert_eq!(req.prompt, "Hello world");
assert_eq!(req.max_tokens, 128);
assert!((req.temperature - 0.7).abs() < f32::EPSILON);
assert!(req.stream);
assert_eq!(req.stop.len(), 2);
assert_eq!(req.stop[0], ".");
assert_eq!(req.stop[1], "!");
}
#[test]
fn test_generate_request_minimal() {
let json = r#"{"prompt": "test"}"#;
let req: GenerateRequest = serde_json::from_str(json).expect("value");
assert_eq!(req.prompt, "test");
assert_eq!(req.max_tokens, 256);
assert!((req.temperature - 1.0).abs() < f32::EPSILON);
assert!(!req.stream);
assert!(req.stop.is_empty());
}
#[test]
fn test_generate_response_serialization() {
let resp = GenerateResponse {
text: "Hello there".to_string(),
tokens_generated: 5,
finish_reason: "stop".to_string(),
latency_ms: 123,
};
let json = serde_json::to_string(&resp).expect("value");
assert!(json.contains("\"text\":\"Hello there\""));
assert!(json.contains("\"tokens_generated\":5"));
assert!(json.contains("\"finish_reason\":\"stop\""));
assert!(json.contains("\"latency_ms\":123"));
}
#[test]
fn test_generate_response_roundtrip() {
let original = GenerateResponse {
text: "Output text".to_string(),
tokens_generated: 42,
finish_reason: "length".to_string(),
latency_ms: 567,
};
let json = serde_json::to_string(&original).expect("value");
let parsed: GenerateResponse = serde_json::from_str(&json).expect("value");
assert_eq!(parsed.text, "Output text");
assert_eq!(parsed.tokens_generated, 42);
assert_eq!(parsed.finish_reason, "length");
assert_eq!(parsed.latency_ms, 567);
}
#[test]
fn test_stream_event_token_fields() {
let event = StreamEvent::token("world", 42);
assert_eq!(event.event, "token");
assert_eq!(event.data, "world");
assert_eq!(event.token_id, Some(42));
}
#[test]
fn test_stream_event_done_fields() {
let event = StreamEvent::done("length", 100);
assert_eq!(event.event, "done");
assert!(event.data.contains("\"finish_reason\":\"length\""));
assert!(event.data.contains("\"tokens_generated\":100"));
assert!(event.token_id.is_none());
}
#[test]
fn test_stream_event_error_fields() {
let event = StreamEvent::error("Out of memory");
assert_eq!(event.event, "error");
assert_eq!(event.data, "Out of memory");
assert!(event.token_id.is_none());
}
#[test]
fn test_stream_event_to_sse_format() {
let event = StreamEvent::token("hi", 1);
let sse = event.to_sse();
assert!(sse.starts_with("event: token\n"));
assert!(sse.contains("data: hi\n"));
assert!(sse.ends_with("\n\n"));
}
#[test]
fn test_stream_event_done_sse_format() {
let event = StreamEvent::done("stop", 5);
let sse = event.to_sse();
assert!(sse.starts_with("event: done\n"));
assert!(sse.contains("data: "));
assert!(sse.ends_with("\n\n"));
}
#[test]
fn test_stream_event_serialization() {
let event = StreamEvent::token("test", 99);
let json = serde_json::to_string(&event).expect("value");
assert!(json.contains("\"event\":\"token\""));
assert!(json.contains("\"data\":\"test\""));
assert!(json.contains("\"token_id\":99"));
}
#[test]
fn test_stream_event_done_serialization_skips_token_id() {
let event = StreamEvent::done("stop", 10);
let json = serde_json::to_string(&event).expect("value");
assert!(!json.contains("token_id"));
}
#[test]
fn test_transcribe_request_defaults() {
let json = r#"{}"#;
let req: TranscribeRequest = serde_json::from_str(json).expect("value");
assert!(req.language.is_none());
assert_eq!(req.task, "transcribe");
}
#[test]
fn test_transcribe_request_with_language() {
let json = r#"{"language": "fr", "task": "translate"}"#;
let req: TranscribeRequest = serde_json::from_str(json).expect("value");
assert_eq!(req.language.as_deref(), Some("fr"));
assert_eq!(req.task, "translate");
}
#[test]
fn test_transcribe_response_roundtrip() {
let original = TranscribeResponse {
text: "Hello world".to_string(),
language: "en".to_string(),
duration_seconds: 3.5,
latency_ms: 200,
};
let json = serde_json::to_string(&original).expect("value");
let parsed: TranscribeResponse = serde_json::from_str(&json).expect("value");
assert_eq!(parsed.text, "Hello world");
assert_eq!(parsed.language, "en");
assert!((parsed.duration_seconds - 3.5).abs() < f64::EPSILON);
assert_eq!(parsed.latency_ms, 200);
}
#[test]
fn test_server_info_serialization() {
let info = ServerInfo {
name: "apr-serve".to_string(),
version: "0.25.1".to_string(),
model_id: "my-model".to_string(),
};
let json = serde_json::to_string(&info).expect("value");
assert!(json.contains("\"name\":\"apr-serve\""));
assert!(json.contains("\"version\":\"0.25.1\""));
assert!(json.contains("\"model_id\":\"my-model\""));
}
#[test]
fn test_server_info_roundtrip() {
let original = ServerInfo {
name: "test".to_string(),
version: "1.2.3".to_string(),
model_id: "model-abc".to_string(),
};
let json = serde_json::to_string(&original).expect("value");
let parsed: ServerInfo = serde_json::from_str(&json).expect("value");
assert_eq!(parsed.name, "test");
assert_eq!(parsed.version, "1.2.3");
assert_eq!(parsed.model_id, "model-abc");
}
#[test]
fn test_chat_message_user_message() {
let json = r#"{"role": "user", "content": "Hello"}"#;
let msg: ChatMessage = serde_json::from_str(json).expect("value");
assert_eq!(msg.role, "user");
assert_eq!(msg.content.as_deref(), Some("Hello"));
assert!(msg.tool_calls.is_none());
assert!(msg.tool_call_id.is_none());
assert!(msg.name.is_none());
}
#[test]
fn test_chat_message_system_message() {
let json = r#"{"role": "system", "content": "You are helpful."}"#;
let msg: ChatMessage = serde_json::from_str(json).expect("value");
assert_eq!(msg.role, "system");
assert_eq!(msg.content.as_deref(), Some("You are helpful."));
}
#[test]
fn test_chat_message_assistant_with_tool_calls() {
let json = r#"{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_1",
"type": "function",
"function": {"name": "calc", "arguments": "{\"x\":1}"}
}]
}"#;
let msg: ChatMessage = serde_json::from_str(json).expect("value");
assert_eq!(msg.role, "assistant");
assert!(msg.content.is_none());
assert!(msg.tool_calls.is_some());
let calls = msg.tool_calls.expect("tool_calls");
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].id, "call_1");
assert_eq!(calls[0].function.name, "calc");
}
#[test]
fn test_chat_message_serialization_skips_none() {
let msg = ChatMessage {
role: "user".to_string(),
content: Some("Hi".to_string()),
tool_calls: None,
tool_call_id: None,
name: None,
};
let json = serde_json::to_string(&msg).expect("value");
assert!(!json.contains("tool_calls"));
assert!(!json.contains("tool_call_id"));
assert!(!json.contains("name"));
}
#[test]
fn test_chat_completion_request_minimal() {
let json = r#"{"messages": [{"role": "user", "content": "Hi"}]}"#;
let req: ChatCompletionRequest = serde_json::from_str(json).expect("value");
assert_eq!(req.model, ""); assert_eq!(req.messages.len(), 1);
assert!(req.tools.is_none());
assert!(req.tool_choice.is_none());
assert!(req.max_tokens.is_none());
assert!(!req.stream);
assert!(req.temperature.is_none());
assert!(req.top_p.is_none());
}
#[test]
fn test_chat_completion_request_full() {
let json = r#"{
"model": "gpt-4",
"messages": [
{"role": "system", "content": "Be helpful."},
{"role": "user", "content": "Hello"}
],
"max_tokens": 100,
"stream": true,
"temperature": 0.5,
"top_p": 0.9
}"#;
let req: ChatCompletionRequest = serde_json::from_str(json).expect("value");
assert_eq!(req.model, "gpt-4");
assert_eq!(req.messages.len(), 2);
assert_eq!(req.max_tokens, Some(100));
assert!(req.stream);
assert!((req.temperature.expect("temperature") - 0.5).abs() < f32::EPSILON);
assert!((req.top_p.expect("top_p") - 0.9).abs() < f32::EPSILON);
}
#[test]
fn test_chat_completion_response_roundtrip() {
let original = ChatCompletionResponse {
id: "chatcmpl-001".to_string(),
object: "chat.completion".to_string(),
created: 1700000000,
model: "apr".to_string(),
choices: vec![ChatChoice {
index: 0,
message: ChatMessage {
role: "assistant".to_string(),
content: Some("Hello!".to_string()),
tool_calls: None,
tool_call_id: None,
name: None,
},
finish_reason: Some("stop".to_string()),
}],
usage: Some(TokenUsage {
prompt_tokens: 5,
completion_tokens: 1,
total_tokens: 6,
}),
};
let json = serde_json::to_string(&original).expect("value");
let parsed: ChatCompletionResponse = serde_json::from_str(&json).expect("value");
assert_eq!(parsed.id, "chatcmpl-001");
assert_eq!(parsed.choices.len(), 1);
assert_eq!(parsed.choices[0].message.content.as_deref(), Some("Hello!"));
assert_eq!(parsed.usage.as_ref().expect("as_ref").total_tokens, 6);
}
#[test]
fn test_token_usage_roundtrip() {
let usage = TokenUsage {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
};
let json = serde_json::to_string(&usage).expect("value");
let parsed: TokenUsage = serde_json::from_str(&json).expect("value");
assert_eq!(parsed.prompt_tokens, 10);
assert_eq!(parsed.completion_tokens, 20);
assert_eq!(parsed.total_tokens, 30);
}
#[test]
fn test_tool_choice_mode_auto() {
let json = r#""auto""#;
let choice: ToolChoice = serde_json::from_str(json).expect("value");
match choice {
ToolChoice::Mode(mode) => assert_eq!(mode, "auto"),
_ => panic!("Expected Mode variant"),
}
}
#[test]
fn test_tool_choice_mode_none() {
let json = r#""none""#;
let choice: ToolChoice = serde_json::from_str(json).expect("value");
match choice {
ToolChoice::Mode(mode) => assert_eq!(mode, "none"),
_ => panic!("Expected Mode variant"),
}
}
#[test]
fn test_tool_choice_specific_function() {
let json = r#"{"type": "function", "function": {"name": "get_temp"}}"#;
let choice: ToolChoice = serde_json::from_str(json).expect("value");
match choice {
ToolChoice::Function {
tool_type,
function,
} => {
assert_eq!(tool_type, "function");
assert_eq!(function.name, "get_temp");
}
_ => panic!("Expected Function variant"),
}
}
#[test]
fn test_format_tools_prompt_empty() {
let result = format_tools_prompt(&[]);
assert!(result.is_empty());
}
#[test]
fn test_format_tools_prompt_single_tool() {
let tools = vec![Tool {
tool_type: "function".to_string(),
function: FunctionDef {
name: "search".to_string(),
description: Some("Search the web".to_string()),
parameters: None,
},
}];
let prompt = format_tools_prompt(&tools);
assert!(prompt.contains("### search"));
assert!(prompt.contains("Search the web"));
assert!(prompt.contains("tool_call"));
}
#[test]
fn test_format_tools_prompt_multiple_tools() {
let tools = vec![
Tool {
tool_type: "function".to_string(),
function: FunctionDef {
name: "tool_a".to_string(),
description: Some("First tool".to_string()),
parameters: None,
},
},
Tool {
tool_type: "function".to_string(),
function: FunctionDef {
name: "tool_b".to_string(),
description: Some("Second tool".to_string()),
parameters: Some(serde_json::json!({"type": "object"})),
},
},
];
let prompt = format_tools_prompt(&tools);
assert!(prompt.contains("### tool_a"));
assert!(prompt.contains("### tool_b"));
assert!(prompt.contains("First tool"));
assert!(prompt.contains("Second tool"));
assert!(prompt.contains("Parameters:"));
}