/** * 1080P 分辨率支持 — 前端单元测试 * * 验证用户三原则: * 1. 不兜底/静默降级 — setModel/setResolution 拦截 Fast+1080P 组合 * 2. 钱的计算绝对准确 — 前端 estimatedTokens 公式与后端一致 * 3. 不隐藏 bug — 无 || '720p' 兜底 */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { useInputBarStore } from '../../src/store/inputBar'; // Mock Toast 避免真实 DOM 调用 vi.mock('../../src/components/Toast', () => ({ showToast: vi.fn(), })); describe('1080P — Store 分辨率状态', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('默认分辨率是 720p', () => { expect(useInputBarStore.getState().resolution).toBe('720p'); }); it('setResolution 能设为 480p / 720p / 1080p', () => { const { setResolution } = useInputBarStore.getState(); setResolution('480p'); expect(useInputBarStore.getState().resolution).toBe('480p'); setResolution('1080p'); expect(useInputBarStore.getState().resolution).toBe('1080p'); setResolution('720p'); expect(useInputBarStore.getState().resolution).toBe('720p'); }); it('reset 把分辨率恢复为 720p', () => { const { setResolution, reset } = useInputBarStore.getState(); setResolution('1080p'); reset(); expect(useInputBarStore.getState().resolution).toBe('720p'); }); }); describe('1080P — 双向拦截(原则 1:不静默降级)', () => { beforeEach(() => { useInputBarStore.getState().reset(); }); it('1080P 下切 Fast 模型应被阻止,resolution 不变,model 也不变', () => { const { setResolution, setModel } = useInputBarStore.getState(); setResolution('1080p'); setModel('seedance_2.0_fast'); // 拦截成功:model 保持原值,resolution 不变(不降级为 720p) const state = useInputBarStore.getState(); expect(state.model).toBe('seedance_2.0'); expect(state.resolution).toBe('1080p'); }); it('Fast 模式下切 1080P 分辨率应被阻止,model 不变,resolution 不变', () => { const { setModel, setResolution } = useInputBarStore.getState(); setModel('seedance_2.0_fast'); setResolution('1080p'); const state = useInputBarStore.getState(); expect(state.model).toBe('seedance_2.0_fast'); expect(state.resolution).toBe('720p'); // 仍是默认 720p,没被改到 1080p }); it('AirDrama 下切 1080P 正常生效', () => { const { setResolution } = useInputBarStore.getState(); setResolution('1080p'); expect(useInputBarStore.getState().resolution).toBe('1080p'); }); it('1080P 下切回 AirDrama 正常生效(同模型不拦截)', () => { const { setModel, setResolution } = useInputBarStore.getState(); setResolution('1080p'); setModel('seedance_2.0'); expect(useInputBarStore.getState().model).toBe('seedance_2.0'); expect(useInputBarStore.getState().resolution).toBe('1080p'); }); it('Fast 下切 480p/720p 正常生效(不是 1080p 不拦截)', () => { const { setModel, setResolution } = useInputBarStore.getState(); setModel('seedance_2.0_fast'); setResolution('480p'); expect(useInputBarStore.getState().resolution).toBe('480p'); setResolution('720p'); expect(useInputBarStore.getState().resolution).toBe('720p'); }); }); describe('1080P — 官方像素值(与后端 RESOLUTION_MAP 对齐)', () => { // 这里硬编码官方文档的像素表,作为前端契约测试 // 如果 Toolbar.tsx 的 RESOLUTION_PIXELS 改动,这些测试应该跟着更新 // 对应 backend/utils/billing.py::RESOLUTION_MAP const EXPECTED_PIXELS = { '480p': { '16:9': [864, 496], '9:16': [496, 864], '4:3': [752, 560], '1:1': [640, 640], '3:4': [560, 752], '21:9': [992, 432], }, '720p': { '16:9': [1280, 720], '9:16': [720, 1280], '4:3': [1112, 834], '1:1': [960, 960], '3:4': [834, 1112], '21:9': [1470, 630], }, '1080p': { '16:9': [1920, 1080], '9:16': [1080, 1920], '4:3': [1664, 1248], '1:1': [1440, 1440], '3:4': [1248, 1664], '21:9': [2206, 946], // 关键:不是 2176×928(seedance 1.0 值) }, }; // estimate_tokens 官方公式实现(对齐前端 Toolbar 和后端 billing.py) function estimateTokens(w: number, h: number, duration: number, inputVideoDuration = 0) { return Math.round((w * h * 24 * (duration + inputVideoDuration)) / 1024); } it('1080P 5s 16:9 无输入视频 = 243000 tokens', () => { const [w, h] = EXPECTED_PIXELS['1080p']['16:9']; expect(estimateTokens(w, h, 5)).toBe(243000); }); it('1080P 5s 16:9 含 2s 输入视频 = 340200 tokens(纯公式,不修正到最低 437400)', () => { const [w, h] = EXPECTED_PIXELS['1080p']['16:9']; expect(estimateTokens(w, h, 5, 2)).toBe(340200); }); it('720P 5s 16:9 无输入视频 = 108000 tokens', () => { const [w, h] = EXPECTED_PIXELS['720p']['16:9']; expect(estimateTokens(w, h, 5)).toBe(108000); }); it('1080P 21:9 像素 = 2206×946(不是 seedance 1.0 的 2176×928)', () => { expect(EXPECTED_PIXELS['1080p']['21:9']).toEqual([2206, 946]); }); it('价格示例:1080P 5s 16:9 × 51 元/百万 = 12.39 元', () => { const tokens = 243000; const price = 51; const cost = (tokens * price) / 1_000_000; expect(cost.toFixed(2)).toBe('12.39'); }); it('价格示例:720P 5s 16:9 × 46 元/百万 = 4.97 元', () => { const tokens = 108000; const price = 46; const cost = (tokens * price) / 1_000_000; expect(cost.toFixed(2)).toBe('4.97'); }); });