mod cli_test_util;
use std::path::{Path, PathBuf};
use objectiveai_sdk::RemotePathCommitOptional;
use objectiveai_sdk::cli::command::functions::execute::standard::{
Request, RequestDangerousAdvanced, RequestInput, ResponseItem,
};
use objectiveai_sdk::cli::command::functions::execute::{
FunctionSpec, ProfileSpec,
};
use objectiveai_sdk::functions::FullInlineFunctionOrRemoteCommitOptional;
use objectiveai_sdk::functions::InlineProfileOrRemoteCommitOptional;
use objectiveai_sdk::functions::executions::response::streaming::FunctionExecutionChunk;
use objectiveai_sdk::functions::executions::response::unary::FunctionExecution;
fn snapshots_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../objectiveai-api/assets/functions/executions/client_tests")
}
fn mock_function_spec(name: &str) -> FunctionSpec {
FunctionSpec::Resolved(FullInlineFunctionOrRemoteCommitOptional::Remote(
RemotePathCommitOptional::Mock {
name: name.to_string(),
},
))
}
fn mock_profile_spec(name: &str) -> ProfileSpec {
ProfileSpec::Resolved(InlineProfileOrRemoteCommitOptional::Remote(
RemotePathCommitOptional::Mock {
name: name.to_string(),
},
))
}
fn inline_profile_spec(json: serde_json::Value) -> ProfileSpec {
ProfileSpec::Resolved(
serde_json::from_value::<InlineProfileOrRemoteCommitOptional>(json)
.expect("inline profile JSON must deserialize"),
)
}
async fn run_and_aggregate_normalized(request: Request) -> FunctionExecution {
let executor = cli_test_util::executor();
let items: Vec<ResponseItem> = cli_test_util::collect_stream(&executor, request).await;
let mut chunks = items.into_iter().filter_map(|item| match item {
ResponseItem::Chunk(c) => Some(c),
ResponseItem::Id(_) => None,
});
let mut agg: FunctionExecutionChunk =
chunks.next().expect("at least one chunk must be emitted");
for chunk in chunks {
agg.push(&chunk);
}
let mut execution: FunctionExecution = agg.into();
execution.normalize_for_tests();
execution
}
macro_rules! snapshot_test {
($name:ident, $snapshot:expr, $function:expr, $profile:expr, $seed:expr, $input:tt) => {
#[tokio::test]
async fn $name() {
if cli_test_util::test_api_address().is_none() {
eprintln!(
"OBJECTIVEAI_TEST_PORT not set — skipping {}",
stringify!($name)
);
return;
}
let request = Request {
path_type: objectiveai_sdk::cli::command::functions::execute::standard::Path::FunctionsExecuteStandard,
function: mock_function_spec($function),
profile: mock_profile_spec($profile),
input: RequestInput::Inline(
serde_json::from_value(serde_json::json!($input))
.expect("input must deserialize as InputValue"),
),
continuation: None,
retry_token: None,
split: false,
invert: false,
dangerous_advanced: Some(RequestDangerousAdvanced {
stream: Some(true),
seed: Some($seed),
}),
jq: None,
};
let execution = run_and_aggregate_normalized(request).await;
let snapshot_path = snapshots_dir().join(format!("{}.json", $snapshot));
cli_test_util::assert_normalized_snapshot(
&snapshot_path,
$snapshot,
&execution,
);
}
};
}
snapshot_test!(
mock_1_scalar_leaf_binary_seed_42,
"mock_1_scalar_leaf_binary_seed_42",
"binary-classifier",
"solo-instruction",
42,
{"text": "Hello world"}
);
snapshot_test!(
mock_7_vector_5_criteria_seed_42,
"mock_7_vector_5_criteria_seed_42",
"five-criteria-ranker",
"schema-heavy-trio",
42,
{"items": ["Option A", "Option B", "Option C"]}
);
snapshot_test!(
mock_20_vector_super_branch_seed_42,
"mock_20_vector_super_branch_seed_42",
"nested-vector-super-branch",
"nested-vector-inline-remote",
42,
{"items": ["Alpha", "Beta", "Gamma"]}
);
#[tokio::test]
async fn split_tweet_scorer_10_tweets_seed_42() {
if cli_test_util::test_api_address().is_none() {
eprintln!("OBJECTIVEAI_TEST_PORT not set — skipping split_tweet_scorer_10_tweets_seed_42");
return;
}
let snapshots = snapshots_dir();
let input_path = snapshots.join("inputs/10_tweets.json");
let input_value: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(&input_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", input_path.display())),
)
.expect("input JSON must parse");
let profile_json = serde_json::json!({
"agents": [
{"count": 1, "upstream": "mock", "output_mode": "instruction", "top_logprobs": 6},
{"count": 1, "upstream": "mock", "output_mode": "instruction"}
],
"weights": [1.0, 1.0]
});
let request = Request {
path_type: objectiveai_sdk::cli::command::functions::execute::standard::Path::FunctionsExecuteStandard,
function: mock_function_spec("tweet-scorer"),
profile: inline_profile_spec(profile_json),
input: RequestInput::Inline(
serde_json::from_value(input_value)
.expect("tweets input must deserialize as InputValue"),
),
continuation: None,
retry_token: None,
split: true,
invert: false,
dangerous_advanced: Some(RequestDangerousAdvanced {
stream: Some(true),
seed: Some(42),
}),
jq: None,
};
let execution = run_and_aggregate_normalized(request).await;
let snapshot_path =
snapshots_dir().join("split_tweet_scorer_10_tweets_seed_42.json");
cli_test_util::assert_normalized_snapshot(
&snapshot_path,
"split_tweet_scorer_10_tweets_seed_42",
&execution,
);
}