## test_build_coordinator.nim ## Unit tests for BuildCoordinator import std/[unittest, tables, times, os, options] import ../src/nimpak/build/[types, coordinator, adapter] import ../src/nimpak/graft_coordinator import ../src/nimpak/install_manager import ../src/nimpak/variants # Mock adapter for testing type MockAdapter = ref object of BuildAdapter shouldFail: bool searchResult: bool proc newMockAdapter(name: string, available: bool = true): MockAdapter = result = MockAdapter( name: name, available: available, packageCount: 100, shouldFail: false, searchResult: true ) method isAvailable*(adapter: MockAdapter): bool = adapter.available method searchPackage*( adapter: MockAdapter, packageName: string ): Option[PackageInfo] = if adapter.searchResult: return some(PackageInfo( name: packageName, version: "1.0.0", description: "Mock package", source: adapter.name, category: "", available: true )) return none(PackageInfo) method buildPackage*( adapter: MockAdapter, request: BuildRequest ): BuildResult = result = BuildResult( success: not adapter.shouldFail, source: adapter.name, packageName: request.packageName, version: "1.0.0", artifactPath: if not adapter.shouldFail: "/mock/path" else: "", buildLog: "Mock build log", variantFingerprint: "mock-fingerprint", variantDomains: request.variantFlags, errors: if adapter.shouldFail: @["Mock build error"] else: @[], warnings: @[], buildTime: initDuration(seconds = 1) ) suite "BuildCoordinator Tests": setup: let buildConfig = BuildConfig( cacheDir: getTempDir() / "nip-test-cache", buildLogsDir: getTempDir() / "nip-test-cache" / "logs", keepWork: false, rebuild: false, noInstall: false, timeout: initDuration(hours = 2), jobs: 4, verbose: false ) # Create a minimal graft coordinator for testing let installConfig = InstallConfig( programsDir: getTempDir() / "Programs", linksDir: getTempDir() / "System" / "Links", cacheDir: getTempDir() / "nip-test-cache", dbFile: getTempDir() / "nip-test.db", autoSymlink: true, checkConflicts: true, verbose: false ) let graftCoordinator = newGraftCoordinator(installConfig, false) let coordinator = newBuildCoordinator(buildConfig, graftCoordinator) test "BuildCoordinator initialization": check coordinator != nil check coordinator.config.cacheDir == getTempDir() / "nip-test-cache" check coordinator.adapters.len == 0 check coordinator.verbose == false test "Adapter registration": let mockAdapter = newMockAdapter("mock") coordinator.registerAdapter(mockAdapter) check coordinator.adapters.len == 1 check coordinator.adapters.hasKey("mock") test "Get adapter by name": let mockAdapter = newMockAdapter("test-adapter") coordinator.registerAdapter(mockAdapter) let retrieved = coordinator.getAdapter("test-adapter") check retrieved.isSome check retrieved.get().name == "test-adapter" test "Get non-existent adapter": let retrieved = coordinator.getAdapter("non-existent") check retrieved.isNone test "Adapter name case insensitivity": let mockAdapter = newMockAdapter("TestAdapter") coordinator.registerAdapter(mockAdapter) let retrieved1 = coordinator.getAdapter("testadapter") let retrieved2 = coordinator.getAdapter("TESTADAPTER") let retrieved3 = coordinator.getAdapter("TestAdapter") check retrieved1.isSome check retrieved2.isSome check retrieved3.isSome test "Source detection - Nix available": if dirExists("/nix"): let sources = coordinator.detectSources() check "nix" in sources test "Source detection - PKGSRC available": if dirExists("/usr/pkgsrc"): let sources = coordinator.detectSources() check "pkgsrc" in sources test "Source detection - Gentoo available": if fileExists("/usr/bin/emerge"): let sources = coordinator.detectSources() check "gentoo" in sources test "Source selection - auto with priority": let nixAdapter = newMockAdapter("nix", available = true) let pkgsrcAdapter = newMockAdapter("pkgsrc", available = true) let gentooAdapter = newMockAdapter("gentoo", available = true) coordinator.registerAdapter(nixAdapter) coordinator.registerAdapter(pkgsrcAdapter) coordinator.registerAdapter(gentooAdapter) # Mock the detection to return all sources if dirExists("/nix"): let selected = coordinator.selectSource("auto") check selected.isSome check selected.get() == "nix" # Nix has highest priority test "Source selection - specific source": let mockAdapter = newMockAdapter("mock", available = true) coordinator.registerAdapter(mockAdapter) let selected = coordinator.selectSource("mock") check selected.isSome check selected.get() == "mock" test "Source selection - unavailable source": let mockAdapter = newMockAdapter("mock", available = false) coordinator.registerAdapter(mockAdapter) let selected = coordinator.selectSource("mock") check selected.isNone test "Source selection - no sources available": # Don't register any adapters let selected = coordinator.selectSource("auto") # Result depends on actual system state discard selected test "Build package - successful build": let mockAdapter = newMockAdapter("mock", available = true) coordinator.registerAdapter(mockAdapter) let variantFlags = @[ VariantFlag(domain: "graphics", value: "wayland"), VariantFlag(domain: "optimization", value: "lto") ] let result = coordinator.buildPackage("test-package", variantFlags, "mock") check result.success == true check result.packageName == "test-package" check result.source == "mock" check result.artifactPath == "/mock/path" check result.errors.len == 0 test "Build package - failed build": let mockAdapter = newMockAdapter("mock", available = true) mockAdapter.shouldFail = true coordinator.registerAdapter(mockAdapter) let variantFlags: seq[VariantFlag] = @[] let result = coordinator.buildPackage("test-package", variantFlags, "mock") check result.success == false check result.errors.len > 0 check "Mock build error" in result.errors test "Build package - no suitable source": let variantFlags: seq[VariantFlag] = @[] let result = coordinator.buildPackage("test-package", variantFlags, "non-existent") check result.success == false check result.errors.len > 0 test "Build package - variant flags conversion": let mockAdapter = newMockAdapter("mock", available = true) coordinator.registerAdapter(mockAdapter) let variantFlags = @[ VariantFlag(domain: "graphics", value: "wayland"), VariantFlag(domain: "graphics", value: "vulkan"), VariantFlag(domain: "audio", value: "pipewire") ] let result = coordinator.buildPackage("test-package", variantFlags, "mock") check result.success == true check result.variantDomains.hasKey("graphics") check result.variantDomains["graphics"].len == 2 check result.variantDomains.hasKey("audio") check result.variantDomains["audio"].len == 1 test "Install build artifact - successful": let mockResult = BuildResult( success: true, source: "mock", packageName: "test-package", version: "1.0.0", artifactPath: "/mock/path", buildLog: "", variantFingerprint: "test-fingerprint", variantDomains: initTable[string, seq[string]](), errors: @[], warnings: @[], buildTime: initDuration(seconds = 1) ) let installed = coordinator.installBuildArtifact(mockResult) # Will fail because mock path doesn't exist, but that's expected # The important thing is it returns a tuple with the right structure check installed.errors.len >= 0 # Just check structure is correct test "Install build artifact - failed build": let mockResult = BuildResult( success: false, source: "mock", packageName: "test-package", version: "1.0.0", artifactPath: "", buildLog: "", variantFingerprint: "", variantDomains: initTable[string, seq[string]](), errors: @["Build failed"], warnings: @[], buildTime: initDuration(seconds = 1) ) let installed = coordinator.installBuildArtifact(mockResult) check installed.success == false test "Install build artifact - no install flag": coordinator.config.noInstall = true let mockResult = BuildResult( success: true, source: "mock", packageName: "test-package", version: "1.0.0", artifactPath: "/mock/path", buildLog: "", variantFingerprint: "test-fingerprint", variantDomains: initTable[string, seq[string]](), errors: @[], warnings: @[], buildTime: initDuration(seconds = 1) ) let installed = coordinator.installBuildArtifact(mockResult) check installed.success == true # Returns true but skips installation