"""edit_template 剪辑模板实体单测.""" from datetime import datetime, timezone import pytest from domain.edit_template import EditTemplate, EditTemplateStatus from domain.editing_mode import EditingMode # ── EditTemplateStatus 枚举 ────────────────────────────────────────────────── class TestEditTemplateStatus: """EditTemplateStatus 枚举""" def test_enum_values(self): assert EditTemplateStatus.ACTIVE.value == "active" assert EditTemplateStatus.INACTIVE.value == "inactive" def test_is_str_enum(self): assert isinstance(EditTemplateStatus.ACTIVE, str) assert EditTemplateStatus.ACTIVE == "active" def test_from_string(self): assert EditTemplateStatus("active") == EditTemplateStatus.ACTIVE assert EditTemplateStatus("inactive") == EditTemplateStatus.INACTIVE def test_invalid_raises(self): with pytest.raises(ValueError): EditTemplateStatus("deleted") # ── EditTemplate.create 工厂方法 ──────────────────────────────────────────── class TestEditTemplateCreate: """EditTemplate.create 工厂方法""" def test_minimal_create(self): t = EditTemplate.create("测试模板") assert t.id is not None assert len(t.id) == 32 # uuid4 hex assert t.name == "测试模板" assert t.description == "" assert t.template_type == "default" assert t.editing_mode == "one_take" assert t.config == {} assert t.preview_url == "" assert t.sort_weight == 0 assert t.status == EditTemplateStatus.ACTIVE assert t.version == 1 def test_unique_ids(self): t1 = EditTemplate.create("模板A") t2 = EditTemplate.create("模板B") assert t1.id != t2.id def test_custom_fields(self): t = EditTemplate.create( "自定义模板", description="这是一个自定义模板", template_type="story", editing_mode="one_take", config={"key": "value"}, preview_url="https://example.com/preview.mp4", sort_weight=100, status=EditTemplateStatus.INACTIVE, version=2, ) assert t.name == "自定义模板" assert t.description == "这是一个自定义模板" assert t.template_type == "story" assert t.editing_mode == "one_take" assert t.config == {"key": "value"} assert t.preview_url == "https://example.com/preview.mp4" assert t.sort_weight == 100 assert t.status == EditTemplateStatus.INACTIVE assert t.version == 2 def test_name_stripped(self): t = EditTemplate.create(" 带空格的模板 ") assert t.name == "带空格的模板" def test_empty_name_raises(self): with pytest.raises(ValueError, match="名称"): EditTemplate.create("") def test_whitespace_only_name_raises(self): with pytest.raises(ValueError): EditTemplate.create(" ") def test_invalid_editing_mode_raises(self): with pytest.raises(ValueError, match="editing_mode"): EditTemplate.create("测试", editing_mode="invalid_mode") def test_empty_editing_mode_falls_back_to_default(self): t = EditTemplate.create("测试", editing_mode="") assert t.editing_mode == "one_take" def test_whitespace_editing_mode_falls_back(self): t = EditTemplate.create("测试", editing_mode=" ") assert t.editing_mode == "one_take" def test_editing_mode_stripped(self): t = EditTemplate.create("测试", editing_mode=" one_take ") assert t.editing_mode == "one_take" def test_description_stripped(self): t = EditTemplate.create("测试", description=" 描述 ") assert t.description == "描述" def test_template_type_stripped(self): t = EditTemplate.create("测试", template_type=" vlog ") assert t.template_type == "vlog" def test_empty_template_type_falls_back(self): t = EditTemplate.create("测试", template_type="") assert t.template_type == "default" def test_none_config_becomes_empty_dict(self): t = EditTemplate.create("测试", config=None) assert t.config == {} assert isinstance(t.config, dict) def test_preview_url_stripped(self): t = EditTemplate.create("测试", preview_url=" https://x.com/a.mp4 ") assert t.preview_url == "https://x.com/a.mp4" def test_timestamps_are_utc(self): t = EditTemplate.create("测试") assert t.created_at.tzinfo is not None assert t.updated_at.tzinfo is not None def test_created_at_equals_updated_at_on_create(self): t = EditTemplate.create("测试") # 创建时两个时间应该非常接近 diff = abs((t.updated_at - t.created_at).total_seconds()) assert diff < 1.0 # ── 状态操作 ───────────────────────────────────────────────────────────────── class TestEditTemplateStatusOperations: """EditTemplate 状态操作""" def test_activate_sets_active(self): t = EditTemplate.create("测试", status=EditTemplateStatus.INACTIVE) t.activate() assert t.status == EditTemplateStatus.ACTIVE assert t.is_active is True def test_deactivate_sets_inactive(self): t = EditTemplate.create("测试") t.deactivate() assert t.status == EditTemplateStatus.INACTIVE assert t.is_active is False def test_is_active_true(self): t = EditTemplate.create("测试") assert t.is_active is True def test_is_active_false(self): t = EditTemplate.create("测试", status=EditTemplateStatus.INACTIVE) assert t.is_active is False def test_activate_updates_updated_at(self): t = EditTemplate.create("测试", status=EditTemplateStatus.INACTIVE) old_updated = t.updated_at t.activate() assert t.updated_at >= old_updated def test_deactivate_updates_updated_at(self): t = EditTemplate.create("测试") old_updated = t.updated_at t.deactivate() assert t.updated_at >= old_updated # ── 版本操作 ───────────────────────────────────────────────────────────────── class TestEditTemplateVersion: """EditTemplate 版本操作""" def test_bump_version_increments(self): t = EditTemplate.create("测试") assert t.version == 1 t.bump_version() assert t.version == 2 def test_bump_version_multiple(self): t = EditTemplate.create("测试", version=5) t.bump_version() t.bump_version() t.bump_version() assert t.version == 8 def test_bump_version_updates_updated_at(self): t = EditTemplate.create("测试") old_updated = t.updated_at t.bump_version() assert t.updated_at >= old_updated # ── dataclass 基础特性 ─────────────────────────────────────────────────────── class TestEditTemplateBasics: """EditTemplate 基础特性""" def test_slots_no_extra_attrs(self): t = EditTemplate.create("测试") with pytest.raises(AttributeError): t.nonexistent_field = "value" def test_direct_construction_minimal(self): # 最小构造:仅必填字段 + 状态,其余走默认值 t = EditTemplate( id="custom_id", name="直接构造", status=EditTemplateStatus.ACTIVE, ) assert t.id == "custom_id" assert t.name == "直接构造" assert t.status == EditTemplateStatus.ACTIVE # 默认值检查 assert t.description == "" assert t.config == {} assert t.version == 1 assert t.editing_mode == EditingMode.ONE_TAKE.value assert isinstance(t.created_at, datetime) assert isinstance(t.updated_at, datetime) def test_direct_construction_full(self): # 完整构造:所有字段都传 now = datetime(2025, 1, 1, tzinfo=timezone.utc) t = EditTemplate( id="full_id", name="完整构造", description="测试描述", template_type="custom", editing_mode=EditingMode.PIP.value, config={"key": "value"}, preview_url="https://example.com/preview.jpg", sort_weight=100, status=EditTemplateStatus.INACTIVE, version=3, created_at=now, updated_at=now, ) assert t.id == "full_id" assert t.name == "完整构造" assert t.description == "测试描述" assert t.template_type == "custom" assert t.editing_mode == EditingMode.PIP.value assert t.config == {"key": "value"} assert t.preview_url == "https://example.com/preview.jpg" assert t.sort_weight == 100 assert t.status == EditTemplateStatus.INACTIVE assert t.version == 3 assert t.created_at == now assert t.updated_at == now def test_config_is_independent(self): # 不同实例的 config 应该是独立的 dict t1 = EditTemplate.create("模板1") t2 = EditTemplate.create("模板2") t1.config["key"] = "value" assert "key" not in t2.config def test_equality(self): # 两个不同实例即使内容相同也不等(id不同) t1 = EditTemplate.create("同名模板") t2 = EditTemplate.create("同名模板") assert t1 != t2 def test_same_id_equal(self): now = datetime.now(timezone.utc) t1 = EditTemplate(id="same", name="同名", created_at=now, updated_at=now) t2 = EditTemplate(id="same", name="同名", created_at=now, updated_at=now) assert t1 == t2