"""验证预集成 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()