feat: add tag functionality to Asset
- domain: Asset.tags field with add_tag/remove_tag methods - validation: no empty tags, auto deduplication - tests: 5 integration tests (add/remove/duplicate/empty/idempotent) - all 13 tests passing
This commit is contained in:
@@ -95,6 +95,7 @@ class Asset:
|
||||
storage_key: str
|
||||
mime_type: str
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
tags: list[str] = field(default_factory=list)
|
||||
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
|
||||
@classmethod
|
||||
@@ -124,8 +125,23 @@ class Asset:
|
||||
storage_key=storage_key.strip(),
|
||||
mime_type=mime_type.strip(),
|
||||
metadata=metadata or {},
|
||||
tags=[],
|
||||
)
|
||||
|
||||
def add_tag(self, tag: str) -> None:
|
||||
"""添加标签。空标签会被忽略,自动去重。"""
|
||||
clean_tag = tag.strip()
|
||||
if not clean_tag:
|
||||
raise ValueError("标签不能为空")
|
||||
if clean_tag not in self.tags:
|
||||
self.tags.append(clean_tag)
|
||||
|
||||
def remove_tag(self, tag: str) -> None:
|
||||
"""删除标签。如果标签不存在,不报错(幂等性)。"""
|
||||
clean_tag = tag.strip()
|
||||
if clean_tag in self.tags:
|
||||
self.tags.remove(clean_tag)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class IngestJob:
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import pytest
|
||||
|
||||
from packages.domain import Asset
|
||||
|
||||
|
||||
def test_add_tag_to_asset():
|
||||
"""测试添加标签到 Asset。"""
|
||||
asset = Asset.create(
|
||||
workspace_id="ws-1",
|
||||
project_id="proj-1",
|
||||
library_id="lib-1",
|
||||
name="video.mp4",
|
||||
storage_key="uploads/abc/video.mp4",
|
||||
mime_type="video/mp4",
|
||||
)
|
||||
|
||||
asset.add_tag("风景")
|
||||
asset.add_tag("自然")
|
||||
|
||||
assert len(asset.tags) == 2
|
||||
assert "风景" in asset.tags
|
||||
assert "自然" in asset.tags
|
||||
|
||||
|
||||
def test_add_duplicate_tag_should_ignore():
|
||||
"""测试添加重复标签应自动去重。"""
|
||||
asset = Asset.create(
|
||||
workspace_id="ws-1",
|
||||
project_id="proj-1",
|
||||
library_id="lib-1",
|
||||
name="video.mp4",
|
||||
storage_key="uploads/abc/video.mp4",
|
||||
mime_type="video/mp4",
|
||||
)
|
||||
|
||||
asset.add_tag("风景")
|
||||
asset.add_tag("风景") # 重复
|
||||
|
||||
assert len(asset.tags) == 1
|
||||
assert asset.tags.count("风景") == 1
|
||||
|
||||
|
||||
def test_add_empty_tag_should_fail():
|
||||
"""测试添加空标签应失败。"""
|
||||
asset = Asset.create(
|
||||
workspace_id="ws-1",
|
||||
project_id="proj-1",
|
||||
library_id="lib-1",
|
||||
name="video.mp4",
|
||||
storage_key="uploads/abc/video.mp4",
|
||||
mime_type="video/mp4",
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="标签不能为空"):
|
||||
asset.add_tag("")
|
||||
|
||||
with pytest.raises(ValueError, match="标签不能为空"):
|
||||
asset.add_tag(" ") # 仅空格
|
||||
|
||||
|
||||
def test_remove_tag_from_asset():
|
||||
"""测试从 Asset 删除标签。"""
|
||||
asset = Asset.create(
|
||||
workspace_id="ws-1",
|
||||
project_id="proj-1",
|
||||
library_id="lib-1",
|
||||
name="video.mp4",
|
||||
storage_key="uploads/abc/video.mp4",
|
||||
mime_type="video/mp4",
|
||||
)
|
||||
|
||||
asset.add_tag("风景")
|
||||
asset.add_tag("自然")
|
||||
|
||||
asset.remove_tag("风景")
|
||||
|
||||
assert len(asset.tags) == 1
|
||||
assert "风景" not in asset.tags
|
||||
assert "自然" in asset.tags
|
||||
|
||||
|
||||
def test_remove_nonexistent_tag_should_be_idempotent():
|
||||
"""测试删除不存在的标签应幂等(不报错)。"""
|
||||
asset = Asset.create(
|
||||
workspace_id="ws-1",
|
||||
project_id="proj-1",
|
||||
library_id="lib-1",
|
||||
name="video.mp4",
|
||||
storage_key="uploads/abc/video.mp4",
|
||||
mime_type="video/mp4",
|
||||
)
|
||||
|
||||
asset.add_tag("风景")
|
||||
|
||||
# 删除不存在的标签,不应报错
|
||||
asset.remove_tag("不存在的标签")
|
||||
|
||||
assert len(asset.tags) == 1
|
||||
assert "风景" in asset.tags
|
||||
Reference in New Issue
Block a user