feat(migration): 建立 Skill 迁移工作流与追踪工具
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""迁移内部脚本的最小行为测试。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import hashlib
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_ROOT = Path(__file__).parents[1] / "scripts"
|
||||
|
||||
|
||||
def load_module(name: str):
|
||||
"""从脚本路径加载模块,避免要求项目安装为 Python 包。"""
|
||||
spec = importlib.util.spec_from_file_location(name, SCRIPT_ROOT / f"{name}.py")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
scan_sources = load_module("scan_sources")
|
||||
check_skill = load_module("check_skill")
|
||||
update_lock = load_module("update_lock")
|
||||
|
||||
|
||||
class MigrationScriptTests(unittest.TestCase):
|
||||
def test_scan_uses_relative_paths_and_stable_hash(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
skill = root / "group" / "demo"
|
||||
skill.mkdir(parents=True)
|
||||
(skill / "SKILL.md").write_text(
|
||||
"---\nname: demo\ndescription: 示例\n---\n", encoding="utf-8"
|
||||
)
|
||||
first = scan_sources.scan_source("source-a", root)
|
||||
second = scan_sources.scan_source("source-a", root)
|
||||
self.assertEqual("group/demo", first["skills"][0]["relativePath"])
|
||||
self.assertEqual(first["skills"][0]["sha256"], second["skills"][0]["sha256"])
|
||||
self.assertNotIn(str(root), json.dumps(first, ensure_ascii=False))
|
||||
|
||||
def test_check_reports_name_mismatch_and_forbidden_term(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory) / "expected"
|
||||
root.mkdir()
|
||||
(root / "SKILL.md").write_text(
|
||||
"---\nname: other\ndescription: 示例\n---\n内部标识",
|
||||
encoding="utf-8",
|
||||
)
|
||||
result = check_skill.check_skill(root, ["内部标识"])
|
||||
codes = {issue["code"] for issue in result["issues"]}
|
||||
self.assertIn("name-path-mismatch", codes)
|
||||
self.assertIn("forbidden:内部标识", codes)
|
||||
|
||||
def test_update_lock_preserves_manual_fields(self):
|
||||
path_hash = hashlib.sha256("group/demo".encode("utf-8")).hexdigest()
|
||||
key = f"source-a:{path_hash[:16]}"
|
||||
lock = {
|
||||
"schemaVersion": 1,
|
||||
"sources": {},
|
||||
"skills": {
|
||||
key: {
|
||||
"status": "specified",
|
||||
"target": "plugins/skill/skills/demo",
|
||||
}
|
||||
},
|
||||
}
|
||||
report = {
|
||||
"sources": [
|
||||
{
|
||||
"id": "source-a",
|
||||
"commit": "abc",
|
||||
"skillCount": 1,
|
||||
"skills": [
|
||||
{
|
||||
"name": "demo",
|
||||
"relativePath": "group/demo",
|
||||
"sha256": "123",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
merged = update_lock.merge(lock, report)
|
||||
entry = merged["skills"][key]
|
||||
self.assertEqual("specified", entry["status"])
|
||||
self.assertEqual("plugins/skill/skills/demo", entry["target"])
|
||||
self.assertEqual("123", entry["sourceSha256"])
|
||||
self.assertNotIn("name", entry)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user