diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p0_tests.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p0_tests.rs index 3e334bd638..1603a02458 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p0_tests.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p0_tests.rs @@ -10,7 +10,7 @@ use enums::common::generation::common_resolution::CommonResolution; use enums::common::generation::common_video_model::CommonVideoModel; -use super::support::{base_generate_request, TestHarness}; +use super::support::{base_generate_request, Batch, ExpectedCredits, Seconds, TestHarness}; const STARTING_CREDITS: u64 = 100_000; @@ -22,31 +22,29 @@ async fn seedance_2p0_charges_by_resolution_duration_and_batch() { let _serial = mysql_testing::serial::acquire_serial_test_lock().await; let harness = TestHarness::create().await; - // (resolution, duration seconds, batch count, expected charged credits) - // // Volcengine rates: 480p 7.772 ¢/s, 720p 16 ¢/s, 1080p 46.632 ¢/s, // rounded once after duration × batch. Credits = cents. - let cases: &[(Option, u16, u16, u64)] = &[ - (Some(CommonResolution::FourEightyP), 5, 1, 39), - (Some(CommonResolution::FourEightyP), 10, 1, 78), - (Some(CommonResolution::SevenTwentyP), 5, 1, 80), - (Some(CommonResolution::SevenTwentyP), 10, 1, 160), - (Some(CommonResolution::SevenTwentyP), 15, 1, 240), - (Some(CommonResolution::SevenTwentyP), 5, 2, 160), - (Some(CommonResolution::TenEightyP), 5, 1, 233), - (Some(CommonResolution::TenEightyP), 10, 1, 466), + let cases: &[(Option, Seconds, Batch, ExpectedCredits)] = &[ + (Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(39)), + (Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(78)), + (Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(80)), + (Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(160)), + (Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(240)), + (Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(160)), + (Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(233)), + (Some(CommonResolution::TenEightyP), Seconds(10), Batch(1), ExpectedCredits(466)), // Default resolution is 720p. - (None, 5, 1, 80), + (None, Seconds(5), Batch(1), ExpectedCredits(80)), ]; - for (resolution, duration_seconds, batch_count, expected_credits) in cases { + for (resolution, seconds, batch, expected) in cases { assert_successful_generation_charges( &harness, CommonVideoModel::Seedance2p0, *resolution, - *duration_seconds, - *batch_count, - *expected_credits, + *seconds, + *batch, + *expected, ) .await; } @@ -59,21 +57,21 @@ async fn seedance_2p0_fast_charges_by_resolution_and_duration() { let harness = TestHarness::create().await; // Fast rates: 480p 5.181 ¢/s, 720p 12.727 ¢/s. - let cases: &[(Option, u16, u16, u64)] = &[ - (Some(CommonResolution::FourEightyP), 5, 1, 26), - (Some(CommonResolution::SevenTwentyP), 5, 1, 64), - (Some(CommonResolution::SevenTwentyP), 10, 1, 127), - (Some(CommonResolution::SevenTwentyP), 5, 2, 127), + let cases: &[(Option, Seconds, Batch, ExpectedCredits)] = &[ + (Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(26)), + (Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(64)), + (Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(127)), + (Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(127)), ]; - for (resolution, duration_seconds, batch_count, expected_credits) in cases { + for (resolution, seconds, batch, expected) in cases { assert_successful_generation_charges( &harness, CommonVideoModel::Seedance2p0Fast, *resolution, - *duration_seconds, - *batch_count, - *expected_credits, + *seconds, + *batch, + *expected, ) .await; } @@ -92,31 +90,30 @@ async fn byteplus_and_preview_variants_charge_their_own_rates_not_the_base_rate( let _serial = mysql_testing::serial::acquire_serial_test_lock().await; let harness = TestHarness::create().await; - // (model, resolution, duration, expected charged credits) - let cases: &[(CommonVideoModel, Option, u16, u64)] = &[ + let cases: &[(CommonVideoModel, Option, Seconds, ExpectedCredits)] = &[ // 720p 5s — the canonical bug shape. 125, NOT 80. - (CommonVideoModel::Seedance2p0BytePlus, Some(CommonResolution::SevenTwentyP), 5, 125), - (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::SevenTwentyP), 5, 125), - (CommonVideoModel::PreviewModel, Some(CommonResolution::SevenTwentyP), 5, 125), + (CommonVideoModel::Seedance2p0BytePlus, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(125)), + (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(125)), + (CommonVideoModel::PreviewModel, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(125)), // Fast 720p 5s: 100, NOT 64. - (CommonVideoModel::Seedance2p0BytePlusFast, Some(CommonResolution::SevenTwentyP), 5, 100), - (CommonVideoModel::Seedance2p0BytePlusUltraFast, Some(CommonResolution::SevenTwentyP), 5, 100), - (CommonVideoModel::PreviewModelFast, Some(CommonResolution::SevenTwentyP), 5, 100), + (CommonVideoModel::Seedance2p0BytePlusFast, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(100)), + (CommonVideoModel::Seedance2p0BytePlusUltraFast, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(100)), + (CommonVideoModel::PreviewModelFast, Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(100)), // Other resolutions and durations hold too. - (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::FourEightyP), 5, 50), - (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::TenEightyP), 5, 250), - (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::SevenTwentyP), 10, 250), - (CommonVideoModel::Seedance2p0BytePlusUltraFast, Some(CommonResolution::FourEightyP), 10, 90), + (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::FourEightyP), Seconds(5), ExpectedCredits(50)), + (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::TenEightyP), Seconds(5), ExpectedCredits(250)), + (CommonVideoModel::Seedance2p0BytePlusUltra, Some(CommonResolution::SevenTwentyP), Seconds(10), ExpectedCredits(250)), + (CommonVideoModel::Seedance2p0BytePlusUltraFast, Some(CommonResolution::FourEightyP), Seconds(10), ExpectedCredits(90)), ]; - for (model, resolution, duration_seconds, expected_credits) in cases { + for (model, resolution, seconds, expected) in cases { assert_successful_generation_charges( &harness, *model, *resolution, - *duration_seconds, - 1, - *expected_credits, + *seconds, + Batch(1), + *expected, ) .await; } @@ -150,15 +147,15 @@ async fn insufficient_balance_is_a_402_and_charges_nothing() { // ── Shared assertion ── /// Fund a fresh user, run one generation to completion via the stub Kinovi -/// server, and assert the wallet was debited exactly `expected_credits` +/// server, and assert the wallet was debited exactly the expected credits /// (balance delta AND ledger entry). async fn assert_successful_generation_charges( harness: &TestHarness, model: CommonVideoModel, resolution: Option, - duration_seconds: u16, - batch_count: u16, - expected_credits: u64, + Seconds(duration_seconds): Seconds, + Batch(batch_count): Batch, + ExpectedCredits(expected_credits): ExpectedCredits, ) { let user = harness.create_funded_user(STARTING_CREDITS).await; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p5_tests.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p5_tests.rs index 8c5ee26bc2..f37d104fa2 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p5_tests.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/seedance_2p5_tests.rs @@ -12,7 +12,7 @@ use enums::common::generation::common_resolution::CommonResolution; use enums::common::generation::common_video_model::CommonVideoModel; -use super::support::{base_generate_request, TestHarness}; +use super::support::{base_generate_request, ExpectedCredits, Seconds, TestHarness}; const STARTING_CREDITS: u64 = 100_000; @@ -26,25 +26,25 @@ async fn seedance_2p5_charges_by_resolution_and_duration() { // 2.5 rates: 480p 11.76954733 ¢/s, 720p 26.70781893 ¢/s, ceil-rounded. // Only 480p/720p exist; 1080p downgrades to 720p pricing. No batching. - let cases: &[(Option, u16, u64)] = &[ - (Some(CommonResolution::FourEightyP), 4, 48), - (Some(CommonResolution::FourEightyP), 5, 59), - (Some(CommonResolution::FourEightyP), 30, 354), - (Some(CommonResolution::SevenTwentyP), 4, 107), - (Some(CommonResolution::SevenTwentyP), 5, 134), - (Some(CommonResolution::SevenTwentyP), 10, 268), - (Some(CommonResolution::SevenTwentyP), 30, 802), + let cases: &[(Option, Seconds, ExpectedCredits)] = &[ + (Some(CommonResolution::FourEightyP), Seconds(4), ExpectedCredits(48)), + (Some(CommonResolution::FourEightyP), Seconds(5), ExpectedCredits(59)), + (Some(CommonResolution::FourEightyP), Seconds(30), ExpectedCredits(354)), + (Some(CommonResolution::SevenTwentyP), Seconds(4), ExpectedCredits(107)), + (Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(134)), + (Some(CommonResolution::SevenTwentyP), Seconds(10), ExpectedCredits(268)), + (Some(CommonResolution::SevenTwentyP), Seconds(30), ExpectedCredits(802)), // Default resolution is 720p. - (None, 5, 134), + (None, Seconds(5), ExpectedCredits(134)), ]; - for (resolution, duration_seconds, expected_credits) in cases { + for (resolution, seconds, expected) in cases { assert_successful_generation_charges( &harness, CommonVideoModel::Seedance2p5, *resolution, - *duration_seconds, - *expected_credits, + *seconds, + *expected, ) .await; } @@ -60,21 +60,21 @@ async fn seedance_2p5_ultra_charges_its_own_higher_rates() { let harness = TestHarness::create().await; // Ultra rates: 480p 13.90946502 ¢/s, 720p 31.56378601 ¢/s, ceil-rounded. - let cases: &[(Option, u16, u64)] = &[ - (Some(CommonResolution::FourEightyP), 5, 70), // regular would be 59 - (Some(CommonResolution::FourEightyP), 30, 418), // regular would be 354 - (Some(CommonResolution::SevenTwentyP), 5, 158), // regular would be 134 - (Some(CommonResolution::SevenTwentyP), 30, 947), // regular would be 802 - (None, 5, 158), + let cases: &[(Option, Seconds, ExpectedCredits)] = &[ + (Some(CommonResolution::FourEightyP), Seconds(5), ExpectedCredits(70)), // regular would be 59 + (Some(CommonResolution::FourEightyP), Seconds(30), ExpectedCredits(418)), // regular would be 354 + (Some(CommonResolution::SevenTwentyP), Seconds(5), ExpectedCredits(158)), // regular would be 134 + (Some(CommonResolution::SevenTwentyP), Seconds(30), ExpectedCredits(947)), // regular would be 802 + (None, Seconds(5), ExpectedCredits(158)), ]; - for (resolution, duration_seconds, expected_credits) in cases { + for (resolution, seconds, expected) in cases { assert_successful_generation_charges( &harness, CommonVideoModel::Seedance2p5Ultra, *resolution, - *duration_seconds, - *expected_credits, + *seconds, + *expected, ) .await; } @@ -96,20 +96,20 @@ async fn seedance_2p5_bills_reference_video_input_seconds_at_worst_case_when_unp let _serial = mysql_testing::serial::acquire_serial_test_lock().await; let harness = TestHarness::create().await; - let cases: &[(CommonVideoModel, Option, u16, u64)] = &[ - (CommonVideoModel::Seedance2p5, Some(CommonResolution::FourEightyP), 30, 435), - (CommonVideoModel::Seedance2p5, Some(CommonResolution::SevenTwentyP), 30, 951), - (CommonVideoModel::Seedance2p5Ultra, Some(CommonResolution::FourEightyP), 30, 514), - (CommonVideoModel::Seedance2p5Ultra, Some(CommonResolution::SevenTwentyP), 30, 1124), + let cases: &[(CommonVideoModel, Option, Seconds, ExpectedCredits)] = &[ + (CommonVideoModel::Seedance2p5, Some(CommonResolution::FourEightyP), Seconds(30), ExpectedCredits(435)), + (CommonVideoModel::Seedance2p5, Some(CommonResolution::SevenTwentyP), Seconds(30), ExpectedCredits(951)), + (CommonVideoModel::Seedance2p5Ultra, Some(CommonResolution::FourEightyP), Seconds(30), ExpectedCredits(514)), + (CommonVideoModel::Seedance2p5Ultra, Some(CommonResolution::SevenTwentyP), Seconds(30), ExpectedCredits(1124)), ]; - for (model, resolution, duration_seconds, expected_credits) in cases { + for (model, resolution, seconds, expected) in cases { assert_reference_video_charge_then_refund( &harness, *model, *resolution, - *duration_seconds, - *expected_credits, + *seconds, + *expected, ) .await; } @@ -123,8 +123,8 @@ async fn assert_successful_generation_charges( harness: &TestHarness, model: CommonVideoModel, resolution: Option, - duration_seconds: u16, - expected_credits: u64, + Seconds(duration_seconds): Seconds, + ExpectedCredits(expected_credits): ExpectedCredits, ) { let user = harness.create_funded_user(STARTING_CREDITS).await; @@ -156,8 +156,8 @@ async fn assert_reference_video_charge_then_refund( harness: &TestHarness, model: CommonVideoModel, resolution: Option, - duration_seconds: u16, - expected_credits: u64, + Seconds(duration_seconds): Seconds, + ExpectedCredits(expected_credits): ExpectedCredits, ) { let user = harness.create_funded_user(STARTING_CREDITS).await; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/support.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/support.rs index 4501266a06..24eab1c285 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/support.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/support.rs @@ -170,6 +170,17 @@ impl TestHarness { } } +/// Newtype wrappers so pricing-table test cases read unambiguously: +/// `(Some(FourEightyP), Seconds(5), Batch(1), ExpectedCredits(39))`. +#[derive(Clone, Copy, Debug)] +pub struct Seconds(pub u16); + +#[derive(Clone, Copy, Debug)] +pub struct Batch(pub u16); + +#[derive(Clone, Copy, Debug)] +pub struct ExpectedCredits(pub u64); + /// A generate request with only the model set; tests fill in the rest. /// Idempotency tokens must be unique per call. pub fn base_generate_request(model: CommonVideoModel) -> OmniGenVideoCostAndGenerateRequest {