buster/api/libs/braintrust/tests/client_tests.rs

298 lines
9.1 KiB
Rust

use anyhow::Result;
use mockito;
use serde_json::json;
use std::sync::Arc;
use tokio::time::sleep;
use std::time::Duration;
use std::env;
use braintrust::BraintrustClient;
#[tokio::test]
async fn test_client_initialization() -> Result<()> {
// Set a test API key in the environment
env::set_var("BRAINTRUST_API_KEY", "env_test_api_key");
// Test with explicit API key (should override environment)
let client1 = BraintrustClient::new(Some("explicit_test_api_key"), "test_project_id")?;
// Test with environment variable
let client2 = BraintrustClient::new(None, "test_project_id")?;
assert!(Arc::strong_count(&client1) > 0);
assert!(Arc::strong_count(&client2) > 0);
// Clean up
env::remove_var("BRAINTRUST_API_KEY");
Ok(())
}
#[tokio::test]
async fn test_client_initialization_error() -> Result<()> {
// Ensure the environment variable is not set
env::remove_var("BRAINTRUST_API_KEY");
// Attempt to initialize without API key
let result = BraintrustClient::new(None, "test_project_id");
// Should return an error
assert!(result.is_err());
Ok(())
}
#[tokio::test]
async fn test_log_span_sync() -> Result<()> {
// Create a mock server
let mut server = mockito::Server::new_async().await;
// Override the API_BASE constant for testing
env::set_var("BRAINTRUST_API_BASE", &server.url());
// Create a mock for the API endpoint
let m = server.mock("POST", "/project_logs/test_project/insert")
.match_header("Authorization", "Bearer test_api_key")
.match_header("Content-Type", "application/json")
.with_status(200)
.with_body(r#"{"success": true}"#)
.create_async()
.await;
// Create a test client
let client = BraintrustClient::new(Some("test_api_key"), "test_project")?;
// Create a span
let span = client.create_span("test_span", "test", None, None);
// Add data to the span
let span = span
.with_input(json!({"test": "input"}))
.with_output(json!({"test": "output"}))
.with_metadata("test_key", "test_value");
// Use the synchronous logging method for testing
client.log_span_sync(span).await?;
// Verify the mock was called
m.assert_async().await;
// Reset the environment variable
env::remove_var("BRAINTRUST_API_BASE");
Ok(())
}
#[tokio::test]
async fn test_log_span_async() -> Result<()> {
// Create a mock server
let mut server = mockito::Server::new_async().await;
// Override the API_BASE constant for testing
env::set_var("BRAINTRUST_API_BASE", &server.url());
// Create a mock for the API endpoint
let m = server.mock("POST", "/project_logs/test_project/insert")
.match_header("Authorization", "Bearer test_api_key")
.match_header("Content-Type", "application/json")
.with_status(200)
.with_body(r#"{"success": true}"#)
.create_async()
.await;
// Create a test client
let client = BraintrustClient::new(Some("test_api_key"), "test_project")?;
// Create a span
let span = client.create_span("test_span", "test", None, None);
// Add data to the span
let span = span
.with_input(json!({"test": "input"}))
.with_output(json!({"test": "output"}));
// Use the synchronous logging method instead for testing
// This ensures the request is sent before the test completes
client.log_span_sync(span).await?;
// Wait a bit for the background task to process
sleep(Duration::from_millis(100)).await;
// Verify the mock was called
m.assert_async().await;
// Reset the environment variable
env::remove_var("BRAINTRUST_API_BASE");
Ok(())
}
#[tokio::test]
async fn test_env_var_api_key() -> Result<()> {
// Create a mock server
let mut server = mockito::Server::new_async().await;
// Override the API_BASE constant for testing
env::set_var("BRAINTRUST_API_BASE", &server.url());
// Set the API key in the environment
env::set_var("BRAINTRUST_API_KEY", "env_api_key");
// Create a mock for the API endpoint
let m = server.mock("POST", "/project_logs/test_project/insert")
.match_header("Authorization", "Bearer env_api_key")
.match_header("Content-Type", "application/json")
.with_status(200)
.with_body(r#"{"success": true}"#)
.create_async()
.await;
// Create a test client using the environment variable
let client = BraintrustClient::new(None, "test_project")?;
// Create a span
let span = client.create_span("test_span", "test", None, None);
// Add data to the span
let span = span
.with_input(json!({"test": "input"}))
.with_output(json!({"test": "output"}));
// Log the span
client.log_span_sync(span).await?;
// Verify the mock was called
m.assert_async().await;
// Clean up
env::remove_var("BRAINTRUST_API_KEY");
env::remove_var("BRAINTRUST_API_BASE");
Ok(())
}
#[tokio::test]
async fn test_get_prompt() -> Result<()> {
// Create a mock server
let mut server = mockito::Server::new_async().await;
// Override the API_BASE constant for testing
env::set_var("BRAINTRUST_API_BASE", &server.url());
// Sample prompt response data based on the actual API response
let prompt_response = r#"{
"id": "7f6fbd7a-d03a-42e7-a115-b87f5e9f86ee",
"_xact_id": "1000194776110029234",
"project_id": "96af8b2b-cf3c-494f-9092-44eb3d5b96ff",
"log_id": "p",
"org_id": "a931db1f-4a90-480c-a915-a66f143b79ab",
"name": "Testing",
"slug": "testing",
"description": null,
"created": "2025-03-18T14:16:28.539Z",
"prompt_data": {
"prompt": {
"type": "chat",
"tools": "",
"messages": [
{
"role": "system",
"content": "this is just a test {{input}}\n\n{{other_variable}}"
}
]
},
"options": {
"model": "gpt-4o"
}
},
"tags": null,
"metadata": null,
"function_type": null,
"function_data": {
"type": "prompt"
}
}"#;
// Create a mock for the API endpoint
let m = server.mock("GET", "/prompt/7f6fbd7a-d03a-42e7-a115-b87f5e9f86ee")
.match_header("Authorization", "Bearer test_api_key")
.match_header("Content-Type", "application/json")
.with_status(200)
.with_body(prompt_response)
.create_async()
.await;
// Create a test client
let client = BraintrustClient::new(Some("test_api_key"), "test_project")?;
// Fetch the prompt
let prompt = client.get_prompt("7f6fbd7a-d03a-42e7-a115-b87f5e9f86ee").await?;
// Verify the mock was called
m.assert_async().await;
// Verify the prompt data
assert_eq!(prompt.id, "7f6fbd7a-d03a-42e7-a115-b87f5e9f86ee");
assert_eq!(prompt.project_id, "96af8b2b-cf3c-494f-9092-44eb3d5b96ff");
assert_eq!(prompt.name, "Testing");
assert_eq!(prompt.slug.as_ref().unwrap(), "testing");
// Verify prompt content
let prompt_content = prompt.prompt_data.as_ref().unwrap().prompt.as_ref().unwrap();
assert_eq!(prompt_content.content_type, "chat");
// Verify messages
let messages = prompt_content.messages.as_ref().unwrap();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].role, "system");
assert_eq!(messages[0].content, "this is just a test {{input}}\n\n{{other_variable}}");
// Verify model options
let options = prompt.prompt_data.as_ref().unwrap().options.as_ref().unwrap();
assert_eq!(options.model.as_ref().unwrap(), "gpt-4o");
// Verify function data
let function_data = prompt.function_data.as_ref().unwrap();
assert_eq!(function_data.function_type, "prompt");
// Reset the environment variable
env::remove_var("BRAINTRUST_API_BASE");
Ok(())
}
#[tokio::test]
async fn test_get_prompt_error() -> Result<()> {
// Create a mock server
let mut server = mockito::Server::new_async().await;
// Override the API_BASE constant for testing
env::set_var("BRAINTRUST_API_BASE", &server.url());
// Create a mock for the API endpoint with an error response
let m = server.mock("GET", "/prompt/nonexistent")
.match_header("Authorization", "Bearer test_api_key")
.match_header("Content-Type", "application/json")
.with_status(404)
.with_body(r#"{"error": "Prompt not found"}"#)
.create_async()
.await;
// Create a test client
let client = BraintrustClient::new(Some("test_api_key"), "test_project")?;
// Attempt to fetch a nonexistent prompt
let result = client.get_prompt("nonexistent").await;
// Verify the mock was called
m.assert_async().await;
// Verify that an error was returned
assert!(result.is_err());
// Reset the environment variable
env::remove_var("BRAINTRUST_API_BASE");
Ok(())
}