Files
xiaoxia-saas/tests/unit/test_uuid_field_length.py
用户CI Test abc54ace4e
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 13s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 50s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
fix: expand all UUID fields from varchar(32) to varchar(36)
P0 hotfix: ingest_jobs.library_id (and all other UUID fields across 10
tables) were varchar(32), but standard UUIDs with hyphens are 36 chars,
causing StringDataRightTruncation on insert — mini-program upload was
completely broken.

Changes:
- models.py: All String(32) UUID fields → String(36) across 10 tables
  (projects, edit_templates, edit_plans, template_clip_configs,
   edit_plan_clips, ingest_jobs, classification_jobs, generation_tasks,
   generated_videos, jobs)
- Alembic migration 036: ALTER COLUMN for all affected fields
- Unit tests: 43 tests verifying String(36) on all UUID columns and
  standard UUID construction with hyphens

Fixes: upload/direct/complete StringDataRightTruncation error
2026-07-10 01:23:24 +08:00

205 lines
7.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.
"""UUID 字段长度单元测试 — 验证标准 UUID(带横杠,36字符)可正常插入.
覆盖:
- 所有从 varchar(32) 扩到 varchar(36) 的表
- 插入标准 UUID 格式(带横杠)不报 StringDataRightTruncation
- 字段长度从 32 扩到 36 后 ORM 模型定义正确
"""
from __future__ import annotations
import uuid
import pytest
from packages.adapters.sqlalchemy_impl.models import (
Base,
ClassificationJobModel,
EditPlanClipModel,
EditPlanModel,
EditTemplateModel,
GeneratedVideoModel,
GenerationTaskModel,
IngestJobModel,
JobModel,
ProjectModel,
TemplateClipConfigModel,
)
def _uuid() -> str:
"""生成标准 UUID 字符串(带横杠,36 字符)."""
return str(uuid.uuid4())
class TestUUIDFieldLengthModels:
"""验证 ORM 模型中 UUID 字段定义为 String(36)."""
def _get_column_type_length(self, model_cls, column_name: str) -> int:
"""获取模型列的 String 长度."""
col = model_cls.__table__.columns[column_name]
return col.type.length
# ── ProjectModel ──────────────────────────────────────────────────────────
def test_project_id_is_36(self):
assert self._get_column_type_length(ProjectModel, "id") == 36
def test_project_owner_user_id_is_36(self):
assert self._get_column_type_length(ProjectModel, "owner_user_id") == 36
# ── EditTemplateModel ─────────────────────────────────────────────────────
def test_edit_template_id_is_36(self):
assert self._get_column_type_length(EditTemplateModel, "id") == 36
# ── EditPlanModel ─────────────────────────────────────────────────────────
@pytest.mark.parametrize(
"col",
["id", "template_id", "source_edit_plan_id", "project_id", "created_by_user_id"],
)
def test_edit_plan_uuid_fields_are_36(self, col):
assert self._get_column_type_length(EditPlanModel, col) == 36
# ── TemplateClipConfigModel ───────────────────────────────────────────────
@pytest.mark.parametrize("col", ["id", "template_id"])
def test_template_clip_config_uuid_fields_are_36(self, col):
assert self._get_column_type_length(TemplateClipConfigModel, col) == 36
# ── EditPlanClipModel ─────────────────────────────────────────────────────
@pytest.mark.parametrize(
"col",
["id", "plan_id", "template_clip_config_id", "asset_id"],
)
def test_edit_plan_clip_uuid_fields_are_36(self, col):
assert self._get_column_type_length(EditPlanClipModel, col) == 36
# ── IngestJobModelP0 bug 所在表)─────────────────────────────────────────
@pytest.mark.parametrize(
"col",
["id", "project_id", "library_id", "result_asset_id"],
)
def test_ingest_job_uuid_fields_are_36(self, col):
assert self._get_column_type_length(IngestJobModel, col) == 36
def test_ingest_job_library_id_accepts_standard_uuid(self):
"""P0 回归:library_id 必须能容纳标准 UUID36 字符带横杠)."""
standard_uuid = "550e8400-e29b-41d4-a716-446655440000"
assert len(standard_uuid) == 36
col = IngestJobModel.__table__.columns["library_id"]
assert col.type.length >= 36
# ── ClassificationJobModel ────────────────────────────────────────────────
@pytest.mark.parametrize("col", ["id", "project_id", "asset_id"])
def test_classification_job_uuid_fields_are_36(self, col):
assert self._get_column_type_length(ClassificationJobModel, col) == 36
# ── GenerationTaskModel ───────────────────────────────────────────────────
@pytest.mark.parametrize(
"col",
[
"id",
"project_id",
"strategy_id",
"asset_library_id",
"voice_library_id",
"created_by_user_id",
"source_edit_plan_id",
"batch_id",
],
)
def test_generation_task_uuid_fields_are_36(self, col):
assert self._get_column_type_length(GenerationTaskModel, col) == 36
# ── GeneratedVideoModel ───────────────────────────────────────────────────
@pytest.mark.parametrize(
"col",
["id", "project_id", "generation_task_id", "duplicate_of"],
)
def test_generated_video_uuid_fields_are_36(self, col):
assert self._get_column_type_length(GeneratedVideoModel, col) == 36
# ── JobModel ──────────────────────────────────────────────────────────────
@pytest.mark.parametrize(
"col",
["id", "project_id", "source_id", "created_by_user_id"],
)
def test_job_uuid_fields_are_36(self, col):
assert self._get_column_type_length(JobModel, col) == 36
class TestUUIDInsertWithHyphens:
"""验证标准 UUID(带横杠)可构造 ORM 对象,字段长度足够."""
def test_ingest_job_model_construct_with_standard_uuid(self):
"""P0 回归测试:用标准 UUID 构造 IngestJobModel 不报错."""
std_uuid = _uuid()
assert len(std_uuid) == 36
job = IngestJobModel(
id=_uuid(),
project_id=_uuid(),
library_id=std_uuid, # ← 关键:标准 UUID 带横杠
storage_key="test/file.mp4",
result_asset_id=_uuid(),
)
assert job.library_id == std_uuid
assert len(job.library_id) == 36
def test_project_model_construct_with_standard_uuid(self):
project = ProjectModel(
id=_uuid(),
owner_user_id=_uuid(),
name="test",
)
assert len(project.id) == 36
assert len(project.owner_user_id) == 36
def test_generation_task_model_construct_with_standard_uuid(self):
task = GenerationTaskModel(
id=_uuid(),
project_id=_uuid(),
asset_library_id=_uuid(),
created_by_user_id=_uuid(),
source_edit_plan_id=_uuid(),
batch_id=_uuid(),
)
assert len(task.id) == 36
assert len(task.asset_library_id) == 36
def test_edit_plan_model_construct_with_standard_uuid(self):
plan = EditPlanModel(
id=_uuid(),
template_id=_uuid(),
source_edit_plan_id=_uuid(),
project_id=_uuid(),
created_by_user_id=_uuid(),
)
assert len(plan.id) == 36
assert len(plan.template_id) == 36
def test_generated_video_model_construct_with_standard_uuid(self):
video = GeneratedVideoModel(
id=_uuid(),
project_id=_uuid(),
generation_task_id=_uuid(),
duplicate_of=_uuid(),
name="test.mp4",
file_url="https://example.com/test.mp4",
file_size=1000,
duration=10.0,
width=1280,
height=720,
fps=25.0,
)
assert len(video.id) == 36
assert len(video.duplicate_of) == 36