from __future__ import annotations import os import subprocess import sys import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] class KitInitTests(unittest.TestCase): def setUp(self) -> None: self.temp_dir = tempfile.TemporaryDirectory() self.home = Path(self.temp_dir.name) self.skills_home = self.home / ".skills" kit = self.skills_home / "kits" / "ack" (kit / "templates").mkdir(parents=True) (kit / "scripts").mkdir() (kit / "VERSION").write_text("1.2.3\n", encoding="utf-8") (kit / "templates" / "project.template.md").write_text( "# \nversion=\npath=\n", encoding="utf-8", ) (kit / "templates" / "tasks.template.yaml").write_text( 'updatedAt: ""\n' 'kitVersion: "<接入时的 ack 版本,见 kit 根 VERSION>"\n' 'project:\n' ' name: ""\n' ' repoPath: ""\n' ' devWorktree: ""\n', encoding="utf-8", ) def tearDown(self) -> None: self.temp_dir.cleanup() def run_skiff(self, *args: str) -> subprocess.CompletedProcess[str]: env = os.environ.copy() env["HOME"] = str(self.home) env["PYTHONPATH"] = str(REPO_ROOT) return subprocess.run( [sys.executable, "-m", "skiff", *args], cwd=REPO_ROOT, env=env, text=True, capture_output=True, check=False, ) def test_init_creates_symlink_and_rendered_project_files(self) -> None: project = self.home / "sample-app" project.mkdir() result = self.run_skiff("kit", "init", "ack", "--project", str(project)) self.assertEqual(result.returncode, 0, result.stderr) target = project / "docs" / "ack" self.assertTrue((target / "kit").is_symlink()) self.assertEqual((target / "kit").resolve(), self.skills_home / "kits" / "ack") project_content = (target / "project.md").read_text(encoding="utf-8") tasks_content = (target / "tasks.yaml").read_text(encoding="utf-8") self.assertIn("# sample-app", project_content) self.assertIn("version=1.2.3", project_content) self.assertIn(f'repoPath: "{project}"', tasks_content) self.assertNotIn("", tasks_content) def test_init_copy_mode_copies_kit(self) -> None: project = self.home / "copied-app" project.mkdir() result = self.run_skiff("kit", "init", "ack", "--copy", "--project", str(project)) self.assertEqual(result.returncode, 0, result.stderr) target = project / "docs" / "ack" / "kit" self.assertTrue(target.is_dir()) self.assertFalse(target.is_symlink()) self.assertEqual((target / "VERSION").read_text(encoding="utf-8"), "1.2.3\n") def test_init_refuses_to_overwrite_existing_files(self) -> None: project = self.home / "existing-app" target = project / "docs" / "ack" target.mkdir(parents=True) existing = target / "project.md" existing.write_text("keep me", encoding="utf-8") result = self.run_skiff("kit", "init", "ack", "--project", str(project)) self.assertNotEqual(result.returncode, 0) self.assertIn("拒绝覆盖已有路径", result.stderr) self.assertEqual(existing.read_text(encoding="utf-8"), "keep me") self.assertFalse((target / "kit").exists()) def test_init_rejects_missing_project_directory(self) -> None: project = self.home / "missing-app" result = self.run_skiff("kit", "init", "ack", "--project", str(project)) self.assertNotEqual(result.returncode, 0) self.assertIn("项目目录不存在", result.stderr) self.assertFalse(project.exists()) if __name__ == "__main__": unittest.main()