test on api client

This commit is contained in:
Brandon Thomas committed 2026-07-08 16:08:44 -04:00
1 parent d7868c7da0
commit 54b172bf6f
2 files changed
+80 -4

No files matched your search

@@ -318,6 +318,28 @@ mod tests {
.cost_in_usd_cents.unwrap();
assert!(p1080 > p720, "1080p ({p1080}) should cost more than 720p ({p720})");
}
/// Regression: an image-less v1.5 request must get a quote through the
/// full public builder path (build2 → estimate_cost). The cost UI polls
/// for a price before the user has attached an image; the image
/// requirement is enforced at send time and by the generate endpoints,
/// NOT here.
#[test]
fn image_less_request_gets_a_quote_through_the_builder() {
let builder = GenerateVideoRequestBuilder {
model: RouterVideoModel::GrokImagineVideo1p5,
provider: RouterProvider::Artcraft,
duration_seconds: Some(5),
video_batch_count: Some(1),
..Default::default()
};
let cost = builder.build2()
.expect("build2 should succeed without an image")
.estimate_cost()
.expect("estimate_cost should succeed without an image");
assert!(cost.cost_in_usd_cents.unwrap() > 0);
assert_eq!(cost.cost_in_credits, cost.cost_in_usd_cents);
}
}
// ── Helpers ────────────────────────────────────────────────────────────
@@ -382,8 +404,7 @@ mod tests {
resolution,
duration_seconds: Some(duration_seconds),
video_batch_count: Some(video_batch_count),
// v1.5 requires an input image — the no-image guard in build()
// rejects T2V.
// These cases price the image-to-video tier (one input image).
start_frame: Some(ImageRef::MediaFileToken(MediaFileToken::new("mf_default".to_string()))),
..Default::default()
};
@@ -13,8 +13,11 @@ pub struct ArtcraftGrokImagineVideo1p5RequestState {
impl ArtcraftGrokImagineVideo1p5RequestState {
pub async fn send(&self, client: &RouterArtcraftClient) -> Result<GenerateVideoResponse, ArtcraftRouterError> {
// Defense in depth: `build()` already enforces this. Bouncing here costs
// nothing and avoids an upstream call we know will fail.
// xAI's v1.5 model rejects text-to-video at the server. This is the
// GENERATION-time guard — `build()` and `estimate_cost()` deliberately
// allow image-less states so the cost path can quote a request the user
// is still composing. Bouncing here costs nothing and avoids an upstream
// call we know will fail.
if self.request.start_frame_image_media_token.is_none()
&& self.request.reference_image_media_tokens.as_ref().map_or(true, |v| v.is_empty())
{
@@ -27,3 +30,55 @@ impl ArtcraftGrokImagineVideo1p5RequestState {
send_artcraft_omni_video_request(&self.request, client).await
}
}
#[cfg(test)]
mod tests {
use artcraft_client::credentials::storyteller_credential_set::StorytellerCredentialSet;
use artcraft_client::utils::api_host::ApiHost;
use enums::common::generation::common_video_model::CommonVideoModel as CommonVideoModelEnum;
use super::*;
/// Generation must never reach xAI with an image-less v1.5 request. The
/// build/cost layers allow that state (the cost path quotes it), so the
/// send-time guard is the router's last line of defense.
#[tokio::test]
async fn send_rejects_image_less_request_before_any_network_call() {
let state = ArtcraftGrokImagineVideo1p5RequestState {
request: OmniGenVideoCostAndGenerateRequest {
idempotency_token: Some("a1b2c3d4-e5f6-7890-abcd-ef1234567890".to_string()),
model: Some(CommonVideoModelEnum::GrokImagineVideo1p5),
prompt: Some("a corgi running through a field".to_string()),
negative_prompt: None,
start_frame_image_media_token: None,
end_frame_image_media_token: None,
reference_image_media_tokens: None,
reference_video_media_tokens: None,
reference_audio_media_tokens: None,
reference_character_tokens: None,
resolution: None,
aspect_ratio: None,
bitrate: None,
quality: None,
duration_seconds: Some(5),
video_batch_count: Some(1),
generate_audio: None,
},
};
// The guard fires before the client is used, so an unroutable localhost
// client with empty credentials proves no network call happens.
let client = RouterArtcraftClient::new(
ApiHost::Localhost { port: 1 },
StorytellerCredentialSet::empty(),
);
let err = state.send(&client).await.expect_err("image-less send should be rejected");
match err {
ArtcraftRouterError::Client(ClientError::ModelDoesNotSupportOption { field, .. }) => {
assert_eq!(field, "image_inputs");
}
other => panic!("expected Client(ModelDoesNotSupportOption), got {:?}", other),
}
}
}