use std::time::Duration;
use open_ai_rust::{ChatMessage, Client, OpenAiError, OpenAiModel, PayLoadBuilder, RequestOptions};
use serde_json::json;
use wiremock::matchers::{header, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[test]
fn builder_default_header_invalid_name_silently_dropped() {
let c = Client::builder()
.api_key("k")
.default_header("not a header 🙃", "v")
.build()
.unwrap();
assert_eq!(c.api_key(), "k");
}
#[test]
fn builder_default_header_valid_is_attached_to_auth_headers() {
let c = Client::builder()
.api_key("k")
.default_header("x-custom", "1")
.build()
.unwrap();
assert!(!c.api_key().is_empty());
}
#[test]
fn builder_with_organization_and_project() {
let c = Client::builder()
.api_key("k")
.organization("org_x")
.project("proj_x")
.build()
.unwrap();
let _ = c; }
#[test]
fn builder_with_custom_http_client() {
let http = reqwest::Client::new();
let c = Client::builder()
.api_key("k")
.http_client(http)
.build()
.unwrap();
let _ = c.base_url();
}
#[test]
fn builder_with_timeout_then_no_http_set_path() {
let _ = Client::builder()
.api_key("k")
.timeout(Duration::from_secs(5))
.build()
.unwrap();
}
#[test]
fn from_env_reads_all_optional_vars_when_set() {
let prev_key = std::env::var("OPENAI_API_KEY").ok();
let prev_base = std::env::var("OPENAI_BASE_URL").ok();
let prev_org = std::env::var("OPENAI_ORG_ID").ok();
let prev_proj = std::env::var("OPENAI_PROJECT_ID").ok();
std::env::set_var("OPENAI_API_KEY", "k");
std::env::set_var("OPENAI_BASE_URL", "https://example.test/v1");
std::env::set_var("OPENAI_ORG_ID", "org_z");
std::env::set_var("OPENAI_PROJECT_ID", "proj_z");
let c = Client::from_env().unwrap();
assert_eq!(c.base_url(), "https://example.test/v1");
match prev_key {
Some(v) => std::env::set_var("OPENAI_API_KEY", v),
None => std::env::remove_var("OPENAI_API_KEY"),
}
match prev_base {
Some(v) => std::env::set_var("OPENAI_BASE_URL", v),
None => std::env::remove_var("OPENAI_BASE_URL"),
}
match prev_org {
Some(v) => std::env::set_var("OPENAI_ORG_ID", v),
None => std::env::remove_var("OPENAI_ORG_ID"),
}
match prev_proj {
Some(v) => std::env::set_var("OPENAI_PROJECT_ID", v),
None => std::env::remove_var("OPENAI_PROJECT_ID"),
}
}
#[test]
fn client_with_max_retries_and_with_header_shorthands() {
let c = Client::new("k")
.with_max_retries(7)
.with_header("x-trace", "abc");
let merged = c.with_options(RequestOptions::new().timeout(Duration::from_secs(2)));
let _ = merged;
}
#[test]
fn with_options_merge_keeps_earlier_values_for_unset_fields() {
let c = Client::new("k")
.with_timeout(Duration::from_secs(1))
.with_idempotency_key("evt-1")
.with_header("x-a", "1");
let _ = c;
}
#[tokio::test]
async fn with_options_idempotency_and_extra_header_both_present_on_request() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/chat/completions"))
.and(header("idempotency-key", "evt-1"))
.and(header("x-a", "1"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "x", "object": "chat.completion", "created": 1, "model": "m",
"choices": [{ "finish_reason": "stop", "index": 0, "message": { "role": "assistant", "content": "ok" } }],
"usage": { "prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2 },
})))
.mount(&server)
.await;
let c = Client::builder()
.api_key("k")
.base_url(server.uri())
.build_unchecked()
.with_idempotency_key("evt-1")
.with_header("x-a", "1");
let payload = PayLoadBuilder::new(OpenAiModel::GPT4oMini)
.messages(vec![ChatMessage::user("hi")])
.build();
c.chat().create(payload).await.unwrap();
}
#[tokio::test]
async fn org_and_project_headers_are_sent_when_set() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/chat/completions"))
.and(header("openai-organization", "org_z"))
.and(header("openai-project", "proj_z"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "x", "object": "chat.completion", "created": 1, "model": "m",
"choices": [{ "finish_reason": "stop", "index": 0, "message": { "role": "assistant", "content": "ok" } }],
"usage": { "prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2 },
})))
.mount(&server)
.await;
let c = Client::builder()
.api_key("k")
.base_url(server.uri())
.organization("org_z")
.project("proj_z")
.build_unchecked();
let payload = PayLoadBuilder::new(OpenAiModel::GPT4oMini)
.messages(vec![ChatMessage::user("hi")])
.build();
c.chat().create(payload).await.unwrap();
}
#[tokio::test]
async fn builder_default_header_attaches_value() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/chat/completions"))
.and(header("x-app", "my-cli"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "x", "object": "chat.completion", "created": 1, "model": "m",
"choices": [{ "finish_reason": "stop", "index": 0, "message": { "role": "assistant", "content": "ok" } }],
"usage": { "prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2 },
})))
.mount(&server)
.await;
let c = Client::builder()
.api_key("k")
.base_url(server.uri())
.default_header("x-app", "my-cli")
.build_unchecked();
let payload = PayLoadBuilder::new(OpenAiModel::GPT4oMini)
.messages(vec![ChatMessage::user("hi")])
.build();
c.chat().create(payload).await.unwrap();
}
#[tokio::test]
async fn azure_url_construction_includes_api_version_query() {
use wiremock::matchers::query_param;
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/openai/deployments/dep/chat/completions"))
.and(query_param("api-version", "2024-10-01-preview"))
.and(header("api-key", "ak"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "x", "object": "chat.completion", "created": 1, "model": "m",
"choices": [{ "finish_reason": "stop", "index": 0, "message": { "role": "assistant", "content": "ok" } }],
"usage": { "prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2 },
})))
.mount(&server)
.await;
let c = Client::azure("ak", server.uri(), "dep", "2024-10-01-preview");
let payload = PayLoadBuilder::new(OpenAiModel::GPT4o)
.messages(vec![ChatMessage::user("hi")])
.build();
c.chat().create(payload).await.unwrap();
}
#[tokio::test]
async fn azure_client_with_no_deployment_returns_config_error_on_build_url() {
use open_ai_rust::ClientBuilder;
let _ = ClientBuilder::default();
}
#[test]
fn client_api_key_accessor() {
let c = Client::new("the-key");
assert_eq!(c.api_key(), "the-key");
}
#[test]
fn client_debug_impl_redacts_nothing_but_does_not_panic() {
let c = Client::new("the-key");
let s = format!("{c:?}");
assert!(s.contains("Client"));
}
#[test]
fn error_from_openai_to_string_smoke() {
let s: String = OpenAiError::config("hi").into();
assert!(s.contains("hi"));
}