96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
"""
|
|
积分 Service 单元测试
|
|
=====================
|
|
|
|
不依赖数据库,仅测试配置加载、计费计算、充值档位等纯函数逻辑。
|
|
"""
|
|
|
|
import math
|
|
|
|
import pytest
|
|
|
|
from app.services import point_service as ps
|
|
|
|
|
|
class TestPointsConfig:
|
|
"""积分配置加载"""
|
|
|
|
def test_points_config_loaded(self) -> None:
|
|
assert "script" in ps.POINTS_CONFIG
|
|
assert "tts" in ps.POINTS_CONFIG
|
|
assert "video" in ps.POINTS_CONFIG
|
|
|
|
def test_get_chargeable_source_types(self) -> None:
|
|
types = ps.get_chargeable_source_types()
|
|
assert "script" in types
|
|
assert "tts" in types
|
|
# 免费业务不应出现在扣费列表
|
|
assert "caption" not in types
|
|
|
|
def test_get_recharge_options_returns_list(self) -> None:
|
|
options = ps.get_recharge_options()
|
|
assert isinstance(options, list)
|
|
if options:
|
|
assert "points" in options[0]
|
|
|
|
|
|
class TestCalculateCost:
|
|
"""后置计费计算"""
|
|
|
|
def test_fixed_cost(self) -> None:
|
|
assert ps._calculate_cost("script") == 5
|
|
assert ps._calculate_cost("polish") == 1
|
|
assert ps._calculate_cost("voice_clone") == 200
|
|
|
|
def test_free_cost(self) -> None:
|
|
free_types = [
|
|
key
|
|
for key, cfg in ps.POINTS_CONFIG.items()
|
|
if not key.startswith("_") and cfg.get("mode") == "free"
|
|
]
|
|
for source_type in free_types:
|
|
assert ps._calculate_cost(source_type) == 0
|
|
|
|
def test_unknown_source_type(self) -> None:
|
|
with pytest.raises(ValueError, match="未知的消费类型"):
|
|
ps._calculate_cost("not_exists")
|
|
|
|
def test_tts_duration_cost(self) -> None:
|
|
# tts: divisor=5,按 5 秒为单位计费
|
|
assert ps._calculate_cost("tts", {"seconds": 4}) == 1
|
|
assert ps._calculate_cost("tts", {"seconds": 5}) == 1
|
|
assert ps._calculate_cost("tts", {"seconds": 6}) == 2
|
|
assert ps._calculate_cost("tts", {"seconds": 5.1}) == 2
|
|
|
|
def test_video_duration_cost(self) -> None:
|
|
# video: multiplier=1,按秒向上取整
|
|
assert ps._calculate_cost("video", {"seconds": 10}) == 10
|
|
assert ps._calculate_cost("video", {"seconds": 10.2}) == 11
|
|
|
|
|
|
class TestEstimateMaxCost:
|
|
"""预估上限计算"""
|
|
|
|
def test_tts_estimate_by_char_count(self) -> None:
|
|
cfg = ps.POINTS_CONFIG["tts"]
|
|
seconds_per_char = cfg["estimation"]["seconds_per_char"]
|
|
char_count = 100
|
|
estimated_seconds = char_count * seconds_per_char
|
|
expected = max(
|
|
cfg["min_points"],
|
|
math.ceil(estimated_seconds / cfg["divisor"]),
|
|
)
|
|
result = ps._estimate_max_cost("tts", {"char_count": char_count})
|
|
assert result == expected
|
|
|
|
def test_video_estimate_by_input_seconds(self) -> None:
|
|
with pytest.raises(ValueError, match="input_seconds"):
|
|
ps._estimate_max_cost("video", {})
|
|
|
|
assert ps._estimate_max_cost("video", {"input_seconds": 10}) == 10
|
|
assert ps._estimate_max_cost("video", {"input_seconds": 10.2}) == 11
|
|
|
|
def test_unknown_source_type(self) -> None:
|
|
with pytest.raises(ValueError, match="未知的消费类型"):
|
|
ps._estimate_max_cost("not_exists")
|