| | |
| | | class IntegrationTest: |
| | | """Integration test runner.""" |
| | | |
| | | def __init__(self, keep_temp: bool = False): |
| | | def __init__(self, keep_temp: bool = False, use_cli: bool = False, use_binary: bool = False): |
| | | self.keep_temp = keep_temp |
| | | self.use_cli = use_cli |
| | | self.use_binary = use_binary |
| | | self.temp_dir: Path | None = None |
| | | self.config: Config | None = None |
| | | self.config_path: Path | None = None |
| | | self.passed = 0 |
| | | self.failed = 0 |
| | | |
| | | def _run_cli(self, command: str, *args: str) -> subprocess.CompletedProcess: |
| | | """Run archbuild CLI command via subprocess.""" |
| | | if not self.config_path: |
| | | raise RuntimeError("Config path not set") |
| | | |
| | | env = os.environ.copy() |
| | | |
| | | if self.use_binary: |
| | | binary_path = Path(__file__).parent.parent / "dist" / "archbuild-bin" |
| | | if not binary_path.exists(): |
| | | raise RuntimeError(f"Binary not found at {binary_path}. Run scripts/build_binary.py first.") |
| | | |
| | | cmd = [str(binary_path), "-c", str(self.config_path), command] + list(args) |
| | | # No PYTHONPATH needed for the standalone binary |
| | | else: |
| | | # Add src to PYTHONPATH so the CLI can find the package |
| | | env["PYTHONPATH"] = str(Path(__file__).parent.parent / "src") |
| | | cmd = [sys.executable, "-m", "archbuild.cli", "-c", str(self.config_path), command] + list(args) |
| | | |
| | | return subprocess.run(cmd, capture_output=True, text=True, env=env) |
| | | |
| | | def setup(self) -> None: |
| | | """Set up temporary test environment.""" |
| | | console.print("\n[bold blue]═══ Setting up test environment ═══[/]") |
| | |
| | | }, |
| | | ) |
| | | |
| | | # Save config to disk for CLI to use |
| | | from archbuild.config import save_config |
| | | self.config_path = self.temp_dir / "config.yaml" |
| | | save_config(self.config, self.config_path) |
| | | |
| | | setup_logging(self.config.log_level) |
| | | console.print(" [green]✓[/] Configuration created") |
| | | |
| | |
| | | """Test repository initialization.""" |
| | | console.print("\n[bold blue]═══ Test: Repository Initialization ═══[/]") |
| | | |
| | | repo = RepoManager(self.config) |
| | | repo.ensure_repo_exists() |
| | | if self.use_cli: |
| | | result = self._run_cli("init") |
| | | self.check(result.returncode == 0, "CLI: init command success") |
| | | else: |
| | | repo = RepoManager(self.config) |
| | | repo.ensure_repo_exists() |
| | | |
| | | # Check directories exist |
| | | self.check( |
| | |
| | | |
| | | results: dict[str, BuildStatus] = {} |
| | | |
| | | async with AURClient() as aur: |
| | | async with Builder(self.config, aur) as builder: |
| | | for pkg_name in packages: |
| | | console.print(f"\n Building {pkg_name}...") |
| | | result = await builder.build_package(pkg_name, force=True) |
| | | results[pkg_name] = result.status |
| | | if self.use_cli: |
| | | for pkg_name in packages: |
| | | console.print(f"\n Building {pkg_name} via CLI...") |
| | | result = self._run_cli("build", pkg_name, "-f") |
| | | |
| | | if result.returncode == 0: |
| | | results[pkg_name] = BuildStatus.SUCCESS |
| | | self.check(True, f"CLI: Built {pkg_name} successfully") |
| | | |
| | | # Check for created artifacts |
| | | pkg_dir = self.config.repository.build_dir / pkg_name |
| | | artifacts = list(pkg_dir.glob("*.pkg.tar.*")) |
| | | artifacts = [a for a in artifacts if not a.name.endswith(".sig")] |
| | | self.check(len(artifacts) > 0, f" Created {len(artifacts)} artifact(s)") |
| | | else: |
| | | results[pkg_name] = BuildStatus.FAILED |
| | | self.check(False, f"CLI: Failed to build {pkg_name}\nError: {result.stderr}") |
| | | else: |
| | | async with AURClient() as aur: |
| | | async with Builder(self.config, aur) as builder: |
| | | for pkg_name in packages: |
| | | console.print(f"\n Building {pkg_name}...") |
| | | result = await builder.build_package(pkg_name, force=True) |
| | | results[pkg_name] = result.status |
| | | |
| | | if result.status == BuildStatus.SUCCESS: |
| | | self.check(True, f"Built {pkg_name} successfully ({result.duration:.1f}s)") |
| | | self.check( |
| | | len(result.artifacts) > 0, |
| | | f" Created {len(result.artifacts)} artifact(s)" |
| | | ) |
| | | for artifact in result.artifacts: |
| | | console.print(f" → {artifact.name}") |
| | | else: |
| | | self.check(False, f"Failed to build {pkg_name}: {result.error}") |
| | | if result.status == BuildStatus.SUCCESS: |
| | | self.check(True, f"Built {pkg_name} successfully ({result.duration:.1f}s)") |
| | | self.check( |
| | | len(result.artifacts) > 0, |
| | | f" Created {len(result.artifacts)} artifact(s)" |
| | | ) |
| | | for artifact in result.artifacts: |
| | | console.print(f" → {artifact.name}") |
| | | else: |
| | | self.check(False, f"Failed to build {pkg_name}: {result.error}") |
| | | |
| | | return results |
| | | |
| | |
| | | """Test adding packages to repository.""" |
| | | console.print("\n[bold blue]═══ Test: Repository Management ═══[/]") |
| | | |
| | | repo = RepoManager(self.config) |
| | | if self.use_cli: |
| | | # In CLI mode, 'add' or 'build' already adds to repo, but we can test 'remake' |
| | | result = self._run_cli("remake") |
| | | self.check(result.returncode == 0, "CLI: remake command success") |
| | | else: |
| | | repo = RepoManager(self.config) |
| | | async with AURClient() as aur: |
| | | async with Builder(self.config, aur) as builder: |
| | | for pkg_name in packages: |
| | | # Get the build result with artifacts |
| | | pkg_dir = self.config.repository.build_dir / pkg_name |
| | | if not pkg_dir.exists(): |
| | | continue |
| | | |
| | | async with AURClient() as aur: |
| | | async with Builder(self.config, aur) as builder: |
| | | for pkg_name in packages: |
| | | # Get the build result with artifacts |
| | | pkg_dir = self.config.repository.build_dir / pkg_name |
| | | if not pkg_dir.exists(): |
| | | continue |
| | | # Find artifacts |
| | | artifacts = list(pkg_dir.glob("*.pkg.tar.*")) |
| | | artifacts = [a for a in artifacts if not a.name.endswith(".sig")] |
| | | |
| | | # Find artifacts |
| | | artifacts = list(pkg_dir.glob("*.pkg.tar.*")) |
| | | artifacts = [a for a in artifacts if not a.name.endswith(".sig")] |
| | | |
| | | if artifacts: |
| | | # Create a mock build result for add_packages |
| | | from archbuild.builder import BuildResult |
| | | mock_result = BuildResult( |
| | | package=pkg_name, |
| | | status=BuildStatus.SUCCESS, |
| | | artifacts=artifacts, |
| | | ) |
| | | success = repo.add_packages(mock_result) |
| | | self.check(success, f"Added {pkg_name} to repository") |
| | | if artifacts: |
| | | # Create a mock build result for add_packages |
| | | from archbuild.builder import BuildResult |
| | | mock_result = BuildResult( |
| | | package=pkg_name, |
| | | status=BuildStatus.SUCCESS, |
| | | artifacts=artifacts, |
| | | ) |
| | | success = repo.add_packages(mock_result) |
| | | self.check(success, f"Added {pkg_name} to repository") |
| | | |
| | | # List packages in repo |
| | | repo = RepoManager(self.config) |
| | | pkg_list = repo.list_packages() |
| | | self.check(len(pkg_list) > 0, f"Repository contains {len(pkg_list)} package(s)") |
| | | |
| | |
| | | """Test repository database integrity.""" |
| | | console.print("\n[bold blue]═══ Test: Database Integrity ═══[/]") |
| | | |
| | | if self.use_cli: |
| | | # We already tested remake, let's just check the DB file |
| | | pass |
| | | |
| | | db_path = self.config.repository.path / f"{self.config.repository.name}.db.tar.zst" |
| | | |
| | | self.check(db_path.exists(), f"Database file exists: {db_path.name}") |
| | |
| | | """Test package cleanup functionality.""" |
| | | console.print("\n[bold blue]═══ Test: Cleanup ═══[/]") |
| | | |
| | | repo = RepoManager(self.config) |
| | | removed = repo.cleanup() |
| | | |
| | | self.check(True, f"Cleanup removed {removed} old version(s)") |
| | | if self.use_cli: |
| | | result = self._run_cli("cleanup") |
| | | self.check(result.returncode == 0, "CLI: cleanup command success") |
| | | else: |
| | | repo = RepoManager(self.config) |
| | | removed = repo.cleanup() |
| | | self.check(True, f"Cleanup removed {removed} old version(s)") |
| | | |
| | | async def run(self) -> bool: |
| | | """Run all integration tests.""" |
| | | mode_str = " (Python Mode)" |
| | | if self.use_binary: |
| | | mode_str = " (Binary Mode)" |
| | | elif self.use_cli: |
| | | mode_str = " (CLI Mode)" |
| | | |
| | | console.print("[bold magenta]╔════════════════════════════════════════════╗[/]") |
| | | console.print("[bold magenta]║ Archbuild Integration Test Suite ║[/]") |
| | | console.print(f"[bold magenta]║ Archbuild Integration Test Suite{mode_str:<10}║[/]") |
| | | console.print("[bold magenta]╚════════════════════════════════════════════╝[/]") |
| | | |
| | | try: |
| | |
| | | async def main() -> int: |
| | | """Main entry point.""" |
| | | keep_temp = "--keep-temp" in sys.argv |
| | | use_cli = "--use-cli" in sys.argv |
| | | use_binary = "--use-binary" in sys.argv |
| | | |
| | | if use_binary: |
| | | use_cli = True # Binary mode is a sub-mode of CLI mode |
| | | |
| | | # Check if running on Arch Linux |
| | | if not Path("/etc/arch-release").exists(): |
| | |
| | | console.print(f"[red]Required tool not found: {tool}[/]") |
| | | return 1 |
| | | |
| | | test = IntegrationTest(keep_temp=keep_temp) |
| | | test = IntegrationTest(keep_temp=keep_temp, use_cli=use_cli, use_binary=use_binary) |
| | | success = await test.run() |
| | | |
| | | return 0 if success else 1 |