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:
Xiaoxia AI
2026-06-15 16:47:45 +08:00
parent 37f5e0cead
commit 5f0a280669
2 changed files with 115 additions and 0 deletions
+16
View File
@@ -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:
+99
View File
@@ -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