mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
test omni api urls
This commit is contained in:
+68
-1
@@ -12,7 +12,8 @@ use enums::common::generation::common_video_model::CommonVideoModel;
|
||||
use artcraft_api_keys::ArtcraftApiKey;
|
||||
|
||||
use crate::http_server::endpoints::omni_gen::generate::video::tests::support::{
|
||||
base_generate_request, ExpectedCredits, Seconds, TestHarness, STARTING_CREDITS,
|
||||
base_generate_request, to_omni_api_request, ExpectedCredits, Seconds, TestHarness,
|
||||
STARTING_CREDITS,
|
||||
};
|
||||
|
||||
mod billing_parity {
|
||||
@@ -45,6 +46,72 @@ mod billing_parity {
|
||||
}
|
||||
}
|
||||
|
||||
mod url_ingestion {
|
||||
use super::*;
|
||||
|
||||
/// The omni_api-only URL-input flow, end to end: a stub-hosted video URL
|
||||
/// is ingested (download → bucket upload → media_files row owned by the
|
||||
/// API user), then billed exactly like the same request made with a media
|
||||
/// TOKEN through omni_gen. Seedance 2.0 at 720p 5s with one reference
|
||||
/// video: 111 credits on both paths, fully successful, no refund.
|
||||
#[tokio::test]
|
||||
#[cfg_attr(feature = "skip_database_tests", ignore)]
|
||||
async fn reference_video_url_bills_like_a_media_token() {
|
||||
const EXPECTED_CREDITS: u64 = 111;
|
||||
|
||||
let harness = TestHarness::create().await;
|
||||
|
||||
// omni_gen with a probeable media TOKEN pointing at the 5s stub fixture.
|
||||
let web_user = harness.create_funded_user(STARTING_CREDITS).await;
|
||||
let video_token = mysql_testing::fixtures::media_files::create_probeable_test_video_media_file(
|
||||
&harness.pool,
|
||||
&web_user.user_token,
|
||||
5_000,
|
||||
)
|
||||
.await
|
||||
.expect("probeable video fixture");
|
||||
|
||||
let mut request = base_generate_request(CommonVideoModel::Seedance2p0);
|
||||
request.resolution = Some(CommonResolution::SevenTwentyP);
|
||||
request.duration_seconds = Some(5);
|
||||
request.reference_video_media_tokens = Some(vec![video_token]);
|
||||
let response = harness
|
||||
.post_generate(&web_user, request)
|
||||
.await
|
||||
.expect("omni_gen generation with reference token");
|
||||
assert!(response.success);
|
||||
|
||||
// omni_api with a URL to the same 5s fixture, no token.
|
||||
let api_user = harness.create_funded_user(STARTING_CREDITS).await;
|
||||
let api_key = harness.create_api_key(&api_user).await;
|
||||
|
||||
let mut base = base_generate_request(CommonVideoModel::Seedance2p0);
|
||||
base.resolution = Some(CommonResolution::SevenTwentyP);
|
||||
base.duration_seconds = Some(5);
|
||||
let mut api_request = to_omni_api_request(base);
|
||||
api_request.reference_video_urls =
|
||||
Some(vec![format!("{}/media/dur5000ms/source.mp4", harness.stub_kinovi_base_url)]);
|
||||
|
||||
let response = harness
|
||||
.post_omni_api_request(&api_key, api_request)
|
||||
.await
|
||||
.expect("omni_api generation with reference URL");
|
||||
assert!(response.success);
|
||||
|
||||
let web_debit = STARTING_CREDITS - harness.wallet_balance(&web_user).await;
|
||||
let api_debit = STARTING_CREDITS - harness.wallet_balance(&api_user).await;
|
||||
assert_eq!(web_debit, EXPECTED_CREDITS, "omni_gen token reference debited the wrong amount");
|
||||
assert_eq!(api_debit, EXPECTED_CREDITS, "omni_api URL reference debited the wrong amount");
|
||||
|
||||
let entries = harness.ledger_entries(&api_user).await;
|
||||
let debit = entries
|
||||
.iter()
|
||||
.find(|entry| entry.credits_delta < 0)
|
||||
.expect("omni_api URL generation must have a debit ledger entry");
|
||||
assert!(!debit.is_refunded, "successful URL-input generation must not be refunded");
|
||||
}
|
||||
}
|
||||
|
||||
mod api_key_only_auth {
|
||||
use super::*;
|
||||
|
||||
|
||||
+29
-6
@@ -180,6 +180,16 @@ impl TestHarness {
|
||||
&self,
|
||||
api_key: &ArtcraftApiKey,
|
||||
request: OmniGenVideoCostAndGenerateRequest,
|
||||
) -> Result<OmniGenVideoGenerateResponse, CommonWebError> {
|
||||
self.post_omni_api_request(api_key, to_omni_api_request(request)).await
|
||||
}
|
||||
|
||||
/// POST a raw omni_api request — URL input fields allowed — with API-key
|
||||
/// authentication. Use this to exercise the URL-ingestion path.
|
||||
pub async fn post_omni_api_request(
|
||||
&self,
|
||||
api_key: &ArtcraftApiKey,
|
||||
api_request: OmniApiVideoGenerateRequest,
|
||||
) -> Result<OmniGenVideoGenerateResponse, CommonWebError> {
|
||||
let http_request = TestRequest::post()
|
||||
.uri("/v1/omni_api/generate/video")
|
||||
@@ -187,8 +197,6 @@ impl TestHarness {
|
||||
.peer_addr("127.0.0.1:9999".parse().expect("peer addr"))
|
||||
.to_http_request();
|
||||
|
||||
let api_request = to_omni_api_request(request);
|
||||
|
||||
omni_api_video_generate_handler(
|
||||
http_request,
|
||||
Json(api_request),
|
||||
@@ -325,8 +333,12 @@ fn process_stub_kinovi_base_url() -> &'static String {
|
||||
/// - `GET` paths containing `dur{millis}ms` serve a REAL generated video of
|
||||
/// that duration (ffmpeg, cached per duration) — the reference-video
|
||||
/// billing probe downloads and ffprobes these.
|
||||
/// - `GET` paths under the `video_` bucket prefix (URL-ingested uploads)
|
||||
/// serve a default 5s fixture video; other GETs are 404 so the
|
||||
/// unreachable-token fixtures keep failing as intended.
|
||||
/// - `POST …uploads.signedUploadUrl` answers with an upload URL back at this
|
||||
/// stub; the subsequent `PUT` is accepted with 200.
|
||||
/// stub; any `PUT` (that upload, or an S3 PutObject from the test bucket
|
||||
/// client) is accepted with 200.
|
||||
/// - Any other `POST` answers as a successful workflow.runTask with
|
||||
/// process-unique order ids.
|
||||
fn spawn_stub_server() -> String {
|
||||
@@ -374,6 +386,13 @@ fn spawn_stub_server() -> String {
|
||||
if request_line.starts_with("GET") {
|
||||
match extract_fixture_duration_millis(&request_line) {
|
||||
Some(millis) => ("200 OK", "video/mp4", fixture_video_bytes(millis)),
|
||||
// URL-ingested uploads land under the `video_` bucket prefix
|
||||
// (see MediaUploadKind::bucket_prefix); serve those a default
|
||||
// 5s fixture. Token-fixture paths without a prefix stay 404 —
|
||||
// the charge-then-refund tests depend on that.
|
||||
None if request_line.contains("/video_") => {
|
||||
("200 OK", "video/mp4", fixture_video_bytes(5_000))
|
||||
}
|
||||
None => ("404 Not Found", "text/plain", b"not found".to_vec()),
|
||||
}
|
||||
} else if request_line.starts_with("PUT") {
|
||||
@@ -497,13 +516,16 @@ fn build_test_server_state(pool: MySqlPool, media_cdn_override_url: String) -> S
|
||||
use std::sync::OnceLock;
|
||||
static DUMMIES: OnceLock<ExpensiveDummies> = OnceLock::new();
|
||||
let dummies = DUMMIES.get_or_init(|| retry_keychain_flake(|| {
|
||||
// Bucket uploads (e.g. URL-input ingestion) PUT to the stub, which
|
||||
// accepts anything; downloads then come back through the media CDN
|
||||
// override, also the stub.
|
||||
let dummy_bucket = || {
|
||||
LegacyBucketClient::create(
|
||||
"test-access-key",
|
||||
"test-secret-key",
|
||||
"auto",
|
||||
"test-bucket",
|
||||
"http://127.0.0.1:1",
|
||||
&media_cdn_override_url,
|
||||
None,
|
||||
Some(Duration::from_secs(5)),
|
||||
)
|
||||
@@ -860,8 +882,9 @@ pub async fn assert_real_input_videos_charge(
|
||||
}
|
||||
|
||||
/// Map an omni_gen request onto the omni_api request shape (token fields
|
||||
/// only; the URL fields stay None — URL ingestion is not exercised here).
|
||||
fn to_omni_api_request(request: OmniGenVideoCostAndGenerateRequest) -> OmniApiVideoGenerateRequest {
|
||||
/// only; the URL fields start None — tests exercising URL ingestion set
|
||||
/// them afterwards).
|
||||
pub fn to_omni_api_request(request: OmniGenVideoCostAndGenerateRequest) -> OmniApiVideoGenerateRequest {
|
||||
OmniApiVideoGenerateRequest {
|
||||
idempotency_token: request.idempotency_token,
|
||||
model: request.model,
|
||||
|
||||
Reference in New Issue
Block a user