more thorough

This commit is contained in:
Hanashi
2026-08-09 20:29:13 -04:00
parent c112423e2c
commit 72c60302df
4 changed files with 971 additions and 118 deletions
@@ -24,4 +24,4 @@
pub mod support;
mod seedance_2p0;
mod seedance_2p5_tests;
mod seedance_2p5;
@@ -0,0 +1,3 @@
//! Seedance 2.5 family pricing tests.
mod seedance_2p5_tests;
@@ -0,0 +1,967 @@
//! Seedance 2.5 family pricing tests: 2.5 and 2.5 Ultra through the real
//! generate endpoint against the test database, asserting exact wallet
//! debits — including the reference-video input-seconds dimension.
//!
//! Rate cards (ceil-rounded once):
//! - Seedance 2.5: 480p 11.76954733 ¢/s, 720p 26.70781893 ¢/s;
//! with video refs 480p 7.24279835 ¢/s, 720p 15.84362140 ¢/s over
//! (output + input) seconds
//! - Seedance 2.5 Ultra: 480p 13.90946502 ¢/s, 720p 31.56378601 ¢/s;
//! with video refs 480p 8.55967078 ¢/s, 720p 18.72427984 ¢/s
//!
//! Only 480p and 720p are offered (1080p/4K downgrade to 720p), durations
//! run 4-30s, and every request generates a single video regardless of the
//! requested batch size.
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation::common_video_model::CommonVideoModel;
use crate::http_server::endpoints::omni_gen::generate::video::tests::support::{
assert_reference_video_charge_then_refund, assert_successful_generation_charges,
assert_variant_charges_premium, Batch, CreditsDelta, ExpectedCredits, Seconds, TestHarness,
};
// ── Seedance 2.5 ──
mod seedance_2p5 {
use super::*;
/// Every duration 4-30s at every resolution, single video.
/// Seedance 2.5 offers 480p and 720p; 1080p and 4K downgrade to 720p (and price accordingly).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_every_duration_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(48)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(71)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(83)),
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(95)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(106)),
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(118)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(130)),
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(142)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(154)),
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(165)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(177)),
(Some(CommonResolution::FourEightyP), Seconds(16), Batch(1), ExpectedCredits(189)),
(Some(CommonResolution::FourEightyP), Seconds(17), Batch(1), ExpectedCredits(201)),
(Some(CommonResolution::FourEightyP), Seconds(18), Batch(1), ExpectedCredits(212)),
(Some(CommonResolution::FourEightyP), Seconds(19), Batch(1), ExpectedCredits(224)),
(Some(CommonResolution::FourEightyP), Seconds(20), Batch(1), ExpectedCredits(236)),
(Some(CommonResolution::FourEightyP), Seconds(21), Batch(1), ExpectedCredits(248)),
(Some(CommonResolution::FourEightyP), Seconds(22), Batch(1), ExpectedCredits(259)),
(Some(CommonResolution::FourEightyP), Seconds(23), Batch(1), ExpectedCredits(271)),
(Some(CommonResolution::FourEightyP), Seconds(24), Batch(1), ExpectedCredits(283)),
(Some(CommonResolution::FourEightyP), Seconds(25), Batch(1), ExpectedCredits(295)),
(Some(CommonResolution::FourEightyP), Seconds(26), Batch(1), ExpectedCredits(307)),
(Some(CommonResolution::FourEightyP), Seconds(27), Batch(1), ExpectedCredits(318)),
(Some(CommonResolution::FourEightyP), Seconds(28), Batch(1), ExpectedCredits(330)),
(Some(CommonResolution::FourEightyP), Seconds(29), Batch(1), ExpectedCredits(342)),
(Some(CommonResolution::FourEightyP), Seconds(30), Batch(1), ExpectedCredits(354)),
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(107)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(161)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(187)),
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(214)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(241)),
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(268)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(294)),
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(321)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(374)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(401)),
(Some(CommonResolution::SevenTwentyP), Seconds(16), Batch(1), ExpectedCredits(428)),
(Some(CommonResolution::SevenTwentyP), Seconds(17), Batch(1), ExpectedCredits(455)),
(Some(CommonResolution::SevenTwentyP), Seconds(18), Batch(1), ExpectedCredits(481)),
(Some(CommonResolution::SevenTwentyP), Seconds(19), Batch(1), ExpectedCredits(508)),
(Some(CommonResolution::SevenTwentyP), Seconds(20), Batch(1), ExpectedCredits(535)),
(Some(CommonResolution::SevenTwentyP), Seconds(21), Batch(1), ExpectedCredits(561)),
(Some(CommonResolution::SevenTwentyP), Seconds(22), Batch(1), ExpectedCredits(588)),
(Some(CommonResolution::SevenTwentyP), Seconds(23), Batch(1), ExpectedCredits(615)),
(Some(CommonResolution::SevenTwentyP), Seconds(24), Batch(1), ExpectedCredits(641)),
(Some(CommonResolution::SevenTwentyP), Seconds(25), Batch(1), ExpectedCredits(668)),
(Some(CommonResolution::SevenTwentyP), Seconds(26), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::SevenTwentyP), Seconds(27), Batch(1), ExpectedCredits(722)),
(Some(CommonResolution::SevenTwentyP), Seconds(28), Batch(1), ExpectedCredits(748)),
(Some(CommonResolution::SevenTwentyP), Seconds(29), Batch(1), ExpectedCredits(775)),
(Some(CommonResolution::SevenTwentyP), Seconds(30), Batch(1), ExpectedCredits(802)),
(Some(CommonResolution::TenEightyP), Seconds(4), Batch(1), ExpectedCredits(107)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(1), ExpectedCredits(161)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(1), ExpectedCredits(187)),
(Some(CommonResolution::TenEightyP), Seconds(8), Batch(1), ExpectedCredits(214)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(1), ExpectedCredits(241)),
(Some(CommonResolution::TenEightyP), Seconds(10), Batch(1), ExpectedCredits(268)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(1), ExpectedCredits(294)),
(Some(CommonResolution::TenEightyP), Seconds(12), Batch(1), ExpectedCredits(321)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::TenEightyP), Seconds(14), Batch(1), ExpectedCredits(374)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(1), ExpectedCredits(401)),
(Some(CommonResolution::TenEightyP), Seconds(16), Batch(1), ExpectedCredits(428)),
(Some(CommonResolution::TenEightyP), Seconds(17), Batch(1), ExpectedCredits(455)),
(Some(CommonResolution::TenEightyP), Seconds(18), Batch(1), ExpectedCredits(481)),
(Some(CommonResolution::TenEightyP), Seconds(19), Batch(1), ExpectedCredits(508)),
(Some(CommonResolution::TenEightyP), Seconds(20), Batch(1), ExpectedCredits(535)),
(Some(CommonResolution::TenEightyP), Seconds(21), Batch(1), ExpectedCredits(561)),
(Some(CommonResolution::TenEightyP), Seconds(22), Batch(1), ExpectedCredits(588)),
(Some(CommonResolution::TenEightyP), Seconds(23), Batch(1), ExpectedCredits(615)),
(Some(CommonResolution::TenEightyP), Seconds(24), Batch(1), ExpectedCredits(641)),
(Some(CommonResolution::TenEightyP), Seconds(25), Batch(1), ExpectedCredits(668)),
(Some(CommonResolution::TenEightyP), Seconds(26), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::TenEightyP), Seconds(27), Batch(1), ExpectedCredits(722)),
(Some(CommonResolution::TenEightyP), Seconds(28), Batch(1), ExpectedCredits(748)),
(Some(CommonResolution::TenEightyP), Seconds(29), Batch(1), ExpectedCredits(775)),
(Some(CommonResolution::TenEightyP), Seconds(30), Batch(1), ExpectedCredits(802)),
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(107)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(161)),
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(187)),
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(214)),
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(241)),
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(268)),
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(294)),
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(321)),
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(374)),
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(401)),
(Some(CommonResolution::FourK), Seconds(16), Batch(1), ExpectedCredits(428)),
(Some(CommonResolution::FourK), Seconds(17), Batch(1), ExpectedCredits(455)),
(Some(CommonResolution::FourK), Seconds(18), Batch(1), ExpectedCredits(481)),
(Some(CommonResolution::FourK), Seconds(19), Batch(1), ExpectedCredits(508)),
(Some(CommonResolution::FourK), Seconds(20), Batch(1), ExpectedCredits(535)),
(Some(CommonResolution::FourK), Seconds(21), Batch(1), ExpectedCredits(561)),
(Some(CommonResolution::FourK), Seconds(22), Batch(1), ExpectedCredits(588)),
(Some(CommonResolution::FourK), Seconds(23), Batch(1), ExpectedCredits(615)),
(Some(CommonResolution::FourK), Seconds(24), Batch(1), ExpectedCredits(641)),
(Some(CommonResolution::FourK), Seconds(25), Batch(1), ExpectedCredits(668)),
(Some(CommonResolution::FourK), Seconds(26), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::FourK), Seconds(27), Batch(1), ExpectedCredits(722)),
(Some(CommonResolution::FourK), Seconds(28), Batch(1), ExpectedCredits(748)),
(Some(CommonResolution::FourK), Seconds(29), Batch(1), ExpectedCredits(775)),
(Some(CommonResolution::FourK), Seconds(30), Batch(1), ExpectedCredits(802)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// Every batch size 1-10 at every resolution (5s).
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
/// Seedance 2.5 offers 480p and 720p; 1080p and 4K downgrade to 720p (and price accordingly).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_every_batch_size_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(59)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(59)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(134)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(134)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(134)),
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(134)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// Spot checks across duration x batch x resolution (batch always bills as one video).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_spot_checked_combinations() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(71)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(83)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(106)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(130)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(154)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(177)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(161)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(187)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(241)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(294)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(348)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(401)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(161)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(187)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(241)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(294)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(348)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(401)),
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(161)),
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(187)),
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(241)),
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(294)),
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(348)),
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(401)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Every duration 4-30s at every resolution.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_every_duration_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(247)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(261)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(268)),
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(276)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(283)),
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(290)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(297)),
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(305)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(312)),
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(319)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(326)),
(Some(CommonResolution::FourEightyP), Seconds(16), Batch(1), ExpectedCredits(334)),
(Some(CommonResolution::FourEightyP), Seconds(17), Batch(1), ExpectedCredits(341)),
(Some(CommonResolution::FourEightyP), Seconds(18), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::FourEightyP), Seconds(19), Batch(1), ExpectedCredits(355)),
(Some(CommonResolution::FourEightyP), Seconds(20), Batch(1), ExpectedCredits(363)),
(Some(CommonResolution::FourEightyP), Seconds(21), Batch(1), ExpectedCredits(370)),
(Some(CommonResolution::FourEightyP), Seconds(22), Batch(1), ExpectedCredits(377)),
(Some(CommonResolution::FourEightyP), Seconds(23), Batch(1), ExpectedCredits(384)),
(Some(CommonResolution::FourEightyP), Seconds(24), Batch(1), ExpectedCredits(392)),
(Some(CommonResolution::FourEightyP), Seconds(25), Batch(1), ExpectedCredits(399)),
(Some(CommonResolution::FourEightyP), Seconds(26), Batch(1), ExpectedCredits(406)),
(Some(CommonResolution::FourEightyP), Seconds(27), Batch(1), ExpectedCredits(413)),
(Some(CommonResolution::FourEightyP), Seconds(28), Batch(1), ExpectedCredits(421)),
(Some(CommonResolution::FourEightyP), Seconds(29), Batch(1), ExpectedCredits(428)),
(Some(CommonResolution::FourEightyP), Seconds(30), Batch(1), ExpectedCredits(435)),
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(539)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(571)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(587)),
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(603)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(618)),
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(634)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(650)),
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(666)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(682)),
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(698)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(713)),
(Some(CommonResolution::SevenTwentyP), Seconds(16), Batch(1), ExpectedCredits(729)),
(Some(CommonResolution::SevenTwentyP), Seconds(17), Batch(1), ExpectedCredits(745)),
(Some(CommonResolution::SevenTwentyP), Seconds(18), Batch(1), ExpectedCredits(761)),
(Some(CommonResolution::SevenTwentyP), Seconds(19), Batch(1), ExpectedCredits(777)),
(Some(CommonResolution::SevenTwentyP), Seconds(20), Batch(1), ExpectedCredits(793)),
(Some(CommonResolution::SevenTwentyP), Seconds(21), Batch(1), ExpectedCredits(809)),
(Some(CommonResolution::SevenTwentyP), Seconds(22), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::SevenTwentyP), Seconds(23), Batch(1), ExpectedCredits(840)),
(Some(CommonResolution::SevenTwentyP), Seconds(24), Batch(1), ExpectedCredits(856)),
(Some(CommonResolution::SevenTwentyP), Seconds(25), Batch(1), ExpectedCredits(872)),
(Some(CommonResolution::SevenTwentyP), Seconds(26), Batch(1), ExpectedCredits(888)),
(Some(CommonResolution::SevenTwentyP), Seconds(27), Batch(1), ExpectedCredits(904)),
(Some(CommonResolution::SevenTwentyP), Seconds(28), Batch(1), ExpectedCredits(919)),
(Some(CommonResolution::SevenTwentyP), Seconds(29), Batch(1), ExpectedCredits(935)),
(Some(CommonResolution::SevenTwentyP), Seconds(30), Batch(1), ExpectedCredits(951)),
(Some(CommonResolution::TenEightyP), Seconds(4), Batch(1), ExpectedCredits(539)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(1), ExpectedCredits(571)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(1), ExpectedCredits(587)),
(Some(CommonResolution::TenEightyP), Seconds(8), Batch(1), ExpectedCredits(603)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(1), ExpectedCredits(618)),
(Some(CommonResolution::TenEightyP), Seconds(10), Batch(1), ExpectedCredits(634)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(1), ExpectedCredits(650)),
(Some(CommonResolution::TenEightyP), Seconds(12), Batch(1), ExpectedCredits(666)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(1), ExpectedCredits(682)),
(Some(CommonResolution::TenEightyP), Seconds(14), Batch(1), ExpectedCredits(698)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(1), ExpectedCredits(713)),
(Some(CommonResolution::TenEightyP), Seconds(16), Batch(1), ExpectedCredits(729)),
(Some(CommonResolution::TenEightyP), Seconds(17), Batch(1), ExpectedCredits(745)),
(Some(CommonResolution::TenEightyP), Seconds(18), Batch(1), ExpectedCredits(761)),
(Some(CommonResolution::TenEightyP), Seconds(19), Batch(1), ExpectedCredits(777)),
(Some(CommonResolution::TenEightyP), Seconds(20), Batch(1), ExpectedCredits(793)),
(Some(CommonResolution::TenEightyP), Seconds(21), Batch(1), ExpectedCredits(809)),
(Some(CommonResolution::TenEightyP), Seconds(22), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::TenEightyP), Seconds(23), Batch(1), ExpectedCredits(840)),
(Some(CommonResolution::TenEightyP), Seconds(24), Batch(1), ExpectedCredits(856)),
(Some(CommonResolution::TenEightyP), Seconds(25), Batch(1), ExpectedCredits(872)),
(Some(CommonResolution::TenEightyP), Seconds(26), Batch(1), ExpectedCredits(888)),
(Some(CommonResolution::TenEightyP), Seconds(27), Batch(1), ExpectedCredits(904)),
(Some(CommonResolution::TenEightyP), Seconds(28), Batch(1), ExpectedCredits(919)),
(Some(CommonResolution::TenEightyP), Seconds(29), Batch(1), ExpectedCredits(935)),
(Some(CommonResolution::TenEightyP), Seconds(30), Batch(1), ExpectedCredits(951)),
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(539)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(571)),
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(587)),
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(603)),
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(618)),
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(634)),
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(650)),
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(666)),
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(682)),
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(698)),
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(713)),
(Some(CommonResolution::FourK), Seconds(16), Batch(1), ExpectedCredits(729)),
(Some(CommonResolution::FourK), Seconds(17), Batch(1), ExpectedCredits(745)),
(Some(CommonResolution::FourK), Seconds(18), Batch(1), ExpectedCredits(761)),
(Some(CommonResolution::FourK), Seconds(19), Batch(1), ExpectedCredits(777)),
(Some(CommonResolution::FourK), Seconds(20), Batch(1), ExpectedCredits(793)),
(Some(CommonResolution::FourK), Seconds(21), Batch(1), ExpectedCredits(809)),
(Some(CommonResolution::FourK), Seconds(22), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::FourK), Seconds(23), Batch(1), ExpectedCredits(840)),
(Some(CommonResolution::FourK), Seconds(24), Batch(1), ExpectedCredits(856)),
(Some(CommonResolution::FourK), Seconds(25), Batch(1), ExpectedCredits(872)),
(Some(CommonResolution::FourK), Seconds(26), Batch(1), ExpectedCredits(888)),
(Some(CommonResolution::FourK), Seconds(27), Batch(1), ExpectedCredits(904)),
(Some(CommonResolution::FourK), Seconds(28), Batch(1), ExpectedCredits(919)),
(Some(CommonResolution::FourK), Seconds(29), Batch(1), ExpectedCredits(935)),
(Some(CommonResolution::FourK), Seconds(30), Batch(1), ExpectedCredits(951)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Every batch size 1-10 at every resolution (5s).
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_every_batch_size_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(254)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(254)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(555)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(555)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(555)),
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(555)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Spot checks across duration x batch x resolution.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_spot_checked_combinations() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(261)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(268)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(283)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(297)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(312)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(326)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(571)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(587)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(618)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(650)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(682)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(713)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(571)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(587)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(618)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(650)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(682)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(713)),
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(571)),
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(587)),
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(618)),
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(650)),
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(682)),
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(713)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5, *resolution, *seconds, *batch, *expected,
).await;
}
}
}
// ── Seedance 2.5 Ultra ──
// Fulfilled by Seedance 2.5 (same execution request), priced as itself and
// routed to a different provider account. If the pipeline ever collapses
// the model before billing, every row here fails with the regular 2.5
// numbers.
mod seedance_2p5_u {
use super::*;
/// Every duration 4-30s at every resolution, single video.
/// Seedance 2.5 offers 480p and 720p; 1080p and 4K downgrade to 720p (and price accordingly).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_every_duration_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(56)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(84)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(98)),
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(112)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(126)),
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(140)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(154)),
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(167)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(181)),
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(195)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(209)),
(Some(CommonResolution::FourEightyP), Seconds(16), Batch(1), ExpectedCredits(223)),
(Some(CommonResolution::FourEightyP), Seconds(17), Batch(1), ExpectedCredits(237)),
(Some(CommonResolution::FourEightyP), Seconds(18), Batch(1), ExpectedCredits(251)),
(Some(CommonResolution::FourEightyP), Seconds(19), Batch(1), ExpectedCredits(265)),
(Some(CommonResolution::FourEightyP), Seconds(20), Batch(1), ExpectedCredits(279)),
(Some(CommonResolution::FourEightyP), Seconds(21), Batch(1), ExpectedCredits(293)),
(Some(CommonResolution::FourEightyP), Seconds(22), Batch(1), ExpectedCredits(307)),
(Some(CommonResolution::FourEightyP), Seconds(23), Batch(1), ExpectedCredits(320)),
(Some(CommonResolution::FourEightyP), Seconds(24), Batch(1), ExpectedCredits(334)),
(Some(CommonResolution::FourEightyP), Seconds(25), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::FourEightyP), Seconds(26), Batch(1), ExpectedCredits(362)),
(Some(CommonResolution::FourEightyP), Seconds(27), Batch(1), ExpectedCredits(376)),
(Some(CommonResolution::FourEightyP), Seconds(28), Batch(1), ExpectedCredits(390)),
(Some(CommonResolution::FourEightyP), Seconds(29), Batch(1), ExpectedCredits(404)),
(Some(CommonResolution::FourEightyP), Seconds(30), Batch(1), ExpectedCredits(418)),
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(127)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(190)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(221)),
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(253)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(285)),
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(316)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(379)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(411)),
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(442)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(474)),
(Some(CommonResolution::SevenTwentyP), Seconds(16), Batch(1), ExpectedCredits(506)),
(Some(CommonResolution::SevenTwentyP), Seconds(17), Batch(1), ExpectedCredits(537)),
(Some(CommonResolution::SevenTwentyP), Seconds(18), Batch(1), ExpectedCredits(569)),
(Some(CommonResolution::SevenTwentyP), Seconds(19), Batch(1), ExpectedCredits(600)),
(Some(CommonResolution::SevenTwentyP), Seconds(20), Batch(1), ExpectedCredits(632)),
(Some(CommonResolution::SevenTwentyP), Seconds(21), Batch(1), ExpectedCredits(663)),
(Some(CommonResolution::SevenTwentyP), Seconds(22), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::SevenTwentyP), Seconds(23), Batch(1), ExpectedCredits(726)),
(Some(CommonResolution::SevenTwentyP), Seconds(24), Batch(1), ExpectedCredits(758)),
(Some(CommonResolution::SevenTwentyP), Seconds(25), Batch(1), ExpectedCredits(790)),
(Some(CommonResolution::SevenTwentyP), Seconds(26), Batch(1), ExpectedCredits(821)),
(Some(CommonResolution::SevenTwentyP), Seconds(27), Batch(1), ExpectedCredits(853)),
(Some(CommonResolution::SevenTwentyP), Seconds(28), Batch(1), ExpectedCredits(884)),
(Some(CommonResolution::SevenTwentyP), Seconds(29), Batch(1), ExpectedCredits(916)),
(Some(CommonResolution::SevenTwentyP), Seconds(30), Batch(1), ExpectedCredits(947)),
(Some(CommonResolution::TenEightyP), Seconds(4), Batch(1), ExpectedCredits(127)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(1), ExpectedCredits(190)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(1), ExpectedCredits(221)),
(Some(CommonResolution::TenEightyP), Seconds(8), Batch(1), ExpectedCredits(253)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(1), ExpectedCredits(285)),
(Some(CommonResolution::TenEightyP), Seconds(10), Batch(1), ExpectedCredits(316)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::TenEightyP), Seconds(12), Batch(1), ExpectedCredits(379)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(1), ExpectedCredits(411)),
(Some(CommonResolution::TenEightyP), Seconds(14), Batch(1), ExpectedCredits(442)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(1), ExpectedCredits(474)),
(Some(CommonResolution::TenEightyP), Seconds(16), Batch(1), ExpectedCredits(506)),
(Some(CommonResolution::TenEightyP), Seconds(17), Batch(1), ExpectedCredits(537)),
(Some(CommonResolution::TenEightyP), Seconds(18), Batch(1), ExpectedCredits(569)),
(Some(CommonResolution::TenEightyP), Seconds(19), Batch(1), ExpectedCredits(600)),
(Some(CommonResolution::TenEightyP), Seconds(20), Batch(1), ExpectedCredits(632)),
(Some(CommonResolution::TenEightyP), Seconds(21), Batch(1), ExpectedCredits(663)),
(Some(CommonResolution::TenEightyP), Seconds(22), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::TenEightyP), Seconds(23), Batch(1), ExpectedCredits(726)),
(Some(CommonResolution::TenEightyP), Seconds(24), Batch(1), ExpectedCredits(758)),
(Some(CommonResolution::TenEightyP), Seconds(25), Batch(1), ExpectedCredits(790)),
(Some(CommonResolution::TenEightyP), Seconds(26), Batch(1), ExpectedCredits(821)),
(Some(CommonResolution::TenEightyP), Seconds(27), Batch(1), ExpectedCredits(853)),
(Some(CommonResolution::TenEightyP), Seconds(28), Batch(1), ExpectedCredits(884)),
(Some(CommonResolution::TenEightyP), Seconds(29), Batch(1), ExpectedCredits(916)),
(Some(CommonResolution::TenEightyP), Seconds(30), Batch(1), ExpectedCredits(947)),
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(127)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(190)),
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(221)),
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(253)),
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(285)),
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(316)),
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(348)),
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(379)),
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(411)),
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(442)),
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(474)),
(Some(CommonResolution::FourK), Seconds(16), Batch(1), ExpectedCredits(506)),
(Some(CommonResolution::FourK), Seconds(17), Batch(1), ExpectedCredits(537)),
(Some(CommonResolution::FourK), Seconds(18), Batch(1), ExpectedCredits(569)),
(Some(CommonResolution::FourK), Seconds(19), Batch(1), ExpectedCredits(600)),
(Some(CommonResolution::FourK), Seconds(20), Batch(1), ExpectedCredits(632)),
(Some(CommonResolution::FourK), Seconds(21), Batch(1), ExpectedCredits(663)),
(Some(CommonResolution::FourK), Seconds(22), Batch(1), ExpectedCredits(695)),
(Some(CommonResolution::FourK), Seconds(23), Batch(1), ExpectedCredits(726)),
(Some(CommonResolution::FourK), Seconds(24), Batch(1), ExpectedCredits(758)),
(Some(CommonResolution::FourK), Seconds(25), Batch(1), ExpectedCredits(790)),
(Some(CommonResolution::FourK), Seconds(26), Batch(1), ExpectedCredits(821)),
(Some(CommonResolution::FourK), Seconds(27), Batch(1), ExpectedCredits(853)),
(Some(CommonResolution::FourK), Seconds(28), Batch(1), ExpectedCredits(884)),
(Some(CommonResolution::FourK), Seconds(29), Batch(1), ExpectedCredits(916)),
(Some(CommonResolution::FourK), Seconds(30), Batch(1), ExpectedCredits(947)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// Every batch size 1-10 at every resolution (5s).
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
/// Seedance 2.5 offers 480p and 720p; 1080p and 4K downgrade to 720p (and price accordingly).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_every_batch_size_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(70)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(70)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(158)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(158)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(158)),
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(158)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// Spot checks across duration x batch x resolution (batch always bills as one video).
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn charges_spot_checked_combinations() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(84)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(98)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(126)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(154)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(181)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(209)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(190)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(221)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(285)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(348)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(411)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(474)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(190)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(221)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(285)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(348)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(411)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(474)),
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(190)),
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(221)),
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(285)),
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(348)),
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(411)),
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(474)),
];
for (resolution, seconds, batch, expected) in cases {
assert_successful_generation_charges(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Every duration 4-30s at every resolution.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_every_duration_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(4), Batch(1), ExpectedCredits(292)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(1), ExpectedCredits(309)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(1), ExpectedCredits(317)),
(Some(CommonResolution::FourEightyP), Seconds(8), Batch(1), ExpectedCredits(326)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(1), ExpectedCredits(334)),
(Some(CommonResolution::FourEightyP), Seconds(10), Batch(1), ExpectedCredits(343)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(1), ExpectedCredits(351)),
(Some(CommonResolution::FourEightyP), Seconds(12), Batch(1), ExpectedCredits(360)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(1), ExpectedCredits(369)),
(Some(CommonResolution::FourEightyP), Seconds(14), Batch(1), ExpectedCredits(377)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(1), ExpectedCredits(386)),
(Some(CommonResolution::FourEightyP), Seconds(16), Batch(1), ExpectedCredits(394)),
(Some(CommonResolution::FourEightyP), Seconds(17), Batch(1), ExpectedCredits(403)),
(Some(CommonResolution::FourEightyP), Seconds(18), Batch(1), ExpectedCredits(411)),
(Some(CommonResolution::FourEightyP), Seconds(19), Batch(1), ExpectedCredits(420)),
(Some(CommonResolution::FourEightyP), Seconds(20), Batch(1), ExpectedCredits(428)),
(Some(CommonResolution::FourEightyP), Seconds(21), Batch(1), ExpectedCredits(437)),
(Some(CommonResolution::FourEightyP), Seconds(22), Batch(1), ExpectedCredits(446)),
(Some(CommonResolution::FourEightyP), Seconds(23), Batch(1), ExpectedCredits(454)),
(Some(CommonResolution::FourEightyP), Seconds(24), Batch(1), ExpectedCredits(463)),
(Some(CommonResolution::FourEightyP), Seconds(25), Batch(1), ExpectedCredits(471)),
(Some(CommonResolution::FourEightyP), Seconds(26), Batch(1), ExpectedCredits(480)),
(Some(CommonResolution::FourEightyP), Seconds(27), Batch(1), ExpectedCredits(488)),
(Some(CommonResolution::FourEightyP), Seconds(28), Batch(1), ExpectedCredits(497)),
(Some(CommonResolution::FourEightyP), Seconds(29), Batch(1), ExpectedCredits(506)),
(Some(CommonResolution::FourEightyP), Seconds(30), Batch(1), ExpectedCredits(514)),
(Some(CommonResolution::SevenTwentyP), Seconds(4), Batch(1), ExpectedCredits(637)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(1), ExpectedCredits(675)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(1), ExpectedCredits(693)),
(Some(CommonResolution::SevenTwentyP), Seconds(8), Batch(1), ExpectedCredits(712)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(1), ExpectedCredits(731)),
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(749)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(1), ExpectedCredits(768)),
(Some(CommonResolution::SevenTwentyP), Seconds(12), Batch(1), ExpectedCredits(787)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(1), ExpectedCredits(806)),
(Some(CommonResolution::SevenTwentyP), Seconds(14), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(843)),
(Some(CommonResolution::SevenTwentyP), Seconds(16), Batch(1), ExpectedCredits(862)),
(Some(CommonResolution::SevenTwentyP), Seconds(17), Batch(1), ExpectedCredits(881)),
(Some(CommonResolution::SevenTwentyP), Seconds(18), Batch(1), ExpectedCredits(899)),
(Some(CommonResolution::SevenTwentyP), Seconds(19), Batch(1), ExpectedCredits(918)),
(Some(CommonResolution::SevenTwentyP), Seconds(20), Batch(1), ExpectedCredits(937)),
(Some(CommonResolution::SevenTwentyP), Seconds(21), Batch(1), ExpectedCredits(955)),
(Some(CommonResolution::SevenTwentyP), Seconds(22), Batch(1), ExpectedCredits(974)),
(Some(CommonResolution::SevenTwentyP), Seconds(23), Batch(1), ExpectedCredits(993)),
(Some(CommonResolution::SevenTwentyP), Seconds(24), Batch(1), ExpectedCredits(1012)),
(Some(CommonResolution::SevenTwentyP), Seconds(25), Batch(1), ExpectedCredits(1030)),
(Some(CommonResolution::SevenTwentyP), Seconds(26), Batch(1), ExpectedCredits(1049)),
(Some(CommonResolution::SevenTwentyP), Seconds(27), Batch(1), ExpectedCredits(1068)),
(Some(CommonResolution::SevenTwentyP), Seconds(28), Batch(1), ExpectedCredits(1087)),
(Some(CommonResolution::SevenTwentyP), Seconds(29), Batch(1), ExpectedCredits(1105)),
(Some(CommonResolution::SevenTwentyP), Seconds(30), Batch(1), ExpectedCredits(1124)),
(Some(CommonResolution::TenEightyP), Seconds(4), Batch(1), ExpectedCredits(637)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(1), ExpectedCredits(675)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(1), ExpectedCredits(693)),
(Some(CommonResolution::TenEightyP), Seconds(8), Batch(1), ExpectedCredits(712)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(1), ExpectedCredits(731)),
(Some(CommonResolution::TenEightyP), Seconds(10), Batch(1), ExpectedCredits(749)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(1), ExpectedCredits(768)),
(Some(CommonResolution::TenEightyP), Seconds(12), Batch(1), ExpectedCredits(787)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(1), ExpectedCredits(806)),
(Some(CommonResolution::TenEightyP), Seconds(14), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(1), ExpectedCredits(843)),
(Some(CommonResolution::TenEightyP), Seconds(16), Batch(1), ExpectedCredits(862)),
(Some(CommonResolution::TenEightyP), Seconds(17), Batch(1), ExpectedCredits(881)),
(Some(CommonResolution::TenEightyP), Seconds(18), Batch(1), ExpectedCredits(899)),
(Some(CommonResolution::TenEightyP), Seconds(19), Batch(1), ExpectedCredits(918)),
(Some(CommonResolution::TenEightyP), Seconds(20), Batch(1), ExpectedCredits(937)),
(Some(CommonResolution::TenEightyP), Seconds(21), Batch(1), ExpectedCredits(955)),
(Some(CommonResolution::TenEightyP), Seconds(22), Batch(1), ExpectedCredits(974)),
(Some(CommonResolution::TenEightyP), Seconds(23), Batch(1), ExpectedCredits(993)),
(Some(CommonResolution::TenEightyP), Seconds(24), Batch(1), ExpectedCredits(1012)),
(Some(CommonResolution::TenEightyP), Seconds(25), Batch(1), ExpectedCredits(1030)),
(Some(CommonResolution::TenEightyP), Seconds(26), Batch(1), ExpectedCredits(1049)),
(Some(CommonResolution::TenEightyP), Seconds(27), Batch(1), ExpectedCredits(1068)),
(Some(CommonResolution::TenEightyP), Seconds(28), Batch(1), ExpectedCredits(1087)),
(Some(CommonResolution::TenEightyP), Seconds(29), Batch(1), ExpectedCredits(1105)),
(Some(CommonResolution::TenEightyP), Seconds(30), Batch(1), ExpectedCredits(1124)),
(Some(CommonResolution::FourK), Seconds(4), Batch(1), ExpectedCredits(637)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(6), Batch(1), ExpectedCredits(675)),
(Some(CommonResolution::FourK), Seconds(7), Batch(1), ExpectedCredits(693)),
(Some(CommonResolution::FourK), Seconds(8), Batch(1), ExpectedCredits(712)),
(Some(CommonResolution::FourK), Seconds(9), Batch(1), ExpectedCredits(731)),
(Some(CommonResolution::FourK), Seconds(10), Batch(1), ExpectedCredits(749)),
(Some(CommonResolution::FourK), Seconds(11), Batch(1), ExpectedCredits(768)),
(Some(CommonResolution::FourK), Seconds(12), Batch(1), ExpectedCredits(787)),
(Some(CommonResolution::FourK), Seconds(13), Batch(1), ExpectedCredits(806)),
(Some(CommonResolution::FourK), Seconds(14), Batch(1), ExpectedCredits(824)),
(Some(CommonResolution::FourK), Seconds(15), Batch(1), ExpectedCredits(843)),
(Some(CommonResolution::FourK), Seconds(16), Batch(1), ExpectedCredits(862)),
(Some(CommonResolution::FourK), Seconds(17), Batch(1), ExpectedCredits(881)),
(Some(CommonResolution::FourK), Seconds(18), Batch(1), ExpectedCredits(899)),
(Some(CommonResolution::FourK), Seconds(19), Batch(1), ExpectedCredits(918)),
(Some(CommonResolution::FourK), Seconds(20), Batch(1), ExpectedCredits(937)),
(Some(CommonResolution::FourK), Seconds(21), Batch(1), ExpectedCredits(955)),
(Some(CommonResolution::FourK), Seconds(22), Batch(1), ExpectedCredits(974)),
(Some(CommonResolution::FourK), Seconds(23), Batch(1), ExpectedCredits(993)),
(Some(CommonResolution::FourK), Seconds(24), Batch(1), ExpectedCredits(1012)),
(Some(CommonResolution::FourK), Seconds(25), Batch(1), ExpectedCredits(1030)),
(Some(CommonResolution::FourK), Seconds(26), Batch(1), ExpectedCredits(1049)),
(Some(CommonResolution::FourK), Seconds(27), Batch(1), ExpectedCredits(1068)),
(Some(CommonResolution::FourK), Seconds(28), Batch(1), ExpectedCredits(1087)),
(Some(CommonResolution::FourK), Seconds(29), Batch(1), ExpectedCredits(1105)),
(Some(CommonResolution::FourK), Seconds(30), Batch(1), ExpectedCredits(1124)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Every batch size 1-10 at every resolution (5s).
/// Seedance 2.5 generates a single video per request: every requested batch size 1-10 bills exactly one video.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_every_batch_size_at_every_resolution() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(2), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(3), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(4), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(5), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(6), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(7), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(8), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(9), ExpectedCredits(300)),
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(10), ExpectedCredits(300)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(2), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(3), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(4), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(5), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(6), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(7), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(8), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(9), ExpectedCredits(656)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(10), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(2), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(3), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(4), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(5), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(6), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(7), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(8), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(9), ExpectedCredits(656)),
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(10), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(2), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(3), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(4), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(5), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(6), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(7), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(8), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(9), ExpectedCredits(656)),
(Some(CommonResolution::FourK), Seconds(5), Batch(10), ExpectedCredits(656)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
/// With video references the per-second rate drops but billed seconds = output duration + probed input seconds. The fixture media is unreachable, so the probe fails open to the worst-case 30 input seconds, the upload then fails, and the exact charge is asserted on the refunded ledger entry.
/// Spot checks across duration x batch x resolution.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn video_references_charge_spot_checked_combinations() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits)] = &[
(Some(CommonResolution::FourEightyP), Seconds(6), Batch(2), ExpectedCredits(309)),
(Some(CommonResolution::FourEightyP), Seconds(7), Batch(3), ExpectedCredits(317)),
(Some(CommonResolution::FourEightyP), Seconds(9), Batch(4), ExpectedCredits(334)),
(Some(CommonResolution::FourEightyP), Seconds(11), Batch(5), ExpectedCredits(351)),
(Some(CommonResolution::FourEightyP), Seconds(13), Batch(8), ExpectedCredits(369)),
(Some(CommonResolution::FourEightyP), Seconds(15), Batch(10), ExpectedCredits(386)),
(Some(CommonResolution::SevenTwentyP), Seconds(6), Batch(2), ExpectedCredits(675)),
(Some(CommonResolution::SevenTwentyP), Seconds(7), Batch(3), ExpectedCredits(693)),
(Some(CommonResolution::SevenTwentyP), Seconds(9), Batch(4), ExpectedCredits(731)),
(Some(CommonResolution::SevenTwentyP), Seconds(11), Batch(5), ExpectedCredits(768)),
(Some(CommonResolution::SevenTwentyP), Seconds(13), Batch(8), ExpectedCredits(806)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(10), ExpectedCredits(843)),
(Some(CommonResolution::TenEightyP), Seconds(6), Batch(2), ExpectedCredits(675)),
(Some(CommonResolution::TenEightyP), Seconds(7), Batch(3), ExpectedCredits(693)),
(Some(CommonResolution::TenEightyP), Seconds(9), Batch(4), ExpectedCredits(731)),
(Some(CommonResolution::TenEightyP), Seconds(11), Batch(5), ExpectedCredits(768)),
(Some(CommonResolution::TenEightyP), Seconds(13), Batch(8), ExpectedCredits(806)),
(Some(CommonResolution::TenEightyP), Seconds(15), Batch(10), ExpectedCredits(843)),
(Some(CommonResolution::FourK), Seconds(6), Batch(2), ExpectedCredits(675)),
(Some(CommonResolution::FourK), Seconds(7), Batch(3), ExpectedCredits(693)),
(Some(CommonResolution::FourK), Seconds(9), Batch(4), ExpectedCredits(731)),
(Some(CommonResolution::FourK), Seconds(11), Batch(5), ExpectedCredits(768)),
(Some(CommonResolution::FourK), Seconds(13), Batch(8), ExpectedCredits(806)),
(Some(CommonResolution::FourK), Seconds(15), Batch(10), ExpectedCredits(843)),
];
for (resolution, seconds, batch, expected) in cases {
assert_reference_video_charge_then_refund(
&harness, CommonVideoModel::Seedance2p5Ultra, *resolution, *seconds, *batch, *expected,
).await;
}
}
}
// ── Revenue premium: Ultra over the base model ──
// Both prices and the delta are encoded so a change to EITHER rate card
// shows up here, not just a flipped ordering.
mod premium {
use super::*;
/// Seedance 2.5 Ultra is fulfilled by Seedance 2.5 but has its own (higher)
/// price. Same shape as the 2.0 collapse-bug pins: if the pipeline ever
/// collapses Ultra before billing, these fail with the regular 2.5 numbers.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn seedance_2p5_ultra_charges_a_premium_over_the_base_model() {
let harness = TestHarness::create().await;
let cases: &[(Option<CommonResolution>, Seconds, Batch, ExpectedCredits, ExpectedCredits, CreditsDelta)] = &[
(Some(CommonResolution::FourEightyP), Seconds(5), Batch(1), ExpectedCredits(59), ExpectedCredits(70), CreditsDelta(11)),
(Some(CommonResolution::FourEightyP), Seconds(30), Batch(1), ExpectedCredits(354), ExpectedCredits(418), CreditsDelta(64)),
(Some(CommonResolution::SevenTwentyP), Seconds(5), Batch(1), ExpectedCredits(134), ExpectedCredits(158), CreditsDelta(24)),
(Some(CommonResolution::SevenTwentyP), Seconds(10), Batch(1), ExpectedCredits(268), ExpectedCredits(316), CreditsDelta(48)),
(Some(CommonResolution::SevenTwentyP), Seconds(15), Batch(1), ExpectedCredits(401), ExpectedCredits(474), CreditsDelta(73)),
(Some(CommonResolution::SevenTwentyP), Seconds(30), Batch(1), ExpectedCredits(802), ExpectedCredits(947), CreditsDelta(145)),
// 1080p and 4K downgrade to 720p, so the premium matches 720p.
(Some(CommonResolution::TenEightyP), Seconds(5), Batch(1), ExpectedCredits(134), ExpectedCredits(158), CreditsDelta(24)),
(Some(CommonResolution::FourK), Seconds(5), Batch(1), ExpectedCredits(134), ExpectedCredits(158), CreditsDelta(24)),
];
for (resolution, seconds, batch, base, variant, delta) in cases {
assert_variant_charges_premium(
&harness,
CommonVideoModel::Seedance2p5,
CommonVideoModel::Seedance2p5Ultra,
*resolution, *seconds, *batch, *base, *variant, *delta,
).await;
}
}
}
@@ -1,117 +0,0 @@
//! Seedance 2.5 family pricing tests: 2.5 and 2.5 Ultra through the real
//! generate endpoint against the test database, asserting exact wallet
//! debits — including the reference-video input-seconds dimension.
//!
//! Reference-video note: the fixture media files point at unreachable CDN
//! URLs, so the duration probe fails open and bills the worst-case 30 input
//! seconds (pinned here), and the provider upload then fails, triggering a
//! refund. The ledger entry keeps its original debit amount with
//! `is_refunded = true`, which lets these tests assert the exact charge for
//! reference-video requests without any real media.
use enums::common::generation::common_resolution::CommonResolution;
use enums::common::generation::common_video_model::CommonVideoModel;
use super::support::{
assert_reference_video_charge_then_refund, assert_successful_generation_charges, Batch,
ExpectedCredits, Seconds, TestHarness,
};
// ── Text-to-video pricing (successful generation via the stub provider) ──
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn seedance_2p5_charges_by_resolution_and_duration() {
let harness = TestHarness::create().await;
// 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<CommonResolution>, 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, Seconds(5), ExpectedCredits(134)),
];
for (resolution, seconds, expected) in cases {
assert_successful_generation_charges(
&harness,
CommonVideoModel::Seedance2p5,
*resolution,
*seconds,
Batch(1),
*expected,
)
.await;
}
}
/// Seedance 2.5 Ultra is fulfilled by Seedance 2.5 but has its own (higher)
/// price. Same shape as the 2.0 collapse-bug pins: if the pipeline ever
/// collapses Ultra before billing, these fail with the regular 2.5 numbers.
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
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<CommonResolution>, 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, seconds, expected) in cases {
assert_successful_generation_charges(
&harness,
CommonVideoModel::Seedance2p5Ultra,
*resolution,
*seconds,
Batch(1),
*expected,
)
.await;
}
}
// ── Reference-video input-seconds billing ──
//
// With video references the per-second rate drops but billed seconds =
// output duration + probed input seconds. Unreachable fixture media probes
// fail open to the 30-second worst case:
// 2.5: 480p with refs = 7.24279835 ¢/s → 30s out + 30s in = 60 × rate = 435
// 2.5: 720p with refs = 15.84362140 ¢/s → 60 × rate = 951
// Ultra: 480p with refs = 8.55967078 ¢/s → 60 × rate = 514
// Ultra: 720p with refs = 18.72427984 ¢/s → 60 × rate = 1124
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn seedance_2p5_bills_reference_video_input_seconds_at_worst_case_when_unprobeable() {
let harness = TestHarness::create().await;
let cases: &[(CommonVideoModel, Option<CommonResolution>, 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, seconds, expected) in cases {
assert_reference_video_charge_then_refund(
&harness,
*model,
*resolution,
*seconds,
Batch(1),
*expected,
)
.await;
}
}