Files
xiaoxia-saas/tests/unit/domain/test_preset_bgm.py
T
xiaoxia c2329f3f98
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
test(wave189): preset_bgm 预设BGM库 +44测 (#1151)
2026-07-30 00:24:10 +08:00

300 lines
10 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""preset_bgm 预设BGM库单测."""
from dataclasses import FrozenInstanceError
import pytest
from domain.preset_bgm import (
BGM_STYLES,
PRESET_BGM_LIBRARY,
PresetBGM,
get_preset_bgm,
list_preset_bgm_by_style,
search_preset_bgm,
)
# ── PresetBGM dataclass ──────────────────────────────────────────────────────
class TestPresetBGM:
"""PresetBGM dataclass"""
def test_minimal_creation(self):
b = PresetBGM(id="test_001", name="测试音乐", style="upbeat", duration=60.0)
assert b.id == "test_001"
assert b.name == "测试音乐"
assert b.style == "upbeat"
assert b.duration == 60.0
assert b.artist == ""
assert b.description == ""
assert b.tags == []
assert b.audio_url == ""
def test_full_creation(self):
b = PresetBGM(
id="bgm_001",
name="阳光清晨",
style="upbeat",
duration=120.5,
artist="音乐人A",
description="轻快明亮的吉他",
tags=["轻快", "阳光"],
audio_url="https://cdn.example.com/bgm.mp3",
)
assert b.id == "bgm_001"
assert b.artist == "音乐人A"
assert b.description == "轻快明亮的吉他"
assert b.tags == ["轻快", "阳光"]
assert b.audio_url == "https://cdn.example.com/bgm.mp3"
def test_frozen_immutable(self):
b = PresetBGM(id="test", name="Test", style="relax", duration=100.0)
with pytest.raises(FrozenInstanceError):
b.name = "NewName"
def test_equality(self):
b1 = PresetBGM(id="same", name="同名", style="upbeat", duration=60.0)
b2 = PresetBGM(id="same", name="同名", style="upbeat", duration=60.0)
assert b1 == b2
def test_inequality(self):
b1 = PresetBGM(id="a", name="A", style="upbeat", duration=60.0)
b2 = PresetBGM(id="b", name="B", style="relax", duration=90.0)
assert b1 != b2
def test_not_hashable_due_to_list_tags(self):
# 包含 list 字段(tags)的 frozen dataclass 不可哈希
b = PresetBGM(id="test", name="Test", style="tech", duration=60.0)
with pytest.raises(TypeError):
hash(b)
# ── PRESET_BGM_LIBRARY 清单 ──────────────────────────────────────────────────
class TestPresetBGMLibrary:
"""预设BGM库清单"""
def test_not_empty(self):
assert len(PRESET_BGM_LIBRARY) > 0
def test_all_are_preset_bgm(self):
for bgm in PRESET_BGM_LIBRARY:
assert isinstance(bgm, PresetBGM)
def test_unique_ids(self):
ids = [b.id for b in PRESET_BGM_LIBRARY]
assert len(ids) == len(set(ids))
def test_all_have_required_fields(self):
for bgm in PRESET_BGM_LIBRARY:
assert bgm.id != ""
assert bgm.name != ""
assert bgm.style != ""
assert bgm.duration > 0
def test_upbeat_style_count(self):
upbeats = [b for b in PRESET_BGM_LIBRARY if b.style == "upbeat"]
assert len(upbeats) >= 3
def test_relax_style_count(self):
relax = [b for b in PRESET_BGM_LIBRARY if b.style == "relax"]
assert len(relax) >= 3
def test_tech_style_count(self):
tech = [b for b in PRESET_BGM_LIBRARY if b.style == "tech"]
assert len(tech) >= 2
def test_commerce_style_count(self):
commerce = [b for b in PRESET_BGM_LIBRARY if b.style == "commerce"]
assert len(commerce) >= 2
def test_sunny_morning_preset(self):
b = next(b for b in PRESET_BGM_LIBRARY if b.id == "bgm_upbeat_001")
assert b.name == "阳光清晨"
assert b.style == "upbeat"
assert b.duration == 120.0
assert "吉他" in b.description
assert "vlog" in b.tags
def test_quiet_time_preset(self):
b = next(b for b in PRESET_BGM_LIBRARY if b.id == "bgm_relax_001")
assert b.name == "静谧时光"
assert b.style == "relax"
assert b.duration == 180.0
def test_future_tech_preset(self):
b = next(b for b in PRESET_BGM_LIBRARY if b.id == "bgm_tech_001")
assert b.name == "未来科技"
assert b.style == "tech"
def test_all_durations_positive(self):
for bgm in PRESET_BGM_LIBRARY:
assert bgm.duration > 0
def test_all_tags_are_lists(self):
for bgm in PRESET_BGM_LIBRARY:
assert isinstance(bgm.tags, list)
# ── BGM_STYLES 风格字典 ─────────────────────────────────────────────────────
class TestBGMStyles:
"""BGM_STYLES 风格分类字典"""
def test_styles_exist(self):
assert "upbeat" in BGM_STYLES
assert "relax" in BGM_STYLES
assert "tech" in BGM_STYLES
assert "commerce" in BGM_STYLES
assert "emotional" in BGM_STYLES
assert "cinematic" in BGM_STYLES
def test_style_names_chinese(self):
assert BGM_STYLES["upbeat"] == "轻快"
assert BGM_STYLES["relax"] == "治愈"
assert BGM_STYLES["tech"] == "科技"
assert BGM_STYLES["commerce"] == "电商"
assert BGM_STYLES["emotional"] == "情感"
assert BGM_STYLES["cinematic"] == "电影"
def test_library_styles_are_defined(self):
# 库中的所有风格都应该在 BGM_STYLES 中有定义
styles_in_library = {b.style for b in PRESET_BGM_LIBRARY}
for style in styles_in_library:
assert style in BGM_STYLES, f"style {style} not defined in BGM_STYLES"
# ── get_preset_bgm ──────────────────────────────────────────────────────────
class TestGetPresetBGM:
"""get_preset_bgm 函数"""
def test_get_existing(self):
b = get_preset_bgm("bgm_upbeat_001")
assert b is not None
assert b.id == "bgm_upbeat_001"
assert b.name == "阳光清晨"
def test_get_relax(self):
b = get_preset_bgm("bgm_relax_002")
assert b is not None
assert b.style == "relax"
def test_get_nonexistent_returns_none(self):
b = get_preset_bgm("nonexistent_id")
assert b is None
def test_get_empty_string_returns_none(self):
b = get_preset_bgm("")
assert b is None
def test_returns_same_instance(self):
b1 = get_preset_bgm("bgm_upbeat_001")
b2 = get_preset_bgm("bgm_upbeat_001")
assert b1 is b2
# ── list_preset_bgm_by_style ─────────────────────────────────────────────────
class TestListPresetBGMByStyle:
"""list_preset_bgm_by_style 函数"""
def test_upbeat_style(self):
result = list_preset_bgm_by_style("upbeat")
assert len(result) >= 3
for b in result:
assert b.style == "upbeat"
def test_relax_style(self):
result = list_preset_bgm_by_style("relax")
assert len(result) >= 3
for b in result:
assert b.style == "relax"
def test_tech_style(self):
result = list_preset_bgm_by_style("tech")
assert len(result) >= 2
def test_commerce_style(self):
result = list_preset_bgm_by_style("commerce")
assert len(result) >= 2
def test_unknown_style_empty(self):
result = list_preset_bgm_by_style("nonexistent_style")
assert len(result) == 0
def test_empty_string_empty(self):
result = list_preset_bgm_by_style("")
assert len(result) == 0
def test_returns_new_list(self):
# 修改返回值不应影响原始列表
result = list_preset_bgm_by_style("upbeat")
result.clear()
assert len(list_preset_bgm_by_style("upbeat")) >= 3
# ── search_preset_bgm ────────────────────────────────────────────────────────
class TestSearchPresetBGM:
"""search_preset_bgm 函数"""
def test_search_by_name(self):
result = search_preset_bgm("阳光")
assert len(result) >= 1
assert any("阳光" in b.name for b in result)
def test_search_by_description(self):
result = search_preset_bgm("钢琴")
assert len(result) >= 1
# 应该匹配描述里有钢琴的
def test_search_by_tag(self):
result = search_preset_bgm("vlog")
assert len(result) >= 1
assert any("vlog" in b.tags for b in result)
def test_search_tech_keyword(self):
result = search_preset_bgm("科技")
assert len(result) >= 2
def test_search_case_insensitive(self):
r1 = search_preset_bgm("UPBEAT")
r2 = search_preset_bgm("upbeat")
assert len(r1) == len(r2)
def test_search_no_match(self):
result = search_preset_bgm("完全不存在的关键词_xyz123")
assert len(result) == 0
def test_search_empty_string(self):
# 空字符串应该匹配所有(因为 "" in any string 是 True
result = search_preset_bgm("")
assert len(result) == len(PRESET_BGM_LIBRARY)
def test_search_electronic(self):
result = search_preset_bgm("电子")
assert len(result) >= 2
def test_order_preserved(self):
# 搜索结果应该保持原列表顺序
result = search_preset_bgm("bgm")
ids = [b.id for b in result]
all_ids = [b.id for b in PRESET_BGM_LIBRARY]
# 验证相对顺序
pos_in_result = {bgm_id: i for i, bgm_id in enumerate(ids)}
prev_pos = -1
for bgm_id in all_ids:
if bgm_id in pos_in_result:
assert pos_in_result[bgm_id] > prev_pos
prev_pos = pos_in_result[bgm_id]
def test_search_partial_tag_match(self):
# 关键词是标签的子串也能匹配
result = search_preset_bgm("吉他")
assert len(result) >= 1