80 lines
4.2 KiB
Python
80 lines
4.2 KiB
Python
"""验证预集成 Skill 的文档契约和隔离 Git 行为。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SKILL = ROOT / "plugins/git/skills/integrate/SKILL.md"
|
|
|
|
|
|
class GitIntegrateTests(unittest.TestCase):
|
|
"""在临时仓库中验证 worktree 集成不会移动输入引用。"""
|
|
|
|
def git(self, repo: Path, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
|
|
"""执行测试仓库中的 Git 命令并保留失败状态供冲突断言。"""
|
|
|
|
return subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True, encoding="utf-8", check=check)
|
|
|
|
def make_repo(self, root: Path, conflict: bool = False) -> tuple[Path, str, str]:
|
|
"""创建具有源、目标分叉的隔离仓库。"""
|
|
|
|
repo = root / "repo"; repo.mkdir(); self.git(repo, "init", "-q")
|
|
self.git(repo, "config", "user.name", "Test User"); self.git(repo, "config", "user.email", "test@example.invalid")
|
|
(repo / "base.txt").write_text("base\n", encoding="utf-8")
|
|
self.git(repo, "add", "base.txt"); self.git(repo, "commit", "-q", "-m", "base")
|
|
self.git(repo, "branch", "target"); self.git(repo, "switch", "-q", "-c", "source")
|
|
source_file = repo / ("base.txt" if conflict else "source.txt")
|
|
source_file.write_text("source\n", encoding="utf-8")
|
|
self.git(repo, "add", "-A"); self.git(repo, "commit", "-q", "-m", "source")
|
|
source_hash = self.git(repo, "rev-parse", "source").stdout.strip()
|
|
self.git(repo, "switch", "-q", "target")
|
|
target_file = repo / ("base.txt" if conflict else "target.txt")
|
|
target_file.write_text("target\n", encoding="utf-8")
|
|
self.git(repo, "add", "-A"); self.git(repo, "commit", "-q", "-m", "target")
|
|
target_hash = self.git(repo, "rev-parse", "target").stdout.strip()
|
|
return repo, source_hash, target_hash
|
|
|
|
def test_isolated_merge_preserves_source_and_target_refs(self) -> None:
|
|
"""预集成分支发生 merge 后,源和目标引用必须保持原哈希。"""
|
|
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp); repo, source_hash, target_hash = self.make_repo(root)
|
|
worktree = root / "integration"
|
|
self.git(repo, "worktree", "add", "-q", "-b", "pre/integration", str(worktree), "target")
|
|
self.git(worktree, "merge", "--no-edit", "source")
|
|
self.assertEqual(source_hash, self.git(repo, "rev-parse", "source").stdout.strip())
|
|
self.assertEqual(target_hash, self.git(repo, "rev-parse", "target").stdout.strip())
|
|
self.assertTrue((worktree / "source.txt").is_file())
|
|
self.assertTrue((worktree / "target.txt").is_file())
|
|
|
|
def test_conflict_stops_before_automatic_resolution(self) -> None:
|
|
"""真实冲突应停留在未合并状态,不移动源或目标引用。"""
|
|
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp); repo, source_hash, target_hash = self.make_repo(root, conflict=True)
|
|
worktree = root / "integration"
|
|
self.git(repo, "worktree", "add", "-q", "-b", "pre/conflict", str(worktree), "target")
|
|
result = self.git(worktree, "merge", "--no-edit", "source", check=False)
|
|
self.assertNotEqual(0, result.returncode)
|
|
self.assertIn("UU base.txt", self.git(worktree, "status", "--short").stdout)
|
|
self.assertEqual(source_hash, self.git(repo, "rev-parse", "source").stdout.strip())
|
|
self.assertEqual(target_hash, self.git(repo, "rev-parse", "target").stdout.strip())
|
|
|
|
def test_skill_separates_stages_and_protects_delivery(self) -> None:
|
|
"""Skill 必须保留阶段授权、环境配置阻断和强推禁用边界。"""
|
|
|
|
text = SKILL.read_text(encoding="utf-8")
|
|
for value in ("assess", "prepare", "publish", "独立 worktree", "环境配置", "私钥"):
|
|
self.assertIn(value, text)
|
|
publish = (SKILL.parent / "references/publish.md").read_text(encoding="utf-8")
|
|
self.assertIn("默认不改用 `--force`", publish)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|