"""Tests for --integration flag on specify init (CLI-level).""" import json import os import yaml class TestInitIntegrationFlag: def test_integration_and_ai_mutually_exclusive(self, tmp_path): from typer.testing import CliRunner from specify_cli import app runner = CliRunner() result = runner.invoke(app, [ "init", str(tmp_path / "test-project"), "--ai", "claude", "--integration", "copilot", ]) assert result.exit_code != 0 assert "mutually exclusive" in result.output def test_unknown_integration_rejected(self, tmp_path): from typer.testing import CliRunner from specify_cli import app runner = CliRunner() result = runner.invoke(app, [ "init", str(tmp_path / "test-project"), "--integration", "nonexistent", ]) assert result.exit_code != 0 assert "Unknown integration" in result.output def test_integration_copilot_creates_files(self, tmp_path): from typer.testing import CliRunner from specify_cli import app runner = CliRunner() project = tmp_path / "int-test" project.mkdir() old_cwd = os.getcwd() try: os.chdir(project) result = runner.invoke(app, [ "init", "--here", "--integration", "copilot", "--script", "sh", "--no-git", ], catch_exceptions=False) finally: os.chdir(old_cwd) assert result.exit_code == 0, f"init failed: {result.output}" assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists() assert (project / ".github" / "prompts" / "speckit.plan.prompt.md").exists() assert (project / ".specify" / "scripts" / "bash" / "common.sh").exists() data = json.loads((project / ".specify" / "integration.json").read_text(encoding="utf-8")) assert data["integration"] == "copilot" assert "scripts" in data assert "update-context" in data["scripts"] opts = json.loads((project / ".specify" / "init-options.json").read_text(encoding="utf-8")) assert opts["integration"] == "copilot" assert (project / ".specify" / "integrations" / "copilot.manifest.json").exists() assert (project / ".specify" / "integrations" / "copilot" / "scripts" / "update-context.sh").exists() shared_manifest = project / ".specify" / "integrations" / "speckit.manifest.json" assert shared_manifest.exists() def test_ai_copilot_auto_promotes(self, tmp_path): from typer.testing import CliRunner from specify_cli import app project = tmp_path / "promote-test" project.mkdir() old_cwd = os.getcwd() try: os.chdir(project) runner = CliRunner() result = runner.invoke(app, [ "init", "--here", "--ai", "copilot", "--script", "sh", "--no-git", ], catch_exceptions=False) finally: os.chdir(old_cwd) assert result.exit_code == 0 assert (project / ".github" / "agents" / "speckit.plan.agent.md").exists() def test_ai_claude_here_preserves_preexisting_commands(self, tmp_path): from typer.testing import CliRunner from specify_cli import app project = tmp_path / "claude-here-existing" project.mkdir() commands_dir = project / ".claude" / "skills" commands_dir.mkdir(parents=True) skill_dir = commands_dir / "speckit-specify" skill_dir.mkdir(parents=True) command_file = skill_dir / "SKILL.md" command_file.write_text("# preexisting command\n", encoding="utf-8") old_cwd = os.getcwd() try: os.chdir(project) runner = CliRunner() result = runner.invoke(app, [ "init", "--here", "--force", "--ai", "claude", "--ai-skills", "--script", "sh", "--no-git", "--ignore-agent-tools", ], catch_exceptions=False) finally: os.chdir(old_cwd) assert result.exit_code == 0, result.output assert command_file.exists() # init replaces skills (not additive); verify the file has valid skill content assert command_file.exists() assert "speckit-specify" in command_file.read_text(encoding="utf-8") assert (project / ".claude" / "skills" / "speckit-plan" / "SKILL.md").exists() def test_shared_infra_skips_existing_files(self, tmp_path): """Pre-existing shared files are not overwritten by _install_shared_infra.""" from typer.testing import CliRunner from specify_cli import app project = tmp_path / "skip-test" project.mkdir() # Pre-create a shared script with custom content scripts_dir = project / ".specify" / "scripts" / "bash" scripts_dir.mkdir(parents=True) custom_content = "# user-modified common.sh\n" (scripts_dir / "common.sh").write_text(custom_content, encoding="utf-8") # Pre-create a shared template with custom content templates_dir = project / ".specify" / "templates" templates_dir.mkdir(parents=True) custom_template = "# user-modified spec-template\n" (templates_dir / "spec-template.md").write_text(custom_template, encoding="utf-8") old_cwd = os.getcwd() try: os.chdir(project) runner = CliRunner() result = runner.invoke(app, [ "init", "--here", "--force", "--integration", "copilot", "--script", "sh", "--no-git", ], catch_exceptions=False) finally: os.chdir(old_cwd) assert result.exit_code == 0 # User's files should be preserved assert (scripts_dir / "common.sh").read_text(encoding="utf-8") == custom_content assert (templates_dir / "spec-template.md").read_text(encoding="utf-8") == custom_template # Other shared files should still be installed assert (scripts_dir / "setup-plan.sh").exists() assert (templates_dir / "plan-template.md").exists() class TestForceExistingDirectory: """Tests for --force merging into an existing named directory.""" def test_force_merges_into_existing_dir(self, tmp_path): """specify init