use chrono::{DateTime, TimeZone, Utc};
use proptest::prelude::*;
use proptest::test_runner::TestCaseError;
use super::content::{
Annotation, CodeExecutionLanguage, Content, FileSearchResultItem, GoogleMapsResultItem,
GoogleSearchResultItem, Place, Resolution, ReviewSnippet, UrlContextResultItem,
};
use super::environment::{
AllowlistEntry, EnvironmentSource, EnvironmentSpec, NetworkConfig, RemoteEnvironment,
SourceType,
};
use super::request::{
AgentConfig, DeepResearchConfig, DynamicConfig, GenerationConfig, ImageAspectRatio,
ImageConfig, ImageSize, InteractionInput, Role, ServiceTier, SpeechConfig, ThinkingLevel,
ThinkingSummaries, TurnContent, VideoConfig, VideoTask, Visualization,
};
use super::response::{
GroundingToolCount, InteractionResponse, InteractionStatus, ModalityTokens,
OwnedFunctionCallInfo, UsageMetadata,
};
use super::response_format::{ResponseDelivery, ResponseFormat, ResponseFormatSpec};
use super::steps::{FunctionResultPayload, Step, StepDelta, StepError};
use super::tools::{
AllowedTools, ExaAiSearchConfig, FunctionCallingMode, FunctionParameters, HybridSearchConfig,
ParallelAiSearchConfig, RagFilter, RagRanking, RagResource, RagRetrievalConfig, RagStoreConfig,
RetrievalType, SearchType, Tool, ToolChoice, VertexAiSearchConfig,
};
use super::webhooks::{RevocationBehavior, WebhookConfig, WebhookEvent, WebhookState};
use super::wire_streaming::StreamChunk;
fn assert_value_roundtrip<T>(value: &T) -> Result<(), TestCaseError>
where
T: serde::Serialize + serde::de::DeserializeOwned,
{
let json = serde_json::to_value(value).expect("Serialization should succeed");
let restored: T = serde_json::from_value(json.clone()).expect("Deserialization should succeed");
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
Ok(())
}
fn arb_clean_float() -> impl Strategy<Value = serde_json::Value> {
(
any::<i32>(),
prop_oneof![Just(1i64), Just(10), Just(100), Just(1000)],
)
.prop_filter_map("must be representable", |(n, divisor)| {
let f = (n as f64) / (divisor as f64);
serde_json::Number::from_f64(f).map(serde_json::Value::Number)
})
}
fn arb_clean_f64() -> impl Strategy<Value = f64> {
(-180_000i32..180_000).prop_map(|n| (n as f64) / 1000.0)
}
fn arb_json_value() -> impl Strategy<Value = serde_json::Value> {
prop_oneof![
Just(serde_json::Value::Null),
any::<bool>().prop_map(serde_json::Value::Bool),
any::<i64>().prop_map(|n| serde_json::Value::Number(n.into())),
arb_clean_float(),
".*".prop_map(serde_json::Value::String),
prop::collection::vec(
prop_oneof![
Just(serde_json::Value::Null),
any::<bool>().prop_map(serde_json::Value::Bool),
any::<i64>().prop_map(|n| serde_json::Value::Number(n.into())),
".*".prop_map(serde_json::Value::String),
],
0..5
)
.prop_map(serde_json::Value::Array),
prop::collection::hash_map("[a-zA-Z_][a-zA-Z0-9_]*", ".*", 0..5).prop_map(|m| {
serde_json::Value::Object(
m.into_iter()
.map(|(k, v)| (k, serde_json::Value::String(v)))
.collect(),
)
}),
]
}
fn arb_non_string_json_value() -> impl Strategy<Value = serde_json::Value> {
arb_json_value().prop_filter("Json payload must not be a bare string", |v| !v.is_string())
}
fn arb_identifier() -> impl Strategy<Value = String> {
"[a-zA-Z_][a-zA-Z0-9_]{0,30}"
}
fn arb_text() -> impl Strategy<Value = String> {
".{0,500}"
}
fn arb_unknown_type() -> impl Strategy<Value = String> {
"zz_[a-z0-9_]{1,12}"
}
fn arb_unknown_object() -> impl Strategy<Value = serde_json::Value> {
prop::collection::hash_map("[a-z_][a-z0-9_]{0,10}", ".{0,20}", 0..3).prop_map(|m| {
serde_json::Value::Object(
m.into_iter()
.map(|(k, v)| (k, serde_json::Value::String(v)))
.collect(),
)
})
}
fn arb_datetime() -> impl Strategy<Value = DateTime<Utc>> {
(0i64..315_360_000).prop_map(|offset_secs| {
Utc.timestamp_opt(1_577_836_800 + offset_secs, 0)
.single()
.expect("valid timestamp")
})
}
fn arb_resolution() -> impl Strategy<Value = Resolution> {
prop_oneof![
Just(Resolution::Low),
Just(Resolution::Medium),
Just(Resolution::High),
Just(Resolution::UltraHigh),
arb_unknown_type().prop_map(|s| Resolution::Unknown {
resolution_type: s.clone(),
data: serde_json::Value::String(s),
}),
]
}
fn arb_review_snippet() -> impl Strategy<Value = ReviewSnippet> {
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_identifier()),
)
.prop_map(|(title, url, review_id)| ReviewSnippet {
title,
url,
review_id,
})
}
fn arb_annotation() -> impl Strategy<Value = Annotation> {
fn span() -> (std::ops::Range<usize>, std::ops::Range<usize>) {
(0usize..1000, 0usize..1000)
}
prop_oneof![
(
span(),
proptest::option::of(arb_text()),
proptest::option::of(arb_text())
)
.prop_map(|((start, len), url, title)| Annotation::UrlCitation {
url,
title,
start_index: start,
end_index: start.saturating_add(len),
}),
(
span(),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_unknown_object()),
proptest::option::of(any::<u32>()),
proptest::option::of(arb_identifier()),
)
.prop_map(
|(
(start, len),
document_uri,
file_name,
source,
custom_metadata,
page_number,
media_id,
)| {
Annotation::FileCitation {
document_uri,
file_name,
source,
custom_metadata,
page_number,
media_id,
start_index: start,
end_index: start.saturating_add(len),
}
}
),
(
span(),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
prop::collection::vec(arb_review_snippet(), 0..2),
)
.prop_map(|((start, len), place_id, name, url, review_snippets)| {
Annotation::PlaceCitation {
place_id,
name,
url,
review_snippets,
start_index: start,
end_index: start.saturating_add(len),
}
}),
(arb_unknown_type(), arb_unknown_object()).prop_map(|(annotation_type, data)| {
Annotation::Unknown {
annotation_type,
data,
}
}),
]
}
fn arb_google_search_result_item() -> impl Strategy<Value = GoogleSearchResultItem> {
(
arb_text(),
arb_text(),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
)
.prop_map(|(title, url, rendered_content, search_suggestions)| {
GoogleSearchResultItem {
title,
url,
rendered_content,
search_suggestions,
}
})
}
fn arb_place() -> impl Strategy<Value = Place> {
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(prop::collection::vec(arb_review_snippet(), 0..2)),
)
.prop_map(
|(name, formatted_address, place_id, url, review_snippets)| Place {
name,
formatted_address,
place_id,
url,
lat: None,
lng: None,
types: None,
rating: None,
user_ratings_total: None,
website: None,
phone_number: None,
review_snippets,
extra: serde_json::Map::new(),
},
)
}
fn arb_google_maps_result_item() -> impl Strategy<Value = GoogleMapsResultItem> {
(
proptest::option::of(proptest::collection::vec(arb_place(), 0..3)),
proptest::option::of(arb_text()),
)
.prop_map(|(places, widget_context_token)| GoogleMapsResultItem {
places,
widget_context_token,
})
}
fn arb_file_search_result_item() -> impl Strategy<Value = FileSearchResultItem> {
(arb_text(), arb_text(), arb_text()).prop_map(|(title, text, store)| FileSearchResultItem {
title,
text,
store,
})
}
fn arb_url_context_result_item() -> impl Strategy<Value = UrlContextResultItem> {
(
arb_text(),
prop_oneof![
Just("success"),
Just("error"),
Just("paywall"),
Just("unsafe")
],
)
.prop_map(|(url, status)| UrlContextResultItem::new(url, status))
}
fn arb_interaction_status() -> impl Strategy<Value = InteractionStatus> {
prop_oneof![
Just(InteractionStatus::Completed),
Just(InteractionStatus::InProgress),
Just(InteractionStatus::RequiresAction),
Just(InteractionStatus::Failed),
Just(InteractionStatus::Cancelled),
Just(InteractionStatus::Incomplete),
Just(InteractionStatus::BudgetExceeded),
arb_unknown_type().prop_map(|status_type| InteractionStatus::Unknown {
status_type: status_type.clone(),
data: serde_json::Value::String(status_type),
}),
]
}
fn arb_function_calling_mode() -> impl Strategy<Value = FunctionCallingMode> {
prop_oneof![
Just(FunctionCallingMode::Auto),
Just(FunctionCallingMode::Any),
Just(FunctionCallingMode::None),
Just(FunctionCallingMode::Validated),
arb_unknown_type().prop_map(|mode_type| FunctionCallingMode::Unknown {
mode_type: mode_type.clone(),
data: serde_json::Value::String(mode_type),
}),
]
}
fn arb_thinking_level() -> impl Strategy<Value = ThinkingLevel> {
prop_oneof![
Just(ThinkingLevel::Minimal),
Just(ThinkingLevel::Low),
Just(ThinkingLevel::Medium),
Just(ThinkingLevel::High),
arb_unknown_type().prop_map(|level_type| ThinkingLevel::Unknown {
level_type: level_type.clone(),
data: serde_json::Value::String(level_type),
}),
]
}
fn arb_thinking_summaries() -> impl Strategy<Value = ThinkingSummaries> {
prop_oneof![
Just(ThinkingSummaries::Auto),
Just(ThinkingSummaries::None),
arb_unknown_type().prop_map(|summaries_type| ThinkingSummaries::Unknown {
summaries_type: summaries_type.clone(),
data: serde_json::Value::String(summaries_type),
}),
]
}
fn arb_service_tier() -> impl Strategy<Value = ServiceTier> {
prop_oneof![
Just(ServiceTier::Flex),
Just(ServiceTier::Standard),
Just(ServiceTier::Priority),
arb_unknown_type().prop_map(|tier_type| ServiceTier::Unknown {
tier_type: tier_type.clone(),
data: serde_json::Value::String(tier_type),
}),
]
}
fn arb_search_type() -> impl Strategy<Value = SearchType> {
prop_oneof![
Just(SearchType::WebSearch),
Just(SearchType::ImageSearch),
Just(SearchType::EnterpriseWebSearch),
arb_unknown_type().prop_map(|search_type| SearchType::Unknown {
search_type: search_type.clone(),
data: serde_json::Value::String(search_type),
}),
]
}
fn arb_code_execution_language() -> impl Strategy<Value = CodeExecutionLanguage> {
prop_oneof![
Just(CodeExecutionLanguage::Python),
arb_unknown_type().prop_map(|language_type| CodeExecutionLanguage::Unknown {
language_type: language_type.clone(),
data: serde_json::Value::String(language_type),
}),
]
}
fn arb_role() -> impl Strategy<Value = Role> {
prop_oneof![
Just(Role::User),
Just(Role::Model),
arb_unknown_type().prop_map(|role_type| Role::Unknown {
data: serde_json::Value::String(role_type.clone()),
role_type,
}),
]
}
fn arb_image_aspect_ratio() -> impl Strategy<Value = ImageAspectRatio> {
prop_oneof![
Just(ImageAspectRatio::Square),
Just(ImageAspectRatio::Portrait2x3),
Just(ImageAspectRatio::Landscape3x2),
Just(ImageAspectRatio::Portrait3x4),
Just(ImageAspectRatio::Landscape4x3),
Just(ImageAspectRatio::Portrait4x5),
Just(ImageAspectRatio::Landscape5x4),
Just(ImageAspectRatio::Portrait9x16),
Just(ImageAspectRatio::Widescreen16x9),
Just(ImageAspectRatio::Ultrawide21x9),
Just(ImageAspectRatio::Tall1x8),
Just(ImageAspectRatio::Wide8x1),
Just(ImageAspectRatio::Tall1x4),
Just(ImageAspectRatio::Wide4x1),
arb_unknown_type().prop_map(|ratio_type| ImageAspectRatio::Unknown {
ratio_type: ratio_type.clone(),
data: serde_json::Value::String(ratio_type),
}),
]
}
fn arb_image_size() -> impl Strategy<Value = ImageSize> {
prop_oneof![
Just(ImageSize::Sd512),
Just(ImageSize::Hd1k),
Just(ImageSize::Hd2k),
Just(ImageSize::Uhd4k),
arb_unknown_type().prop_map(|size_type| ImageSize::Unknown {
size_type: size_type.clone(),
data: serde_json::Value::String(size_type),
}),
]
}
fn arb_image_config() -> impl Strategy<Value = ImageConfig> {
(
proptest::option::of(arb_image_aspect_ratio()),
proptest::option::of(arb_image_size()),
)
.prop_map(|(aspect_ratio, image_size)| ImageConfig {
aspect_ratio,
image_size,
})
}
fn arb_known_content() -> impl Strategy<Value = Content> {
prop_oneof![
(
proptest::option::of(arb_text()),
proptest::option::of(proptest::collection::vec(arb_annotation(), 0..3))
)
.prop_map(|(text, annotations)| Content::Text { text, annotations }),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_resolution())
)
.prop_map(|(data, uri, mime_type, resolution)| Content::Image {
data,
uri,
mime_type,
resolution
}),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(any::<u32>()),
proptest::option::of(1u32..8),
)
.prop_map(
|(data, uri, mime_type, sample_rate, channels)| Content::Audio {
data,
uri,
mime_type,
sample_rate,
channels,
}
),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_resolution())
)
.prop_map(|(data, uri, mime_type, resolution)| Content::Video {
data,
uri,
mime_type,
resolution
}),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text())
)
.prop_map(|(data, uri, mime_type)| Content::Document {
data,
uri,
mime_type
}),
]
}
#[cfg(feature = "strict-unknown")]
fn arb_content() -> impl Strategy<Value = Content> {
arb_known_content()
}
#[cfg(not(feature = "strict-unknown"))]
fn arb_content() -> impl Strategy<Value = Content> {
prop_oneof![
arb_known_content(),
(arb_unknown_type(), arb_unknown_object())
.prop_map(|(content_type, data)| Content::Unknown { content_type, data }),
]
}
fn arb_function_result_payload() -> impl Strategy<Value = FunctionResultPayload> {
prop_oneof![
arb_text().prop_map(FunctionResultPayload::Text),
arb_non_string_json_value().prop_map(FunctionResultPayload::Json),
prop::collection::vec(arb_content(), 1..3).prop_map(FunctionResultPayload::Contents),
]
}
fn arb_step_error() -> impl Strategy<Value = StepError> {
(
proptest::option::of(any::<i64>()),
proptest::option::of(arb_text()),
proptest::option::of(prop::collection::vec(arb_json_value(), 0..2)),
)
.prop_map(|(code, message, details)| StepError {
code,
message,
details,
})
}
fn arb_known_step() -> impl Strategy<Value = Step> {
prop_oneof![
prop::collection::vec(arb_content(), 0..3).prop_map(|content| Step::UserInput { content }),
(
prop::collection::vec(arb_content(), 0..3),
proptest::option::of(arb_step_error()),
)
.prop_map(|(content, error)| Step::ModelOutput { content, error }),
(
proptest::option::of(arb_text()),
prop::collection::vec(arb_content(), 0..2),
)
.prop_map(|(signature, summary)| Step::Thought { signature, summary }),
(
arb_identifier(),
arb_identifier(),
arb_json_value(),
proptest::option::of(arb_text()),
)
.prop_map(|(id, name, arguments, signature)| Step::FunctionCall {
id,
name,
arguments,
signature,
}),
(
arb_identifier(),
proptest::option::of(arb_identifier()),
arb_function_result_payload(),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(|(call_id, name, result, is_error, signature)| {
Step::FunctionResult {
call_id,
name,
result,
is_error,
signature,
}
}),
(
arb_identifier(),
arb_code_execution_language(),
arb_text(),
proptest::option::of(arb_text()),
)
.prop_map(|(id, language, code, signature)| Step::CodeExecutionCall {
id,
language,
code,
signature,
}),
(
arb_identifier(),
arb_text(),
proptest::bool::ANY,
proptest::option::of(arb_text()),
)
.prop_map(
|(call_id, result, is_error, signature)| Step::CodeExecutionResult {
call_id,
result,
is_error,
signature,
}
),
(
arb_identifier(),
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(id, urls, signature)| Step::UrlContextCall {
id,
urls,
signature
}),
(
arb_identifier(),
prop::collection::vec(arb_url_context_result_item(), 0..3),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(
|(call_id, result, is_error, signature)| Step::UrlContextResult {
call_id,
result,
is_error,
signature,
}
),
(
arb_identifier(),
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_search_type()),
proptest::option::of(arb_text()),
)
.prop_map(
|(id, queries, search_type, signature)| Step::GoogleSearchCall {
id,
queries,
search_type,
signature,
}
),
(
arb_identifier(),
prop::collection::vec(arb_google_search_result_item(), 0..3),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(
|(call_id, result, is_error, signature)| Step::GoogleSearchResult {
call_id,
result,
is_error,
signature,
}
),
(
arb_identifier(),
arb_identifier(),
arb_identifier(),
arb_json_value(),
)
.prop_map(
|(id, name, server_name, arguments)| Step::McpServerToolCall {
id,
name,
server_name,
arguments,
}
),
(
arb_identifier(),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_identifier()),
arb_function_result_payload(),
)
.prop_map(
|(call_id, name, server_name, result)| Step::McpServerToolResult {
call_id,
name,
server_name,
result,
}
),
(arb_identifier(), proptest::option::of(arb_text()))
.prop_map(|(id, signature)| { Step::FileSearchCall { id, signature } }),
(
arb_identifier(),
prop::collection::vec(arb_file_search_result_item(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(call_id, result, signature)| Step::FileSearchResult {
call_id,
result,
signature,
}),
(
arb_identifier(),
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(id, queries, signature)| Step::GoogleMapsCall {
id,
queries,
signature
}),
(
arb_identifier(),
prop::collection::vec(arb_google_maps_result_item(), 0..2),
proptest::option::of(arb_text()),
)
.prop_map(|(call_id, result, signature)| Step::GoogleMapsResult {
call_id,
result,
signature,
}),
]
}
#[cfg(feature = "strict-unknown")]
fn arb_step() -> impl Strategy<Value = Step> {
arb_known_step()
}
#[cfg(not(feature = "strict-unknown"))]
fn arb_step() -> impl Strategy<Value = Step> {
prop_oneof![
arb_known_step(),
(arb_unknown_type(), arb_unknown_object())
.prop_map(|(step_type, data)| Step::Unknown { step_type, data }),
]
}
fn arb_step_delta() -> impl Strategy<Value = StepDelta> {
prop_oneof![
arb_text().prop_map(|text| StepDelta::Text { text }),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_resolution()),
)
.prop_map(|(data, uri, mime_type, resolution)| StepDelta::Image {
data,
uri,
mime_type,
resolution,
}),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
proptest::option::of(1u32..8),
)
.prop_map(|(data, uri, mime_type, rate, sample_rate, channels)| {
StepDelta::Audio {
data,
uri,
mime_type,
rate,
sample_rate,
channels,
}
}),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_resolution()),
)
.prop_map(|(data, uri, mime_type, resolution)| StepDelta::Video {
data,
uri,
mime_type,
resolution,
}),
(
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
)
.prop_map(|(data, uri, mime_type)| StepDelta::Document {
data,
uri,
mime_type
}),
proptest::option::of(arb_content())
.prop_map(|content| StepDelta::ThoughtSummary { content }),
proptest::option::of(arb_text())
.prop_map(|signature| StepDelta::ThoughtSignature { signature }),
prop::collection::vec(arb_annotation(), 0..3)
.prop_map(|annotations| StepDelta::TextAnnotation { annotations }),
arb_text().prop_map(|arguments| StepDelta::ArgumentsDelta { arguments }),
(
arb_identifier(),
proptest::option::of(arb_identifier()),
arb_function_result_payload(),
proptest::option::of(proptest::bool::ANY),
)
.prop_map(
|(call_id, name, result, is_error)| StepDelta::FunctionResult {
call_id,
name,
result,
is_error,
}
),
(
arb_code_execution_language(),
proptest::option::of(arb_text()),
proptest::option::of(arb_text()),
)
.prop_map(|(language, code, signature)| StepDelta::CodeExecutionCall {
language: Some(language),
code,
signature,
}),
(
arb_text(),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(
|(result, is_error, signature)| StepDelta::CodeExecutionResult {
result,
is_error,
signature,
}
),
(
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(urls, signature)| StepDelta::UrlContextCall { urls, signature }),
(
prop::collection::vec(arb_url_context_result_item(), 0..3),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(
|(result, is_error, signature)| StepDelta::UrlContextResult {
result,
is_error,
signature,
}
),
(
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(queries, signature)| StepDelta::GoogleSearchCall { queries, signature }),
(
prop::collection::vec(arb_google_search_result_item(), 0..3),
proptest::option::of(proptest::bool::ANY),
proptest::option::of(arb_text()),
)
.prop_map(
|(result, is_error, signature)| StepDelta::GoogleSearchResult {
result,
is_error,
signature,
}
),
(arb_identifier(), arb_identifier(), arb_json_value()).prop_map(
|(name, server_name, arguments)| StepDelta::McpServerToolCall {
name,
server_name,
arguments,
}
),
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_identifier()),
arb_function_result_payload(),
)
.prop_map(
|(name, server_name, result)| StepDelta::McpServerToolResult {
name,
server_name,
result,
}
),
proptest::option::of(arb_text())
.prop_map(|signature| StepDelta::FileSearchCall { signature }),
(
prop::collection::vec(arb_file_search_result_item(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(result, signature)| StepDelta::FileSearchResult { result, signature }),
(
prop::collection::vec(arb_text(), 0..3),
proptest::option::of(arb_text()),
)
.prop_map(|(queries, signature)| StepDelta::GoogleMapsCall { queries, signature }),
(
prop::collection::vec(arb_google_maps_result_item(), 0..2),
proptest::option::of(arb_text()),
)
.prop_map(|(result, signature)| StepDelta::GoogleMapsResult { result, signature }),
(arb_unknown_type(), arb_unknown_object())
.prop_map(|(delta_type, data)| StepDelta::Unknown { delta_type, data }),
]
}
fn arb_interaction_input() -> impl Strategy<Value = InteractionInput> {
prop_oneof![
arb_text().prop_map(InteractionInput::Text),
prop::collection::vec(arb_content(), 1..3).prop_map(InteractionInput::Content),
prop::collection::vec(arb_step(), 0..3).prop_map(InteractionInput::Steps),
]
}
fn arb_turn_content() -> impl Strategy<Value = TurnContent> {
prop_oneof![
arb_text().prop_map(TurnContent::Text),
prop::collection::vec(arb_content(), 0..3).prop_map(TurnContent::Parts),
]
}
fn arb_allowed_tools() -> impl Strategy<Value = AllowedTools> {
(
proptest::option::of(arb_function_calling_mode()),
prop::collection::vec(arb_identifier(), 0..3),
)
.prop_map(|(mode, tools)| AllowedTools { mode, tools })
}
fn arb_tool_choice() -> impl Strategy<Value = ToolChoice> {
prop_oneof![
arb_function_calling_mode().prop_map(ToolChoice::Mode),
arb_allowed_tools().prop_map(ToolChoice::AllowedTools),
arb_unknown_type().prop_map(|key| {
let data = serde_json::json!({ key.clone(): "future_config" });
ToolChoice::Unknown {
choice_type: key,
data,
}
}),
]
}
fn arb_speech_config() -> impl Strategy<Value = SpeechConfig> {
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_identifier()),
)
.prop_map(|(voice, language, speaker)| SpeechConfig {
voice,
language,
speaker,
})
}
fn arb_generation_config() -> impl Strategy<Value = GenerationConfig> {
let part1 = (
proptest::option::of(arb_clean_float().prop_map(|v| v.as_f64().unwrap() as f32)),
proptest::option::of(1..10000i32),
proptest::option::of(arb_clean_float().prop_map(|v| v.as_f64().unwrap() as f32)),
proptest::option::of(arb_thinking_level()),
proptest::option::of(1..1000i64),
proptest::option::of(proptest::collection::vec(arb_identifier(), 0..3)),
proptest::option::of(arb_thinking_summaries()),
);
let part2 = (
proptest::option::of(arb_tool_choice()),
proptest::option::of(arb_clean_float().prop_map(|v| v.as_f64().unwrap() as f32)),
proptest::option::of(arb_clean_float().prop_map(|v| v.as_f64().unwrap() as f32)),
proptest::option::of(proptest::collection::vec(arb_speech_config(), 1..3)),
proptest::option::of(arb_image_config()),
proptest::option::of(arb_video_config()),
);
(part1, part2).prop_map(
|(
(
temperature,
max_output_tokens,
top_p,
thinking_level,
seed,
stop_sequences,
thinking_summaries,
),
(
tool_choice,
presence_penalty,
frequency_penalty,
speech_config,
image_config,
video_config,
),
)| {
GenerationConfig {
temperature,
max_output_tokens,
top_p,
thinking_level,
seed,
stop_sequences,
thinking_summaries,
tool_choice,
presence_penalty,
frequency_penalty,
speech_config,
image_config,
video_config,
}
},
)
}
fn arb_video_task() -> impl Strategy<Value = VideoTask> {
prop_oneof![
Just(VideoTask::TextToVideo),
Just(VideoTask::ImageToVideo),
Just(VideoTask::ReferenceToVideo),
Just(VideoTask::Edit),
Just(VideoTask::Extend),
arb_unknown_type().prop_map(|task_type| VideoTask::Unknown {
data: serde_json::Value::String(task_type.clone()),
task_type,
}),
]
}
fn arb_video_config() -> impl Strategy<Value = VideoConfig> {
proptest::option::of(arb_video_task()).prop_map(|task| VideoConfig { task })
}
fn arb_agent_config() -> impl Strategy<Value = AgentConfig> {
prop_oneof![
proptest::option::of(arb_thinking_summaries()).prop_map(|thinking_summaries| {
let mut config = DeepResearchConfig::new();
if let Some(ts) = thinking_summaries {
config = config.with_thinking_summaries(ts);
}
config.into()
}),
Just(DynamicConfig::new().into()),
arb_identifier().prop_map(|config_type| {
AgentConfig::from_value(serde_json::json!({
"type": config_type,
"customField": 42
}))
}),
]
}
fn arb_modality_tokens() -> impl Strategy<Value = ModalityTokens> {
(
prop_oneof![
Just("TEXT".to_string()),
Just("IMAGE".to_string()),
Just("AUDIO".to_string()),
Just("VIDEO".to_string()),
arb_identifier(), ],
any::<u32>(),
)
.prop_map(|(modality, tokens)| ModalityTokens { modality, tokens })
}
fn arb_modality_tokens_vec() -> impl Strategy<Value = Option<Vec<ModalityTokens>>> {
proptest::option::of(prop::collection::vec(arb_modality_tokens(), 0..4))
}
fn arb_grounding_tool_count() -> impl Strategy<Value = GroundingToolCount> {
(
proptest::option::of(prop_oneof![
Just("google_search".to_string()),
Just("google_maps".to_string()),
Just("retrieval".to_string()),
arb_identifier(),
]),
proptest::option::of(any::<u32>()),
)
.prop_map(|(tool_type, count)| GroundingToolCount { tool_type, count })
}
fn arb_usage_metadata() -> impl Strategy<Value = UsageMetadata> {
(
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
proptest::option::of(any::<u32>()),
arb_modality_tokens_vec(),
arb_modality_tokens_vec(),
arb_modality_tokens_vec(),
arb_modality_tokens_vec(),
proptest::option::of(prop::collection::vec(arb_grounding_tool_count(), 0..3)),
)
.prop_map(
|(
total_input_tokens,
total_output_tokens,
total_tokens,
total_cached_tokens,
total_thought_tokens,
total_tool_use_tokens,
input_tokens_by_modality,
output_tokens_by_modality,
cached_tokens_by_modality,
tool_use_tokens_by_modality,
grounding_tool_count,
)| {
UsageMetadata {
total_input_tokens,
total_output_tokens,
total_tokens,
total_cached_tokens,
total_thought_tokens,
total_tool_use_tokens,
input_tokens_by_modality,
output_tokens_by_modality,
cached_tokens_by_modality,
tool_use_tokens_by_modality,
grounding_tool_count,
}
},
)
}
fn arb_owned_function_call_info() -> impl Strategy<Value = OwnedFunctionCallInfo> {
(arb_identifier(), arb_identifier(), arb_json_value())
.prop_map(|(id, name, args)| OwnedFunctionCallInfo { id, name, args })
}
fn arb_function_parameters() -> impl Strategy<Value = FunctionParameters> {
(
Just("object".to_string()),
arb_json_value(),
prop::collection::vec(arb_identifier(), 0..3),
)
.prop_map(|(type_, properties, required)| {
FunctionParameters::new(type_, properties, required)
})
}
fn arb_tool() -> impl Strategy<Value = Tool> {
prop_oneof![
(arb_identifier(), arb_text(), arb_function_parameters()).prop_map(
|(name, description, parameters)| Tool::Function {
name,
description,
parameters
}
),
proptest::option::of(proptest::collection::vec(arb_search_type(), 1..3))
.prop_map(|search_types| Tool::GoogleSearch { search_types }),
(
proptest::option::of(any::<bool>()),
proptest::option::of(arb_clean_f64()),
proptest::option::of(arb_clean_f64()),
)
.prop_map(|(enable_widget, latitude, longitude)| Tool::GoogleMaps {
enable_widget,
latitude,
longitude,
}),
Just(Tool::CodeExecution),
Just(Tool::UrlContext),
(
proptest::collection::vec(arb_identifier(), 1..4),
proptest::option::of(any::<i32>()),
proptest::option::of(arb_text())
)
.prop_map(|(store_names, top_k, metadata_filter)| {
Tool::FileSearch {
store_names,
top_k,
metadata_filter,
}
}),
(
prop_oneof![
Just("browser".to_string()),
Just("mobile".to_string()),
Just("desktop".to_string()),
arb_identifier(),
],
proptest::collection::vec(arb_identifier(), 0..3),
proptest::option::of(any::<bool>()),
proptest::collection::vec(arb_identifier(), 0..2),
)
.prop_map(
|(environment, excluded, detect, disabled)| Tool::ComputerUse {
environment,
excluded_predefined_functions: excluded,
enable_prompt_injection_detection: detect,
disabled_safety_policies: disabled,
}
),
(
arb_identifier(),
arb_text(),
proptest::option::of(proptest::collection::vec(arb_allowed_tools(), 1..3)),
proptest::option::of(proptest::collection::btree_map(
arb_identifier(),
arb_text(),
1..3
)),
)
.prop_map(|(name, url, allowed_tools, headers)| Tool::McpServer {
name,
url,
allowed_tools,
headers: headers.map(|m| m.into_iter().collect()),
}),
(
proptest::option::of(proptest::collection::vec(arb_retrieval_type(), 1..3)),
proptest::option::of(arb_vertex_ai_search_config()),
proptest::option::of(arb_exa_ai_search_config()),
proptest::option::of(arb_parallel_ai_search_config()),
proptest::option::of(arb_rag_store_config()),
)
.prop_map(
|(
retrieval_types,
vertex_ai_search_config,
exa_ai_search_config,
parallel_ai_search_config,
rag_store_config,
)| Tool::Retrieval {
retrieval_types,
vertex_ai_search_config,
exa_ai_search_config,
parallel_ai_search_config,
rag_store_config: rag_store_config.map(Box::new),
},
),
(arb_unknown_type(), arb_unknown_object())
.prop_map(|(tool_type, data)| Tool::Unknown { tool_type, data }),
]
}
fn arb_retrieval_type() -> impl Strategy<Value = RetrievalType> {
prop_oneof![
Just(RetrievalType::VertexAiSearch),
Just(RetrievalType::RagStore),
Just(RetrievalType::ExaAiSearch),
Just(RetrievalType::ParallelAiSearch),
arb_unknown_type().prop_map(|retrieval_type| RetrievalType::Unknown {
data: serde_json::Value::String(retrieval_type.clone()),
retrieval_type,
}),
]
}
fn arb_vertex_ai_search_config() -> impl Strategy<Value = VertexAiSearchConfig> {
(
proptest::option::of(arb_identifier()),
proptest::option::of(proptest::collection::vec(arb_identifier(), 0..3)),
)
.prop_map(|(engine, datastores)| VertexAiSearchConfig { engine, datastores })
}
fn arb_exa_ai_search_config() -> impl Strategy<Value = ExaAiSearchConfig> {
(arb_identifier(), proptest::option::of(arb_unknown_object())).prop_map(
|(api_key, custom_config)| ExaAiSearchConfig {
api_key,
custom_config,
},
)
}
fn arb_parallel_ai_search_config() -> impl Strategy<Value = ParallelAiSearchConfig> {
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_unknown_object()),
)
.prop_map(|(api_key, custom_config)| ParallelAiSearchConfig {
api_key,
custom_config,
})
}
fn arb_rag_store_config() -> impl Strategy<Value = RagStoreConfig> {
(
proptest::option::of(proptest::collection::vec(
(
proptest::option::of(arb_identifier()),
proptest::option::of(proptest::collection::vec(arb_identifier(), 0..2)),
)
.prop_map(|(rag_corpus, rag_file_ids)| RagResource {
rag_corpus,
rag_file_ids,
}),
0..3,
)),
proptest::option::of(1..100i32),
proptest::option::of(arb_clean_f64()),
proptest::option::of(arb_rag_retrieval_config()),
)
.prop_map(
|(rag_resources, similarity_top_k, vector_distance_threshold, rag_retrieval_config)| {
RagStoreConfig {
rag_resources,
similarity_top_k,
vector_distance_threshold,
rag_retrieval_config,
}
},
)
}
fn arb_rag_retrieval_config() -> impl Strategy<Value = RagRetrievalConfig> {
(
proptest::option::of(1..100i32),
proptest::option::of(
proptest::option::of(arb_clean_float().prop_map(|v| v.as_f64().unwrap() as f32))
.prop_map(|alpha| HybridSearchConfig { alpha }),
),
proptest::option::of(
(
proptest::option::of(arb_clean_f64()),
proptest::option::of(arb_clean_f64()),
proptest::option::of(arb_text()),
)
.prop_map(
|(vector_distance_threshold, vector_similarity_threshold, metadata_filter)| {
RagFilter {
vector_distance_threshold,
vector_similarity_threshold,
metadata_filter,
}
},
),
),
proptest::option::of(
proptest::option::of(arb_identifier()).prop_map(|model_name| RagRanking {
ranking_config: "rank_service".to_string(),
model_name,
}),
),
)
.prop_map(
|(top_k, hybrid_search, filter, ranking)| RagRetrievalConfig {
top_k,
hybrid_search,
filter,
ranking,
},
)
}
fn arb_webhook_event() -> impl Strategy<Value = WebhookEvent> {
prop_oneof![
Just(WebhookEvent::BatchSucceeded),
Just(WebhookEvent::BatchExpired),
Just(WebhookEvent::BatchFailed),
Just(WebhookEvent::InteractionRequiresAction),
Just(WebhookEvent::InteractionCompleted),
Just(WebhookEvent::InteractionFailed),
Just(WebhookEvent::VideoGenerated),
arb_unknown_type().prop_map(|event_type| WebhookEvent::Unknown {
data: serde_json::Value::String(event_type.clone()),
event_type,
}),
]
}
fn arb_webhook_state() -> impl Strategy<Value = WebhookState> {
prop_oneof![
Just(WebhookState::Enabled),
Just(WebhookState::Disabled),
Just(WebhookState::DisabledDueToFailedDeliveries),
arb_unknown_type().prop_map(|state_type| WebhookState::Unknown {
data: serde_json::Value::String(state_type.clone()),
state_type,
}),
]
}
fn arb_revocation_behavior() -> impl Strategy<Value = RevocationBehavior> {
prop_oneof![
Just(RevocationBehavior::RevokePreviousSecretsAfterH24),
Just(RevocationBehavior::RevokePreviousSecretsImmediately),
arb_unknown_type().prop_map(|behavior_type| RevocationBehavior::Unknown {
data: serde_json::Value::String(behavior_type.clone()),
behavior_type,
}),
]
}
fn arb_webhook_config() -> impl Strategy<Value = WebhookConfig> {
(
proptest::option::of(proptest::collection::vec(arb_text(), 0..3)),
proptest::option::of(arb_unknown_object()),
)
.prop_map(|(uris, user_metadata)| WebhookConfig {
uris,
user_metadata,
})
}
fn arb_source_type() -> impl Strategy<Value = SourceType> {
prop_oneof![
Just(SourceType::Gcs),
Just(SourceType::Inline),
Just(SourceType::Repository),
Just(SourceType::SkillRegistry),
arb_unknown_type().prop_map(|source_type| SourceType::Unknown {
data: serde_json::Value::String(source_type.clone()),
source_type,
}),
]
}
fn arb_environment_source() -> impl Strategy<Value = EnvironmentSource> {
(
proptest::option::of(arb_source_type()),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_text()),
proptest::option::of(arb_identifier()),
)
.prop_map(
|(source_type, source, target, content, encoding)| EnvironmentSource {
source_type,
source,
target,
content,
encoding,
..Default::default()
},
)
}
fn arb_network_config() -> impl Strategy<Value = NetworkConfig> {
prop_oneof![
Just(NetworkConfig::Disabled),
proptest::collection::vec(
(
arb_identifier(),
proptest::option::of(proptest::collection::vec(
proptest::collection::btree_map(arb_identifier(), arb_text(), 1..3)
.prop_map(|m| m.into_iter().collect::<std::collections::HashMap<_, _>>()),
1..2,
)),
)
.prop_map(|(domain, transform)| AllowlistEntry {
domain,
transform,
..Default::default()
}),
0..3,
)
.prop_map(NetworkConfig::allowlist),
]
}
fn arb_environment_spec() -> impl Strategy<Value = EnvironmentSpec> {
prop_oneof![
arb_identifier().prop_map(EnvironmentSpec::Id),
(
proptest::collection::vec(arb_environment_source(), 0..3),
proptest::option::of(arb_network_config()),
)
.prop_map(|(sources, network)| {
EnvironmentSpec::Remote(RemoteEnvironment {
sources,
network,
..Default::default()
})
}),
]
}
fn arb_response_delivery() -> impl Strategy<Value = ResponseDelivery> {
prop_oneof![
Just(ResponseDelivery::Inline),
Just(ResponseDelivery::Uri),
arb_unknown_type().prop_map(|delivery_type| ResponseDelivery::Unknown {
data: serde_json::Value::String(delivery_type.clone()),
delivery_type,
}),
]
}
fn arb_response_format() -> impl Strategy<Value = ResponseFormat> {
prop_oneof![
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_unknown_object()),
)
.prop_map(|(mime_type, schema)| ResponseFormat::Text { mime_type, schema }),
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_response_delivery()),
proptest::option::of(8000..48000i32),
proptest::option::of(32000..320_000i32),
)
.prop_map(|(mime_type, delivery, sample_rate, bit_rate)| {
ResponseFormat::Audio {
mime_type,
delivery,
sample_rate,
bit_rate,
}
}),
(
proptest::option::of(arb_identifier()),
proptest::option::of(arb_response_delivery()),
proptest::option::of(arb_image_aspect_ratio()),
proptest::option::of(arb_image_size()),
)
.prop_map(|(mime_type, delivery, aspect_ratio, image_size)| {
ResponseFormat::Image {
mime_type,
delivery,
aspect_ratio,
image_size,
}
}),
(
proptest::option::of(arb_response_delivery()),
proptest::option::of(arb_identifier()),
proptest::option::of(arb_image_aspect_ratio()),
proptest::option::of(arb_identifier()),
)
.prop_map(|(delivery, gcs_uri, aspect_ratio, duration)| {
ResponseFormat::Video {
delivery,
gcs_uri,
aspect_ratio,
duration,
}
}),
]
}
fn arb_response_format_spec() -> impl Strategy<Value = ResponseFormatSpec> {
prop_oneof![
arb_response_format().prop_map(ResponseFormatSpec::Single),
proptest::collection::vec(arb_response_format(), 0..3).prop_map(ResponseFormatSpec::List),
]
}
fn arb_visualization() -> impl Strategy<Value = Visualization> {
prop_oneof![
Just(Visualization::Off),
Just(Visualization::Auto),
arb_unknown_type().prop_map(|visualization_type| Visualization::Unknown {
data: serde_json::Value::String(visualization_type.clone()),
visualization_type,
}),
]
}
fn arb_interaction_response() -> impl Strategy<Value = InteractionResponse> {
let part1 = (
proptest::option::of(arb_identifier()), proptest::option::of(arb_identifier()), proptest::option::of(arb_identifier()), proptest::option::of(arb_interaction_input()), prop::collection::vec(arb_step(), 0..4), arb_interaction_status(), proptest::option::of(arb_usage_metadata()), );
let part2 = (
proptest::option::of(prop::collection::vec(arb_tool(), 0..3)), proptest::option::of(arb_identifier()), proptest::option::of(arb_identifier()), proptest::option::of(Just("interaction".to_string())), proptest::option::of(arb_service_tier()), proptest::option::of(arb_webhook_config()), proptest::option::of(arb_text()), proptest::option::of(arb_datetime()), proptest::option::of(arb_datetime()), );
(part1, part2).prop_map(
|(
(id, model, agent, input, steps, status, usage),
(
tools,
previous_interaction_id,
environment_id,
object,
service_tier,
webhook_config,
output_text,
created,
updated,
),
)| {
InteractionResponse {
id,
model,
agent,
input,
steps,
status,
usage,
tools,
previous_interaction_id,
environment_id,
object,
service_tier,
webhook_config,
output_text,
created,
updated,
}
},
)
}
fn arb_stream_chunk() -> impl Strategy<Value = StreamChunk> {
prop_oneof![
arb_interaction_response().prop_map(|interaction| StreamChunk::Created { interaction }),
(arb_identifier(), arb_interaction_status()).prop_map(|(interaction_id, status)| {
StreamChunk::StatusUpdate {
interaction_id,
status,
}
}),
(any::<usize>(), arb_step())
.prop_map(|(index, step)| StreamChunk::StepStart { index, step }),
(any::<usize>(), arb_step_delta())
.prop_map(|(index, delta)| StreamChunk::StepDelta { index, delta }),
(
any::<usize>(),
proptest::option::of(arb_usage_metadata()),
proptest::option::of(arb_usage_metadata()),
)
.prop_map(|(index, usage, step_usage)| StreamChunk::StepStop {
index,
usage,
step_usage,
}),
arb_interaction_response().prop_map(StreamChunk::Completed),
(arb_text(), proptest::option::of(arb_identifier()))
.prop_map(|(message, code)| { StreamChunk::Error { message, code } }),
(arb_unknown_type(), arb_json_value())
.prop_map(|(chunk_type, data)| StreamChunk::Unknown { chunk_type, data }),
]
}
proptest! {
#[test]
fn modality_tokens_roundtrip(tokens in arb_modality_tokens()) {
let json = serde_json::to_string(&tokens).expect("Serialization should succeed");
let restored: ModalityTokens = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(tokens, restored);
}
#[test]
fn grounding_tool_count_roundtrip(count in arb_grounding_tool_count()) {
let json = serde_json::to_string(&count).expect("Serialization should succeed");
let restored: GroundingToolCount = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(count, restored);
}
#[test]
fn usage_metadata_roundtrip(usage in arb_usage_metadata()) {
let json = serde_json::to_string(&usage).expect("Serialization should succeed");
let restored: UsageMetadata = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(usage, restored);
}
#[test]
fn owned_function_call_info_roundtrip(info in arb_owned_function_call_info()) {
let json = serde_json::to_string(&info).expect("Serialization should succeed");
let restored: OwnedFunctionCallInfo = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(info, restored);
}
#[test]
fn interaction_status_roundtrip(status in arb_interaction_status()) {
let json = serde_json::to_string(&status).expect("Serialization should succeed");
let restored: InteractionStatus = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(status, restored);
}
#[test]
fn code_execution_language_roundtrip(lang in arb_code_execution_language()) {
let json = serde_json::to_string(&lang).expect("Serialization should succeed");
let restored: CodeExecutionLanguage = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(lang, restored);
}
#[test]
fn code_execution_language_wire_is_lowercase(_unused in Just(())) {
let json = serde_json::to_string(&CodeExecutionLanguage::Python).unwrap();
prop_assert_eq!(json, "\"python\"");
}
#[test]
fn function_calling_mode_roundtrip(mode in arb_function_calling_mode()) {
let json = serde_json::to_string(&mode).expect("Serialization should succeed");
let restored: FunctionCallingMode = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(mode, restored);
}
#[test]
fn thinking_level_roundtrip(level in arb_thinking_level()) {
let json = serde_json::to_string(&level).expect("Serialization should succeed");
let restored: ThinkingLevel = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(level, restored);
}
#[test]
fn thinking_summaries_roundtrip(summaries in arb_thinking_summaries()) {
let json = serde_json::to_string(&summaries).expect("Serialization should succeed");
let restored: ThinkingSummaries = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(summaries, restored);
}
#[test]
fn service_tier_roundtrip(tier in arb_service_tier()) {
let json = serde_json::to_string(&tier).expect("Serialization should succeed");
let restored: ServiceTier = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(tier, restored);
}
#[test]
fn search_type_roundtrip(search_type in arb_search_type()) {
let json = serde_json::to_string(&search_type).expect("Serialization should succeed");
let restored: SearchType = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(search_type, restored);
}
#[test]
fn image_aspect_ratio_roundtrip(ratio in arb_image_aspect_ratio()) {
let json = serde_json::to_string(&ratio).expect("Serialization should succeed");
let restored: ImageAspectRatio = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(ratio, restored);
}
#[test]
fn image_size_roundtrip(size in arb_image_size()) {
let json = serde_json::to_string(&size).expect("Serialization should succeed");
let restored: ImageSize = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(size, restored);
}
#[test]
fn image_config_roundtrip(config in arb_image_config()) {
let json = serde_json::to_string(&config).expect("Serialization should succeed");
let restored: ImageConfig = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(config, restored);
}
#[test]
fn role_roundtrip(role in arb_role()) {
let json = serde_json::to_string(&role).expect("Serialization should succeed");
let restored: Role = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(role, restored);
}
#[test]
fn annotation_roundtrip(annotation in arb_annotation()) {
assert_value_roundtrip(&annotation)?;
}
#[test]
fn turn_content_roundtrip(content in arb_turn_content()) {
assert_value_roundtrip(&content)?;
}
#[test]
fn allowed_tools_roundtrip(allowed in arb_allowed_tools()) {
let json = serde_json::to_string(&allowed).expect("Serialization should succeed");
let restored: AllowedTools = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(allowed, restored);
}
#[test]
fn tool_choice_roundtrip(choice in arb_tool_choice()) {
assert_value_roundtrip(&choice)?;
}
#[test]
fn agent_config_roundtrip(config in arb_agent_config()) {
let json = serde_json::to_string(&config).expect("Serialization should succeed");
let restored: AgentConfig = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(config, restored);
}
#[test]
fn generation_config_roundtrip(config in arb_generation_config()) {
assert_value_roundtrip(&config)?;
}
#[test]
fn video_task_roundtrip(task in arb_video_task()) {
let json = serde_json::to_string(&task).expect("Serialization should succeed");
let restored: VideoTask = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(task, restored);
}
#[test]
fn video_config_roundtrip(config in arb_video_config()) {
let json = serde_json::to_string(&config).expect("Serialization should succeed");
let restored: VideoConfig = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(config, restored);
}
#[test]
fn visualization_roundtrip(visualization in arb_visualization()) {
let json = serde_json::to_string(&visualization).expect("Serialization should succeed");
let restored: Visualization = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(visualization, restored);
}
#[test]
fn webhook_event_roundtrip(event in arb_webhook_event()) {
let json = serde_json::to_string(&event).expect("Serialization should succeed");
let restored: WebhookEvent = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(event, restored);
}
#[test]
fn webhook_state_roundtrip(state in arb_webhook_state()) {
let json = serde_json::to_string(&state).expect("Serialization should succeed");
let restored: WebhookState = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(state, restored);
}
#[test]
fn revocation_behavior_roundtrip(behavior in arb_revocation_behavior()) {
let json = serde_json::to_string(&behavior).expect("Serialization should succeed");
let restored: RevocationBehavior = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(behavior, restored);
}
#[test]
fn webhook_config_roundtrip(config in arb_webhook_config()) {
let json = serde_json::to_string(&config).expect("Serialization should succeed");
let restored: WebhookConfig = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(config, restored);
}
#[test]
fn source_type_roundtrip(source_type in arb_source_type()) {
let json = serde_json::to_string(&source_type).expect("Serialization should succeed");
let restored: SourceType = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(source_type, restored);
}
#[test]
fn environment_spec_roundtrip(spec in arb_environment_spec()) {
let json = serde_json::to_string(&spec).expect("Serialization should succeed");
let restored: EnvironmentSpec = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(spec, restored);
}
#[test]
fn response_delivery_roundtrip(delivery in arb_response_delivery()) {
let json = serde_json::to_string(&delivery).expect("Serialization should succeed");
let restored: ResponseDelivery = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(delivery, restored);
}
#[test]
fn response_format_roundtrip(format in arb_response_format()) {
let json = serde_json::to_string(&format).expect("Serialization should succeed");
let restored: ResponseFormat = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(format, restored);
}
#[test]
fn response_format_spec_roundtrip(spec in arb_response_format_spec()) {
let json = serde_json::to_string(&spec).expect("Serialization should succeed");
let restored: ResponseFormatSpec = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(spec, restored);
}
#[test]
fn retrieval_type_roundtrip(retrieval_type in arb_retrieval_type()) {
let json = serde_json::to_string(&retrieval_type).expect("Serialization should succeed");
let restored: RetrievalType = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(retrieval_type, restored);
}
#[test]
fn function_result_payload_roundtrip(payload in arb_function_result_payload()) {
let json = serde_json::to_value(&payload).expect("Serialization should succeed");
let restored: FunctionResultPayload =
serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert_eq!(
std::mem::discriminant(&payload),
std::mem::discriminant(&restored)
);
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
#[test]
fn content_roundtrip(content in arb_content()) {
assert_value_roundtrip(&content)?;
}
#[test]
fn step_error_roundtrip(error in arb_step_error()) {
let json = serde_json::to_string(&error).expect("Serialization should succeed");
let restored: StepError = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(error, restored);
}
#[test]
fn step_roundtrip(step in arb_step()) {
assert_value_roundtrip(&step)?;
}
#[test]
fn step_delta_roundtrip(delta in arb_step_delta()) {
assert_value_roundtrip(&delta)?;
}
#[test]
fn interaction_input_roundtrip(input in arb_interaction_input()) {
assert_value_roundtrip(&input)?;
if matches!(input, InteractionInput::Text(_)) {
let json = serde_json::to_value(&input).unwrap();
let restored: InteractionInput = serde_json::from_value(json).unwrap();
prop_assert!(matches!(restored, InteractionInput::Text(_)));
}
}
#[test]
fn tool_roundtrip(tool in arb_tool()) {
assert_value_roundtrip(&tool)?;
}
#[test]
fn interaction_response_roundtrip(response in arb_interaction_response()) {
let json = serde_json::to_value(&response).expect("Serialization should succeed");
let restored: InteractionResponse =
serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert_eq!(&response.id, &restored.id);
prop_assert_eq!(&response.model, &restored.model);
prop_assert_eq!(&response.agent, &restored.agent);
prop_assert_eq!(&response.status, &restored.status);
prop_assert_eq!(&response.usage, &restored.usage);
prop_assert_eq!(&response.previous_interaction_id, &restored.previous_interaction_id);
prop_assert_eq!(&response.environment_id, &restored.environment_id);
prop_assert_eq!(&response.output_text, &restored.output_text);
prop_assert_eq!(response.steps.len(), restored.steps.len());
prop_assert_eq!(&response.created, &restored.created);
prop_assert_eq!(&response.updated, &restored.updated);
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
#[test]
fn stream_chunk_roundtrip(chunk in arb_stream_chunk()) {
assert_value_roundtrip(&chunk)?;
}
}
#[cfg(not(feature = "strict-unknown"))]
proptest! {
#[test]
fn step_unknown_preservation(step_type in arb_unknown_type(), data in arb_unknown_object()) {
let step = Step::Unknown {
step_type: step_type.clone(),
data,
};
let json = serde_json::to_value(&step).expect("Serialization should succeed");
let restored: Step = serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert!(restored.is_unknown());
prop_assert_eq!(restored.unknown_step_type(), Some(step_type.as_str()));
prop_assert_eq!(restored.step_type(), step_type.as_str());
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
#[test]
fn content_unknown_preservation(content_type in arb_unknown_type(), data in arb_unknown_object()) {
let content = Content::Unknown {
content_type: content_type.clone(),
data,
};
let json = serde_json::to_value(&content).expect("Serialization should succeed");
let restored: Content = serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert!(restored.is_unknown());
prop_assert_eq!(restored.unknown_content_type(), Some(content_type.as_str()));
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
}
proptest! {
#[test]
fn step_delta_unknown_preservation(delta_type in arb_unknown_type(), data in arb_unknown_object()) {
let delta = StepDelta::Unknown {
delta_type: delta_type.clone(),
data,
};
let json = serde_json::to_value(&delta).expect("Serialization should succeed");
let restored: StepDelta = serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert!(restored.is_unknown());
prop_assert_eq!(restored.unknown_delta_type(), Some(delta_type.as_str()));
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
#[test]
fn annotation_unknown_preservation(annotation_type in arb_unknown_type(), data in arb_unknown_object()) {
let annotation = Annotation::Unknown {
annotation_type: annotation_type.clone(),
data,
};
let json = serde_json::to_value(&annotation).expect("Serialization should succeed");
let restored: Annotation = serde_json::from_value(json.clone()).expect("Deserialization should succeed");
prop_assert!(restored.is_unknown());
prop_assert_eq!(restored.unknown_annotation_type(), Some(annotation_type.as_str()));
let restored_json = serde_json::to_value(&restored).expect("Re-serialization should succeed");
prop_assert_eq!(json, restored_json);
}
}
proptest! {
#[test]
fn empty_text_content_roundtrip(_unused in Just(())) {
let content = Content::Text { text: Some(String::new()), annotations: None };
assert_value_roundtrip(&content)?;
}
#[test]
fn none_text_content_roundtrip(_unused in Just(())) {
let content = Content::Text { text: None, annotations: None };
assert_value_roundtrip(&content)?;
}
#[test]
fn special_chars_in_text(text in ".*[\n\r\t\"\\\\].*") {
let content = Content::Text { text: Some(text), annotations: None };
assert_value_roundtrip(&content)?;
}
#[test]
fn unicode_in_text(text in ".*[\\u{1F600}-\\u{1F64F}].*") {
let content = Content::Text { text: Some(text), annotations: None };
assert_value_roundtrip(&content)?;
}
#[test]
fn large_token_counts(
input in any::<u32>(),
output in any::<u32>(),
total in any::<u32>(),
) {
let usage = UsageMetadata {
total_input_tokens: Some(input),
total_output_tokens: Some(output),
total_tokens: Some(total),
..Default::default()
};
let json = serde_json::to_string(&usage).expect("Serialization should succeed");
let restored: UsageMetadata = serde_json::from_str(&json).expect("Deserialization should succeed");
prop_assert_eq!(usage, restored);
}
#[test]
fn empty_input_array_deserializes_as_steps(_unused in Just(())) {
let input = InteractionInput::Content(vec![]);
let json = serde_json::to_value(&input).expect("Serialization should succeed");
prop_assert_eq!(&json, &serde_json::json!([]));
let restored: InteractionInput =
serde_json::from_value(json).expect("Deserialization should succeed");
prop_assert_eq!(restored, InteractionInput::Steps(vec![]));
}
#[test]
fn deeply_nested_json_in_function_call(_unused in Just(())) {
let nested_args = serde_json::json!({
"level1": {
"level2": {
"level3": {
"level4": [1, 2, 3, "four", true, null]
},
"another_level3": {
"data": "value",
"numbers": [1.5, 2.5, 3.5]
}
},
"array_at_level2": [
{"nested_in_array": "works"},
[1, 2, [3, 4, 5]]
]
}
});
let step = Step::FunctionCall {
id: "call_123".to_string(),
name: "deep_function".to_string(),
arguments: nested_args,
signature: None,
};
assert_value_roundtrip(&step)?;
}
#[test]
fn deeply_nested_json_in_function_result(_unused in Just(())) {
let nested_result = serde_json::json!({
"success": true,
"data": {
"items": [
{
"id": 1,
"metadata": {
"created": "2024-01-01",
"tags": ["tag1", "tag2", {"complex": "tag"}]
}
},
{
"id": 2,
"metadata": {
"created": "2024-01-02",
"nested_array": [[1, 2], [3, 4], [[5, 6], [7, 8]]]
}
}
]
}
});
let step = Step::FunctionResult {
call_id: "call_123".to_string(),
name: Some("deep_function".to_string()),
result: FunctionResultPayload::Json(nested_result),
is_error: None,
signature: None,
};
assert_value_roundtrip(&step)?;
}
}