1. 删除了 test/unit/external_systems/framework/ 下的废弃空测试目录 2. 修复多处测试断言逻辑、参数传递与测试数据构造 3. 新增日志级别、缓存令牌、流事件等DTO单元测试 4. 补充路由绑定、会话仓储、outbox仓储的测试覆盖 5. 更新测试用例中的异常类型、参数校验与业务逻辑断言
264 lines
7.9 KiB
Python
264 lines
7.9 KiB
Python
"""media.py DTO 单元测试。
|
|
|
|
覆盖 ``MediaType`` 枚举与 ``MediaUploadResult`` / ``MediaDownloadResult`` /
|
|
``MediaMetadata`` / ``AttachmentUploadResult`` 的字段赋值、默认值、不可变
|
|
语义与 ``__post_init__`` 校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.media import (
|
|
AttachmentUploadResult,
|
|
MediaDownloadResult,
|
|
MediaMetadata,
|
|
MediaType,
|
|
MediaUploadResult,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestMediaType:
|
|
"""媒体类型枚举测试。"""
|
|
|
|
def test_enum_values_are_correct(self):
|
|
# Arrange
|
|
# Act
|
|
# Assert
|
|
assert MediaType.IMAGE == "image"
|
|
assert MediaType.FILE == "file"
|
|
assert MediaType.AUDIO == "audio"
|
|
assert MediaType.VIDEO == "video"
|
|
|
|
def test_enum_inherits_from_str(self):
|
|
# Arrange
|
|
# Act
|
|
# Assert
|
|
assert issubclass(MediaType, str)
|
|
assert isinstance(MediaType.IMAGE, str)
|
|
|
|
def test_enum_has_four_members(self):
|
|
# Arrange
|
|
# Act
|
|
members = list(MediaType)
|
|
# Assert
|
|
assert len(members) == 4
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestMediaUploadResult:
|
|
"""媒体上传结果 DTO 测试。"""
|
|
|
|
def test_required_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
result = MediaUploadResult(media_id="img_001")
|
|
# Assert
|
|
assert result.media_id == "img_001"
|
|
assert result.url is None
|
|
assert result.expires_at is None
|
|
|
|
def test_with_all_fields(self):
|
|
# Arrange
|
|
now = datetime(2026, 1, 1, 12, 0, 0)
|
|
# Act
|
|
result = MediaUploadResult(
|
|
media_id="img_001",
|
|
url="https://example.com/img.png",
|
|
expires_at=now,
|
|
)
|
|
# Assert
|
|
assert result.url == "https://example.com/img.png"
|
|
assert result.expires_at is now
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
result = MediaUploadResult(media_id="img_001")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
result.media_id = "other" # type: ignore[misc]
|
|
|
|
def test_empty_media_id_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
MediaUploadResult(media_id="")
|
|
assert exc_info.value.field == "media_id"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestMediaDownloadResult:
|
|
"""媒体下载结果 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Arrange
|
|
content = b"\x89PNG"
|
|
# Act
|
|
result = MediaDownloadResult(content=content, size=4)
|
|
# Assert
|
|
assert result.content == content
|
|
assert result.size == 4
|
|
assert result.filename is None
|
|
assert result.mime_type is None
|
|
|
|
def test_with_optional_fields(self):
|
|
# Arrange
|
|
# Act
|
|
result = MediaDownloadResult(
|
|
content=b"data",
|
|
size=4,
|
|
filename="test.png",
|
|
mime_type="image/png",
|
|
)
|
|
# Assert
|
|
assert result.filename == "test.png"
|
|
assert result.mime_type == "image/png"
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
result = MediaDownloadResult(content=b"", size=0)
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
result.size = 100 # type: ignore[misc]
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestMediaMetadata:
|
|
"""媒体元数据 DTO 测试。"""
|
|
|
|
def test_required_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
meta = MediaMetadata(
|
|
media_id="img_001",
|
|
media_type=MediaType.IMAGE,
|
|
size=1024,
|
|
)
|
|
# Assert
|
|
assert meta.media_id == "img_001"
|
|
assert meta.media_type is MediaType.IMAGE
|
|
assert meta.size == 1024
|
|
assert meta.mime_type is None
|
|
assert meta.created_at is None
|
|
|
|
def test_with_optional_fields(self):
|
|
# Arrange
|
|
now = datetime(2026, 1, 1, 12, 0, 0)
|
|
# Act
|
|
meta = MediaMetadata(
|
|
media_id="img_001",
|
|
media_type=MediaType.IMAGE,
|
|
size=1024,
|
|
mime_type="image/png",
|
|
created_at=now,
|
|
)
|
|
# Assert
|
|
assert meta.mime_type == "image/png"
|
|
assert meta.created_at is now
|
|
|
|
def test_zero_size_is_valid(self):
|
|
# Arrange
|
|
# Act
|
|
meta = MediaMetadata(media_id="img_001", media_type=MediaType.IMAGE, size=0)
|
|
# Assert - size 为 0 是合法的(非负)
|
|
assert meta.size == 0
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
meta = MediaMetadata(media_id="img_001", media_type=MediaType.IMAGE, size=1)
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
meta.media_id = "other" # type: ignore[misc]
|
|
|
|
def test_empty_media_id_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
MediaMetadata(media_id="", media_type=MediaType.IMAGE, size=1)
|
|
assert exc_info.value.field == "media_id"
|
|
|
|
def test_negative_size_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
MediaMetadata(media_id="img_001", media_type=MediaType.IMAGE, size=-1)
|
|
assert exc_info.value.field == "size"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestAttachmentUploadResult:
|
|
"""附件上传结果 DTO 测试。"""
|
|
|
|
def test_required_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
result = AttachmentUploadResult(
|
|
attachment_id="att-1",
|
|
url="https://example.com/att.png",
|
|
mime_type="image/png",
|
|
size_bytes=1024,
|
|
)
|
|
# Assert
|
|
assert result.attachment_id == "att-1"
|
|
assert result.url == "https://example.com/att.png"
|
|
assert result.mime_type == "image/png"
|
|
assert result.size_bytes == 1024
|
|
assert result.expires_at is None
|
|
|
|
def test_with_expires_at(self):
|
|
# Arrange
|
|
now = datetime(2026, 1, 1, 12, 0, 0)
|
|
# Act
|
|
result = AttachmentUploadResult(
|
|
attachment_id="att-1",
|
|
url="https://example.com/att.png",
|
|
mime_type="image/png",
|
|
size_bytes=1024,
|
|
expires_at=now,
|
|
)
|
|
# Assert
|
|
assert result.expires_at is now
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
result = AttachmentUploadResult(
|
|
attachment_id="att-1", url="url", mime_type="mt", size_bytes=1
|
|
)
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
result.attachment_id = "other" # type: ignore[misc]
|
|
|
|
def test_empty_attachment_id_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AttachmentUploadResult(attachment_id="", url="url", mime_type="mt", size_bytes=1)
|
|
assert exc_info.value.field == "attachment_id"
|
|
|
|
def test_empty_url_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AttachmentUploadResult(attachment_id="a", url="", mime_type="mt", size_bytes=1)
|
|
assert exc_info.value.field == "url"
|
|
|
|
def test_empty_mime_type_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AttachmentUploadResult(attachment_id="a", url="url", mime_type="", size_bytes=1)
|
|
assert exc_info.value.field == "mime_type"
|
|
|
|
def test_non_positive_size_bytes_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
AttachmentUploadResult(attachment_id="a", url="url", mime_type="mt", size_bytes=0)
|
|
assert exc_info.value.field == "size_bytes"
|