""" 积分 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")