Files
xiaoxia-saas/tests/unit/test_unify_template_segments.py
CI Bot 148b41a5d6
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 0s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m16s
AI Code Review / AI Code Review (pull_request) Successful in 2m46s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m4s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m12s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m5s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 11m30s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 14m33s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 18m58s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 36m55s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 48m14s
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 2m46s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 5m39s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 376h46m44s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 376h48m13s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 376h48m15s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 376h49m58s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 376h50m0s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 376h50m2s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 377h26m46s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 377h26m48s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 377h26m50s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 377h27m30s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 377h39m34s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 377h39m36s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 377h39m38s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 377h41m25s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 377h41m31s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 377h41m27s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 377h41m33s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 377h41m33s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 377h46m38s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 378h15m43s
style: auto-format with black + isort + prettier [skip ci-format-check]
2026-08-31 13:32:33 +00:00

210 lines
6.8 KiB
Python
Raw Permalink 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.
"""统一模板 segments 数据源单元测试。
验证 template_repository 从 template_clip_configs 读取 segments,
写入走 template_clip_configs,回退兼容 template_segments。
"""
from __future__ import annotations
import sys
import uuid
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from packages.adapters.sqlalchemy_impl.models import (
Base,
TemplateClipConfigModel,
TemplateModel,
TemplateSegmentModel,
)
from packages.adapters.sqlalchemy_impl.template_repository import (
SQLAlchemyTemplateRepository,
)
from packages.domain.template import Template, TemplateSegment
@pytest.fixture()
def session():
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
s = Session()
try:
yield s
finally:
s.close()
@pytest.fixture()
def repo(session):
return SQLAlchemyTemplateRepository(session)
def _make_template(template_id=None, user_id="u1", name="测试模板", mode="one_take"):
tid = template_id or str(uuid.uuid4())
return Template(
id=tid,
user_id=user_id,
name=name,
mode=mode,
category="",
tags=[],
estimated_duration=30.0,
is_active=True,
segments=[],
)
def _make_segment(template_id, order=1, material_type=None):
return TemplateSegment(
id=str(uuid.uuid4()),
template_id=template_id,
segment_order=order,
duration_min=5.0,
duration_max=10.0,
material_type=material_type,
)
class TestCreateSegments:
def test_writes_to_clip_configs(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1)
repo.create_segments([seg])
clips = session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.template_id == tpl.id).all()
assert len(clips) == 1
assert clips[0].clip_type == "main"
assert clips[0].order == 1
assert clips[0].min_duration == 5.0
def test_material_type_stored_in_config(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1, material_type="voiceover")
repo.create_segments([seg])
clip = session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.template_id == tpl.id).first()
assert clip.config["material_type"] == "voiceover"
class TestListSegments:
def test_reads_from_clip_configs(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1, material_type="voiceover")
repo.create_segments([seg])
result = repo.list_segments(tpl.id)
assert len(result) == 1
assert result[0].material_type == "voiceover"
def test_fallback_to_old_table(self, repo, session):
tpl = _make_template()
repo.create(tpl)
old = TemplateSegmentModel(
id=str(uuid.uuid4()),
template_id=tpl.id,
segment_order=1,
duration_min=3.0,
duration_max=8.0,
material_type="场景",
)
session.add(old)
session.commit()
result = repo.list_segments(tpl.id)
assert len(result) == 1
assert result[0].material_type == "场景"
def test_clip_configs_takes_priority(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1)
repo.create_segments([seg])
old = TemplateSegmentModel(
id=str(uuid.uuid4()), template_id=tpl.id, segment_order=1, duration_min=1.0, duration_max=2.0
)
session.add(old)
session.commit()
result = repo.list_segments(tpl.id)
assert len(result) == 1
assert result[0].duration_min == 5.0
class TestListByUser:
def test_batch_loads_from_clip_configs(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1, material_type="人物")
repo.create_segments([seg])
result = repo.list_by_user("u1")
assert len(result) == 1
assert len(result[0].segments) == 1
assert result[0].segments[0].material_type == "人物"
def test_fallback_for_old_data(self, repo, session):
tpl = _make_template()
repo.create(tpl)
old = TemplateSegmentModel(
id=str(uuid.uuid4()), template_id=tpl.id, segment_order=1, duration_min=2.0, duration_max=6.0
)
session.add(old)
session.commit()
result = repo.list_by_user("u1")
assert len(result) == 1
assert len(result[0].segments) == 1
assert result[0].segments[0].duration_min == 2.0
class TestCopyTemplate:
def test_copy_writes_to_clip_configs(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1, material_type="voiceover")
repo.create_segments([seg])
copied = repo.copy_template(tpl.id, "u1", "副本模板")
assert copied.id != tpl.id
clips = session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.template_id == copied.id).all()
assert len(clips) == 1
assert clips[0].config["material_type"] == "voiceover"
def test_copy_empty_segments(self, repo, session):
tpl = _make_template()
repo.create(tpl)
copied = repo.copy_template(tpl.id, "u1", "空副本")
assert len(copied.segments) == 0
class TestDelete:
def test_delete_cleans_both_tables(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1)
repo.create_segments([seg])
old = TemplateSegmentModel(
id=str(uuid.uuid4()), template_id=tpl.id, segment_order=1, duration_min=1.0, duration_max=2.0
)
session.add(old)
session.commit()
repo.delete(tpl.id, "u1")
c1 = session.query(TemplateClipConfigModel).filter(TemplateClipConfigModel.template_id == tpl.id).count()
c2 = session.query(TemplateSegmentModel).filter(TemplateSegmentModel.template_id == tpl.id).count()
assert c1 == 0
assert c2 == 0
def test_delete_segments_by_template(self, repo, session):
tpl = _make_template()
repo.create(tpl)
seg = _make_segment(tpl.id, order=1)
repo.create_segments([seg])
old = TemplateSegmentModel(
id=str(uuid.uuid4()), template_id=tpl.id, segment_order=2, duration_min=1.0, duration_max=2.0
)
session.add(old)
session.commit()
count = repo.delete_segments_by_template(tpl.id)
assert count == 2