#[tokio::test]
async fn test_chat_completions_multi_turn_conversation() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is 2+2?"},
{"role": "assistant", "content": "2+2 equals 4."},
{"role": "user", "content": "And what is 3+3?"}
]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_long_conversation() {
let app = create_test_app_shared();
let mut messages = vec![serde_json::json!({"role": "system", "content": "You are helpful."})];
for i in 0..10 {
messages.push(serde_json::json!({"role": "user", "content": format!("Turn {i}")}));
messages.push(serde_json::json!({"role": "assistant", "content": format!("Response {i}")}));
}
messages.push(serde_json::json!({"role": "user", "content": "Final question"}));
let req_body = serde_json::json!({
"model": "default",
"messages": messages
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_unicode_content() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "こんにちは世界 🌍 مرحبا 你好"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_special_chars() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Test with \"quotes\", 'apostrophes', and\nnewlines\ttabs"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_empty_model_name() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "",
"messages": [{"role": "user", "content": "Test"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_whitespace_content() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": " spaces "}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
assert!(
response.status() == StatusCode::OK
|| response.status() == StatusCode::NOT_FOUND
|| response.status() == StatusCode::INTERNAL_SERVER_ERROR
|| response.status() == StatusCode::SERVICE_UNAVAILABLE
|| response.status() == StatusCode::UNPROCESSABLE_ENTITY
);
}
#[tokio::test]
async fn test_chat_completions_response_has_usage() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Test"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let result: ChatCompletionResponse = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => return, };
assert!(result.usage.prompt_tokens > 0);
assert_eq!(
result.usage.total_tokens,
result.usage.prompt_tokens + result.usage.completion_tokens
);
}
#[tokio::test]
async fn test_chat_completions_response_has_finish_reason() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Test"}]
});
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let result: ChatCompletionResponse = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => return, };
assert!(!result.choices[0].finish_reason.is_empty());
assert!(
result.choices[0].finish_reason == "stop" || result.choices[0].finish_reason == "length"
);
}
#[tokio::test]
async fn test_chat_completions_response_timestamps() {
let app = create_test_app_shared();
let req_body = serde_json::json!({
"model": "default",
"messages": [{"role": "user", "content": "Test"}]
});
let before = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("test value should be present")
.as_secs() as i64;
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/v1/chat/completions")
.header("content-type", "application/json")
.body(Body::from(serde_json::to_string(&req_body).expect("JSON serialization failed")))
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let after = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("test value should be present")
.as_secs() as i64;
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let result: ChatCompletionResponse = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => return, };
assert!(result.created >= before - 1); assert!(result.created <= after + 1);
}
#[tokio::test]
async fn test_models_endpoint_returns_list_object() {
let app = create_test_app_shared();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/v1/models")
.body(Body::empty())
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let result: OpenAIModelsResponse = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => return, };
assert_eq!(result.object, "list");
}
#[tokio::test]
async fn test_models_endpoint_model_fields() {
let app = create_test_app_shared();
let response = app
.oneshot(
Request::builder()
.method("GET")
.uri("/v1/models")
.body(Body::empty())
.expect("test value should be present"),
)
.await
.expect("test value should be present");
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.await
.expect("test value should be present");
let result: OpenAIModelsResponse = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => return, };
for model in &result.data {
assert!(!model.id.is_empty());
assert_eq!(model.object, "model");
assert!(model.created > 0);
assert!(!model.owned_by.is_empty());
}
}