"""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"