use super::*;
use crate::api::llm::LlmRequest;
use serde_json::json;
fn req(content: serde_json::Value) -> LlmRequest {
LlmRequest {
headers: serde_json::Map::new(),
content,
}
}
#[test]
fn builtin_provider_surface_registry_keeps_request_priority() {
let surfaces: Vec<_> = BUILTIN_PROVIDER_SURFACES
.iter()
.map(|descriptor| descriptor.surface)
.collect();
assert_eq!(
surfaces,
vec![
ProviderSurface::OpenAIResponses,
ProviderSurface::AnthropicMessages,
ProviderSurface::OpenAIChat,
]
);
}
#[test]
fn detect_request_responses_by_input_or_instructions() {
assert_eq!(
detect_request_surface(&json!({"input": []})),
Some(ProviderSurface::OpenAIResponses)
);
assert_eq!(
detect_request_surface(&json!({"instructions": "x"})),
Some(ProviderSurface::OpenAIResponses)
);
}
#[test]
fn detect_request_anthropic_by_system() {
assert_eq!(
detect_request_surface(&json!({"system": "x", "messages": []})),
Some(ProviderSurface::AnthropicMessages)
);
}
#[test]
fn detect_request_chat_by_messages() {
assert_eq!(
detect_request_surface(&json!({"messages": []})),
Some(ProviderSurface::OpenAIChat)
);
}
#[test]
fn detect_request_priority_responses_then_anthropic_then_chat() {
assert_eq!(
detect_request_surface(&json!({"input": [], "system": "x", "messages": []})),
Some(ProviderSurface::OpenAIResponses)
);
assert_eq!(
detect_request_surface(&json!({"system": "x", "messages": []})),
Some(ProviderSurface::AnthropicMessages)
);
}
#[test]
fn detect_request_none_for_unknown_or_non_object() {
assert_eq!(detect_request_surface(&json!({})), None);
assert_eq!(detect_request_surface(&json!({"foo": 1})), None);
assert_eq!(detect_request_surface(&json!([1, 2, 3])), None);
assert_eq!(detect_request_surface(&json!("string")), None);
}
#[test]
fn detect_response_chat_by_choices() {
assert_eq!(
detect_response_surface(&json!({"choices": []})),
Some(ProviderSurface::OpenAIChat)
);
}
#[test]
fn detect_response_responses_by_output_or_output_text() {
assert_eq!(
detect_response_surface(&json!({"output": []})),
Some(ProviderSurface::OpenAIResponses)
);
assert_eq!(
detect_response_surface(&json!({"output_text": "hi"})),
Some(ProviderSurface::OpenAIResponses)
);
}
#[test]
fn detect_response_output_text_must_be_string() {
assert_eq!(detect_response_surface(&json!({"output_text": null})), None);
assert_eq!(
detect_response_surface(&json!({"output_text": {"nested": 1}})),
None
);
}
#[test]
fn detect_response_anthropic_by_type_message_and_content() {
assert_eq!(
detect_response_surface(&json!({"type": "message", "content": []})),
Some(ProviderSurface::AnthropicMessages)
);
}
#[test]
fn detect_response_none_for_empty_object_the_decode_trap() {
assert_eq!(detect_response_surface(&json!({})), None);
}
#[test]
fn detect_response_none_for_ambiguous_choices_and_output() {
assert_eq!(
detect_response_surface(&json!({"choices": [], "output": []})),
None
);
}
#[test]
fn detect_response_none_for_partial_anthropic() {
assert_eq!(detect_response_surface(&json!({"type": "message"})), None);
assert_eq!(detect_response_surface(&json!({"content": []})), None);
}
#[test]
fn detect_response_none_for_non_object() {
assert_eq!(detect_response_surface(&json!([1, 2])), None);
}
#[test]
fn normalize_response_decodes_detected_chat() {
let raw = json!({
"id": "r1",
"model": "gpt-4o",
"choices": [{
"message": {"role": "assistant", "content": "hello"},
"finish_reason": "stop"
}]
});
let decoded = normalize_response(&raw).expect("chat response decodes");
assert_eq!(decoded.response_text(), Some("hello"));
}
#[test]
fn normalize_response_decodes_detected_responses_output_text() {
let raw = json!({
"model": "gpt-4o",
"output": [],
"output_text": "hi there"
});
let decoded = normalize_response(&raw).expect("responses output_text decodes");
assert_eq!(decoded.response_text(), Some("hi there"));
}
#[test]
fn normalize_response_decodes_detected_anthropic() {
let raw = json!({
"type": "message",
"role": "assistant",
"model": "claude-3-5-sonnet",
"content": [{"type": "text", "text": "hi"}],
"stop_reason": "end_turn"
});
let decoded = normalize_response(&raw).expect("anthropic response decodes");
assert_eq!(decoded.response_text(), Some("hi"));
}
#[test]
fn normalize_response_none_for_unrecognized_shape() {
assert!(normalize_response(&json!({"foo": 1})).is_none());
assert!(normalize_response(&json!({})).is_none());
assert!(normalize_response(&json!({"choices": [], "output": []})).is_none());
}
#[test]
fn normalize_request_decodes_detected_chat() {
let request = req(json!({
"model": "gpt-4o",
"messages": [{"role": "user", "content": "hi"}]
}));
let decoded = normalize_request(&request).expect("chat request decodes");
assert!(!decoded.messages.is_empty());
}
#[test]
fn normalize_request_decodes_detected_anthropic() {
let request = req(json!({
"model": "claude-3-5-sonnet",
"system": "be terse",
"messages": [{"role": "user", "content": "hi"}]
}));
let decoded = normalize_request(&request).expect("anthropic request decodes");
assert!(!decoded.messages.is_empty());
}
#[test]
fn normalize_request_decodes_detected_responses() {
let request = req(json!({
"model": "gpt-4o",
"input": "Hello, world!"
}));
let decoded = normalize_request(&request).expect("responses request decodes");
assert!(!decoded.messages.is_empty());
}
#[test]
fn normalize_request_none_for_unknown_shape() {
assert!(normalize_request(&req(json!({"foo": 1}))).is_none());
}
#[test]
fn hint_none_matches_plain_detection() {
for body in [
json!({"input": []}),
json!({"instructions": "x"}),
json!({"system": "x", "messages": []}),
json!({"messages": []}),
json!({"input": [], "system": "x", "messages": []}),
json!({}),
json!({"foo": 1}),
json!([1, 2, 3]),
] {
assert_eq!(
detect_request_surface_with_hint(&body, None),
detect_request_surface(&body),
"hint=None must match plain detection for {body:?}",
);
}
}
#[test]
fn hint_anthropic_upgrades_system_less_messages() {
assert_eq!(
detect_request_surface(&json!({"messages": []})),
Some(ProviderSurface::OpenAIChat)
);
for hint in [Some("anthropic"), Some("anthropic.messages")] {
assert_eq!(
detect_request_surface_with_hint(&json!({"messages": []}), hint),
Some(ProviderSurface::AnthropicMessages),
"messages-only with hint {hint:?} should select Anthropic",
);
}
}
#[test]
fn hint_anthropic_descriptor_decodes_system_less_messages() {
let request = req(json!({
"model": "claude-3-5-sonnet",
"messages": [{"role": "user", "content": "hi"}],
"stop_sequences": ["END"]
}));
assert_eq!(
request_descriptor(&request.content, None).map(|descriptor| descriptor.surface),
Some(ProviderSurface::OpenAIChat)
);
let descriptor = request_descriptor(&request.content, Some("anthropic"))
.expect("anthropic hint should select descriptor");
assert_eq!(descriptor.surface, ProviderSurface::AnthropicMessages);
let decoded = (descriptor.decode_request)(&request).expect("anthropic request decodes");
let stop = decoded
.params
.as_ref()
.and_then(|params| params.stop.as_ref())
.expect("anthropic stop_sequences are normalized");
assert_eq!(stop, &vec!["END".to_string()]);
assert!(!decoded.extra.contains_key("stop_sequences"));
}
#[test]
fn normalize_request_with_hint_decodes_system_less_anthropic() {
let request = req(json!({
"model": "claude-3-5-sonnet",
"messages": [{"role": "user", "content": "hi"}],
"stop_sequences": ["END"]
}));
let decoded_without_hint =
normalize_request(&request).expect("messages-only request decodes as chat by default");
assert!(decoded_without_hint.extra.contains_key("stop_sequences"));
let decoded = normalize_request_with_hint(&request, Some("anthropic.messages"))
.expect("anthropic-hinted request decodes");
let stop = decoded
.params
.as_ref()
.and_then(|params| params.stop.as_ref())
.expect("anthropic stop_sequences are normalized");
assert_eq!(stop, &vec!["END".to_string()]);
assert!(!decoded.extra.contains_key("stop_sequences"));
}
#[test]
fn hint_other_or_unknown_provider_stays_chat() {
for hint in [
Some("openai"),
Some("openai.chat"),
Some("anthropic.count_tokens"),
Some("anthropic.preview"),
Some("passthrough"),
Some("gemini"),
None,
] {
assert_eq!(
detect_request_surface_with_hint(&json!({"messages": []}), hint),
Some(ProviderSurface::OpenAIChat),
"messages-only with hint {hint:?} should stay OpenAIChat",
);
}
}
#[test]
fn hint_never_overrides_strong_signals() {
assert_eq!(
detect_request_surface_with_hint(&json!({"input": [], "messages": []}), Some("anthropic")),
Some(ProviderSurface::OpenAIResponses)
);
assert_eq!(
detect_request_surface_with_hint(
&json!({"instructions": "x", "messages": []}),
Some("anthropic")
),
Some(ProviderSurface::OpenAIResponses)
);
assert_eq!(
detect_request_surface_with_hint(
&json!({"system": "x", "messages": []}),
Some("anthropic")
),
Some(ProviderSurface::AnthropicMessages)
);
}
#[test]
fn hint_does_not_classify_non_object_or_keyless() {
assert_eq!(
detect_request_surface_with_hint(&json!({}), Some("anthropic")),
None
);
assert_eq!(
detect_request_surface_with_hint(&json!([1, 2]), Some("anthropic")),
None
);
}