use serde_json::{Value, json};
use super::*;
fn request<'a>(model: &'a str, provider_options: &'a Value) -> Request<'a> {
Request {
model,
language: None,
prompt: None,
temperature: None,
format: None,
speaker_names: None,
speaker_references: None,
provider_options,
}
}
fn ruby_wav() -> Attachment {
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/ruby.wav");
Attachment::from_bytes(std::fs::read(path).unwrap(), "ruby.wav", None)
}
#[test]
fn gemini_passes_the_requested_format_and_temperature_through() {
let options = json!({});
let r = Request {
format: Some("application/json"),
temperature: Some(0.2),
..request("gemini-2.5-flash", &options)
};
let payload = gemini_payload(&ruby_wav(), &r).unwrap();
assert_eq!(
payload["generationConfig"],
json!({ "responseMimeType": "application/json", "temperature": 0.2 })
);
}
#[test]
fn gemini_refuses_an_attachment_that_is_not_audio() {
let notes = Attachment::from_bytes(b"not audio".to_vec(), "notes.txt", None);
let options = json!({});
let error = gemini_payload(¬es, &request("gemini-2.5-flash", &options)).unwrap_err();
assert!(
matches!(error, Error::UnsupportedAttachment(_)),
"{error:?}"
);
}
#[test]
fn gemini_leaves_the_text_nil_when_the_response_carries_no_candidate() {
assert_eq!(parse_gemini(&json!({}), "gemini-2.5-flash").text, None);
assert_eq!(
parse_gemini(&json!("not a hash"), "gemini-2.5-flash").text,
None
);
}
#[test]
fn gemini_leaves_the_text_nil_when_the_candidate_carries_no_text_parts() {
let inline = json!({ "candidates": [{ "content": { "parts": [{ "inlineData": {} }] } }] });
assert_eq!(parse_gemini(&inline, "gemini-2.5-flash").text, None);
let empty = json!({ "candidates": [{ "content": {} }] });
assert_eq!(parse_gemini(&empty, "gemini-2.5-flash").text, None);
}
#[test]
fn gemini_leaves_the_token_counts_nil_when_the_response_carries_no_usage() {
let data = json!({ "candidates": [{ "content": { "parts": [{ "text": "hi" }] } }] });
let t = parse_gemini(&data, "gemini-2.5-flash");
assert_eq!(t.tokens().input, None);
assert_eq!(t.tokens().output, None);
}
#[test]
fn interactions_preserve_speaker_only_annotations_without_fabricating_timing() {
let data = json!({ "status": "completed", "steps": [{ "type": "model_output", "content": [
{ "type": "text", "text": "Hello", "annotations": [
{ "type": "word_info", "text": "Hello", "speaker": "spk:0" }
] }
] }], "usage": { "total_input_tokens": 14, "total_output_tokens": 0 } });
let t = parse_interactions(&data, "gemini-3.5-transcribe").unwrap();
assert_eq!(t.text.as_deref(), Some("Hello"));
assert_eq!(
t.words,
Some(vec![json!({ "word": "Hello", "speaker": "spk:0" })])
);
assert_eq!((t.tokens().input, t.tokens().output), (Some(14), Some(0)));
assert_eq!(t.duration, None);
}
#[test]
fn interactions_reject_unknown_granularities() {
let error = Family::Interactions
.render_options(Some(&["segment"]), None, false)
.unwrap_err();
assert!(
matches!(&error, Error::Argument(m) if m.contains("must be word")),
"{error:?}"
);
}
#[test]
fn chat_completions_keeps_the_format_the_caller_asked_for_on_a_diarize_model() {
let options = json!({});
let r = Request {
format: Some("json"),
..request("gpt-4o-transcribe-diarize", &options)
};
let payload = multipart_payload(Family::OpenAI, &r).unwrap();
assert_eq!(payload["response_format"], json!("json"));
}
#[test]
fn chat_completions_maps_format_to_response_format() {
let options = json!({});
let r = Request {
format: Some("verbose_json"),
..request("whisper-1", &options)
};
let payload = multipart_payload(Family::OpenAI, &r).unwrap();
assert_eq!(payload["response_format"], json!("verbose_json"));
}
#[test]
fn chat_completions_maps_speaker_names_and_references_to_the_known_speaker_fields() {
let options = json!({});
let names = vec!["Alice".to_string()];
let references = vec![ruby_wav()];
let r = Request {
speaker_names: Some(&names),
speaker_references: Some(&references),
..request("gpt-4o-transcribe-diarize", &options)
};
let payload = multipart_payload(Family::OpenAI, &r).unwrap();
assert_eq!(payload["known_speaker_names"], json!(["Alice"]));
let refs = payload["known_speaker_references"].as_array().unwrap();
assert_eq!(refs.len(), 1);
assert!(
refs[0]
.as_str()
.unwrap()
.starts_with("data:audio/wav;base64,")
);
}
#[test]
fn chat_completions_merges_provider_options_over_rendered_defaults() {
let options =
json!({ "chunking_strategy": { "type": "server_vad" }, "response_format": "json" });
let payload = multipart_payload(
Family::OpenAI,
&request("gpt-4o-transcribe-diarize", &options),
)
.unwrap();
assert_eq!(
payload["chunking_strategy"],
json!({ "type": "server_vad" })
);
assert_eq!(payload["response_format"], json!("json"));
}