package helper import ( "net/http/httptest" "strconv" "testing" "github.com/QuantumNous/new-api/common" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) // 与用户提供的 playground JSON 等价(字段语义一致),用于演练 ModelPriceHelperVideo 的估值链路。 func seedancePlaygroundTaskReq(t *testing.T) relaycommon.TaskSubmitReq { t.Helper() const payload = `{ "model": "Seedance2.0", "prompt": "生成一个小猫嗷嗷叫的视频", "n": 1, "size": "960x540", "fps": 24, "duration": 5, "motion": 0.6, "negative_prompt": "", "seed": null, "images": [] }` var req relaycommon.TaskSubmitReq require.NoError(t, common.UnmarshalJsonStr(payload, &req)) return req } // TestSeedancePlaygroundJSON_PerSecondQuota 演示:若全局为 Seedance2.0 配置了「文生视频按秒」规则, // 则预扣额度 = ceil(duration)×每秒单价(USD)×QuotaPerUnit×分组倍率×渠道折扣。 // 单价需替换为你们控制台真实配置;此处用 0.01 USD/秒便于断言公式。 func TestSeedancePlaygroundJSON_PerSecondQuota(t *testing.T) { gin.SetMode(gin.TestMode) prevRules := ratio_setting.VideoPricingRules2JSONString() defer func() { _ = ratio_setting.UpdateVideoPricingRulesByJSONString(prevRules) }() const pricePerSecUSD = 0.01 cfg := `{"Seedance2.0":{"text_to_video_per_second":[{"resolution":"540p","has_audio":false,"price":` + strconv.FormatFloat(pricePerSecUSD, 'g', -1, 64) + `}]}}` require.NoError(t, ratio_setting.UpdateVideoPricingRulesByJSONString(cfg)) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) req := seedancePlaygroundTaskReq(t) c.Set("task_request", req) relayInfo := &relaycommon.RelayInfo{ UserGroup: "test-group", UsingGroup: "default", OriginModelName: req.Model, } pd, err := ModelPriceHelperVideo(c, relayInfo) require.NoError(t, err) ctx := estimateVideoRequestContext(c) require.Equal(t, videoBillingModeTextToVideo, ctx.Mode, "images 为空应为文生视频") require.Equal(t, 960, ctx.Width) require.Equal(t, 540, ctx.Height) require.Equal(t, 5, ctx.DurationSec) sec := 5 if ctx.DurationSec > 0 { sec = ctx.DurationSec } want := int(common.QuotaPerUnit * float64(sec) * pricePerSecUSD * pd.GroupRatioInfo.GroupRatio) require.Equal(t, want, pd.Quota, "应与 ceil(秒)×单价×QuotaPerUnit×group 一致(渠道 id=0 无折扣)") t.Logf("estimate ctx: mode=%s WxH=%dx%d durationSec=%d fps(estimate)=%d inputTextTokens≈%d", ctx.Mode, ctx.Width, ctx.Height, ctx.DurationSec, ctx.FPS, ctx.InputTextTokens) t.Logf("preconsume quota=%d (QuotaPerUnit=%.0f groupRatio=%.4f)", pd.Quota, common.QuotaPerUnit, pd.GroupRatioInfo.GroupRatio) } func TestPickAudioPriceByResolution_RoundsUpPseudo540p(t *testing.T) { rows := []ratio_setting.VideoResolutionAudioPriceRule{ {Resolution: "480p", HasAudio: false, Price: 9}, {Resolution: "540p", HasAudio: false, Price: 8}, {Resolution: "720p", HasAudio: false, Price: 10}, } price, ok := pickAudioPriceByResolution(videoEstimateContext{ Width: 864, Height: 496, }, false, rows) require.True(t, ok) require.Equal(t, 8.0, price, "864x496 exceeds 480p bounds, so it should round up to 540p") } func TestPickAudioPriceByResolution_UsesTargetAspectRatio(t *testing.T) { rows := []ratio_setting.VideoResolutionAudioPriceRule{ {Resolution: "480p", HasAudio: false, Price: 9}, {Resolution: "540p", HasAudio: false, Price: 8}, {Resolution: "720p", HasAudio: false, Price: 10}, } price, ok := pickAudioPriceByResolution(videoEstimateContext{ Width: 1120, Height: 480, }, false, rows) require.True(t, ok) require.Equal(t, 9.0, price, "21:9 1120x480 should fit the 480p tier for that aspect ratio") }