"""Unit tests for Phase 8 EditTemplate and EditPlan domain entities + repositories.""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) import logging from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from packages.adapters.sqlalchemy_impl.edit_plan_repository import ( SQLAlchemyEditPlanRepository, ) from packages.adapters.sqlalchemy_impl.edit_template_repository import ( SQLAlchemyEditTemplateRepository, ) from packages.adapters.sqlalchemy_impl.models import Base from packages.domain.edit_plan import EditPlan, EditPlanStatus from packages.domain.edit_template import EditTemplate, EditTemplateStatus logger = logging.getLogger(__name__) # ── EditTemplate 领域实体测试 ────────────────────────────────────────── class TestEditTemplate: def test_create_success(self): t = EditTemplate.create("vlog模板", description="日常vlog", template_type="vlog") assert t.name == "vlog模板" assert t.description == "日常vlog" assert t.template_type == "vlog" assert t.status == EditTemplateStatus.ACTIVE assert t.config == {} assert t.sort_weight == 0 assert t.id # 自动生成 def test_create_strips_whitespace(self): t = EditTemplate.create(" 模板 ") assert t.name == "模板" def test_create_empty_name_raises(self): try: EditTemplate.create(" ") raise AssertionError("应该抛出 ValueError") except ValueError as e: assert "模板名称不能为空" in str(e) def test_activate_deactivate(self): t = EditTemplate.create("test") assert t.is_active is True t.deactivate() assert t.is_active is False assert t.status == EditTemplateStatus.INACTIVE t.activate() assert t.is_active is True assert t.status == EditTemplateStatus.ACTIVE # ── EditPlan 领域实体测试 ────────────────────────────────────────────── class TestEditPlan: def test_create_success(self): p = EditPlan.create("tpl-1", "我的计划", config={"bgm": "happy"}) assert p.template_id == "tpl-1" assert p.name == "我的计划" assert p.status == EditPlanStatus.DRAFT assert p.config == {"bgm": "happy"} assert p.total_duration == 0.0 def test_create_empty_name_raises(self): try: EditPlan.create("tpl-1", " ") raise AssertionError("应该抛出 ValueError") except ValueError as e: assert "计划名称不能为空" in str(e) def test_create_empty_template_id_raises(self): try: EditPlan.create(" ", "test") raise AssertionError("应该抛出 ValueError") except ValueError as e: assert "template_id 不能为空" in str(e) def test_status_transitions_happy_path(self): p = EditPlan.create("tpl-1", "test") assert p.status == EditPlanStatus.DRAFT p.start_editing() assert p.status == EditPlanStatus.EDITING p.start_rendering() assert p.status == EditPlanStatus.RENDERING p.mark_completed() assert p.status == EditPlanStatus.COMPLETED def test_status_transitions_failure_path(self): p = EditPlan.create("tpl-1", "test") p.start_editing() p.start_rendering() p.mark_failed() assert p.status == EditPlanStatus.FAILED p.reset_to_draft() assert p.status == EditPlanStatus.DRAFT def test_invalid_transition_raises(self): p = EditPlan.create("tpl-1", "test") try: p.start_rendering() # draft → rendering 不合法 raise AssertionError("应该抛出 ValueError") except ValueError as e: logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True) def test_mark_completed_from_non_rendering_raises(self): p = EditPlan.create("tpl-1", "test") try: p.mark_completed() # draft → completed 不合法 raise AssertionError("应该抛出 ValueError") except ValueError as e: logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True) def test_reset_from_non_failed_raises(self): p = EditPlan.create("tpl-1", "test") try: p.reset_to_draft() # draft → draft 不合法 raise AssertionError("应该抛出 ValueError") except ValueError as e: logger.warning(f"Operation failed in tests/unit/test_phase8_edit_models.py: {e}", exc_info=True) # ── Repository 集成测试(内存 SQLite) ───────────────────────────────── class TestRepositories: def _make_session(self): engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(engine) SessionLocal = sessionmaker(bind=engine) return SessionLocal() def test_edit_template_repository_crud(self): session = self._make_session() try: repo = SQLAlchemyEditTemplateRepository(session) t = EditTemplate.create("模板A", description="测试模板") repo.create(t) fetched = repo.get(t.id) assert fetched is not None assert fetched.name == "模板A" assert fetched.description == "测试模板" # list active = repo.list_active() assert len(active) == 1 assert active[0].id == t.id # update fetched.deactivate() repo.update(fetched) assert repo.get(t.id).status == EditTemplateStatus.INACTIVE assert len(repo.list_active()) == 0 # count assert repo.count() == 1 # delete assert repo.delete(t.id) is True assert repo.get(t.id) is None assert repo.count() == 0 finally: session.close() def test_edit_plan_repository_crud(self): session = self._make_session() try: repo = SQLAlchemyEditPlanRepository(session) p = EditPlan.create("tpl-1", "计划A", config={"key": "val"}) repo.create(p) fetched = repo.get(p.id) assert fetched is not None assert fetched.name == "计划A" assert fetched.config == {"key": "val"} assert fetched.status == EditPlanStatus.DRAFT # list_by_template plans = repo.list_by_template("tpl-1") assert len(plans) == 1 # update status fetched.start_editing() repo.update(fetched) editing = repo.list_by_template("tpl-1", status=EditPlanStatus.EDITING) assert len(editing) == 1 # count assert repo.count() == 1 assert repo.count(status=EditPlanStatus.EDITING) == 1 assert repo.count(status=EditPlanStatus.DRAFT) == 0 # delete assert repo.delete(p.id) is True assert repo.get(p.id) is None finally: session.close()