use openai_tools::batch::response::BatchStatus;
use openai_tools::common::role::Role;
use openai_tools::fine_tuning::response::FineTuningJobStatus;
use openai_tools::realtime::events::server::{ResponseStatus, ServerEvent};
use openai_tools::realtime::ItemStatus;
use openai_tools::videos::response::VideoStatus;
#[test]
fn role_supports_developer() {
assert_eq!(Role::Developer.as_str(), "developer");
assert_eq!(serde_json::to_string(&Role::Developer).unwrap(), "\"developer\"");
assert_eq!(serde_json::from_str::<Role>("\"developer\"").unwrap(), Role::Developer);
assert_eq!(Role::try_from("developer".to_string()).unwrap(), Role::Developer);
assert_eq!(format!("{}", Role::Developer), "developer");
}
#[test]
fn role_preserves_unknown_values() {
let parsed: Role = serde_json::from_str("\"moderator\"").expect("an unknown role must still deserialize");
assert_eq!(parsed, Role::Other("moderator".to_string()));
assert_eq!(parsed.as_str(), "moderator");
assert_eq!(serde_json::to_string(&parsed).unwrap(), "\"moderator\"");
}
#[test]
fn role_known_values_unchanged() {
for (role, expected) in
[(Role::System, "system"), (Role::User, "user"), (Role::Assistant, "assistant"), (Role::Function, "function"), (Role::Tool, "tool")]
{
assert_eq!(role.as_str(), expected);
assert_eq!(format!("{}", role), expected);
assert_eq!(serde_json::to_string(&role).unwrap(), format!("\"{}\"", expected));
assert_eq!(serde_json::from_str::<Role>(&format!("\"{}\"", expected)).unwrap(), role);
}
}
#[test]
fn role_try_from_rejects_unknown() {
assert!(Role::try_from("moderator".to_string()).is_err());
}
#[test]
fn batch_status_preserves_unknown_values() {
let parsed: BatchStatus = serde_json::from_str("\"expired_soon\"").expect("unknown batch status must deserialize");
assert_eq!(parsed, BatchStatus::Other("expired_soon".to_string()));
assert_eq!(serde_json::from_str::<BatchStatus>("\"completed\"").unwrap(), BatchStatus::Completed);
}
#[test]
fn fine_tuning_job_status_preserves_unknown_values() {
let parsed: FineTuningJobStatus = serde_json::from_str("\"paused\"").expect("unknown job status must deserialize");
assert_eq!(parsed, FineTuningJobStatus::Other("paused".to_string()));
assert_eq!(serde_json::from_str::<FineTuningJobStatus>("\"succeeded\"").unwrap(), FineTuningJobStatus::Succeeded);
}
#[test]
fn item_status_preserves_unknown_values() {
let parsed: ItemStatus = serde_json::from_str("\"interrupted\"").expect("unknown item status must deserialize");
assert_eq!(parsed, ItemStatus::Other("interrupted".to_string()));
assert_eq!(serde_json::from_str::<ItemStatus>("\"completed\"").unwrap(), ItemStatus::Completed);
}
#[test]
fn response_status_preserves_unknown_values() {
let parsed: ResponseStatus = serde_json::from_str("\"interrupted\"").expect("unknown response status must deserialize");
assert_eq!(parsed, ResponseStatus::Other("interrupted".to_string()));
assert_eq!(serde_json::from_str::<ResponseStatus>("\"failed\"").unwrap(), ResponseStatus::Failed);
}
#[test]
fn video_status_preserves_unknown_values() {
let parsed: VideoStatus = serde_json::from_str("\"cancelled\"").expect("unknown video status must deserialize");
assert_eq!(parsed, VideoStatus::Other("cancelled".to_string()));
}
#[test]
fn server_event_tolerates_unknown_event_types() {
let json = r#"{"type":"response.brand_new_thing","event_id":"evt_1","payload":{"a":1}}"#;
let parsed: ServerEvent = serde_json::from_str(json).expect("an unknown server event must deserialize");
assert!(matches!(parsed, ServerEvent::Unknown));
}
#[test]
fn server_event_known_types_still_parse() {
let json = r#"{"type":"conversation.item.deleted","event_id":"evt_1","item_id":"item_1"}"#;
let parsed: ServerEvent = serde_json::from_str(json).expect("a known server event must deserialize");
assert!(!matches!(parsed, ServerEvent::Unknown), "a known event must not fall through to Unknown");
assert_eq!(parsed.event_id(), Some("evt_1"));
}
#[test]
fn unknown_server_event_has_no_event_id() {
let json = r#"{"type":"response.brand_new_thing","event_id":"evt_1"}"#;
let parsed: ServerEvent = serde_json::from_str(json).unwrap();
assert_eq!(parsed.event_id(), None);
}
#[test]
fn designated_enums_are_non_exhaustive() {
const REQUIRED: &[(&str, &str)] = &[
("common/models.rs", "ChatModel"),
("common/models.rs", "EmbeddingModel"),
("common/models.rs", "RealtimeModel"),
("common/models.rs", "FineTuningModel"),
("images/request.rs", "ImageModel"),
("videos/request.rs", "VideoModel"),
("audio/request.rs", "TtsModel"),
("audio/request.rs", "SttModel"),
("moderations/request.rs", "ModerationModel"),
("realtime/audio.rs", "TranscriptionModel"),
("audio/request.rs", "Voice"),
("audio/request.rs", "AudioFormat"),
("audio/request.rs", "TranscriptionFormat"),
("audio/request.rs", "TimestampGranularity"),
("realtime/audio.rs", "Voice"),
("realtime/audio.rs", "AudioFormat"),
("realtime/audio.rs", "NoiseReductionType"),
("realtime/session.rs", "Modality"),
("realtime/vad.rs", "Eagerness"),
("images/request.rs", "ImageSize"),
("images/request.rs", "ImageQuality"),
("videos/request.rs", "VideoSize"),
("videos/request.rs", "VideoSeconds"),
("videos/request.rs", "VideoVariant"),
("responses/request.rs", "ReasoningEffort"),
("responses/request.rs", "ReasoningSummary"),
("responses/request.rs", "TextVerbosity"),
("responses/request.rs", "Include"),
("conversations/request.rs", "ConversationInclude"),
("files/request.rs", "FilePurpose"),
("batch/request.rs", "BatchEndpoint"),
("batch/request.rs", "CompletionWindow"),
("common/role.rs", "Role"),
("batch/response.rs", "BatchStatus"),
("fine_tuning/response.rs", "FineTuningJobStatus"),
("realtime/conversation.rs", "ItemStatus"),
("realtime/events/server.rs", "ResponseStatus"),
("realtime/events/server.rs", "ServerEvent"),
("videos/response.rs", "VideoStatus"),
];
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let mut missing = Vec::new();
for (file, name) in REQUIRED {
let src = std::fs::read_to_string(root.join(file)).unwrap_or_else(|e| panic!("cannot read {file}: {e}"));
let decl = format!("pub enum {name} {{");
let at = src.find(&decl).unwrap_or_else(|| panic!("{name} not found in {file}"));
let preceding = &src[..at];
if !preceding.trim_end().ends_with(']') && !preceding.contains("#[non_exhaustive]") {
missing.push(format!("{file}::{name}"));
continue;
}
let window_start = preceding.len().saturating_sub(400);
if !preceding[window_start..].contains("#[non_exhaustive]") {
missing.push(format!("{file}::{name}"));
}
}
assert!(missing.is_empty(), "these public enums must be marked #[non_exhaustive]:\n {}", missing.join("\n "));
}