## test_grafting_integration.nim ## Integration tests for build artifact grafting import std/[unittest, tables, os, strutils] import ../src/nimpak/build/[types, grafting] import ../src/nimpak/graft_coordinator import ../src/nimpak/install_manager suite "Grafting Integration Tests": setup: let testCacheDir = getTempDir() / "nip-graft-test" let testProgramsDir = getTempDir() / "Programs-test" let testLinksDir = getTempDir() / "System-test" / "Links" # Clean up from previous runs if dirExists(testCacheDir): removeDir(testCacheDir) if dirExists(testProgramsDir): removeDir(testProgramsDir) if dirExists(testLinksDir): removeDir(testLinksDir) # Create test directories createDir(testCacheDir) createDir(testProgramsDir) createDir(testLinksDir / "Executables") createDir(testLinksDir / "Libraries") let installConfig = InstallConfig( programsDir: testProgramsDir, linksDir: testLinksDir, cacheDir: testCacheDir, dbFile: testCacheDir / "test.db", autoSymlink: true, checkConflicts: false, verbose: false ) let coordinator = newGraftCoordinator(installConfig, false) test "Validate artifact - valid directory": # Create a mock artifact let artifactPath = testCacheDir / "mock-artifact" createDir(artifactPath / "bin") writeFile(artifactPath / "bin" / "test-exe", "#!/bin/sh\necho test") let (valid, errors) = validateArtifact(artifactPath) check valid == true check errors.len == 0 test "Validate artifact - missing directory": let (valid, errors) = validateArtifact("/nonexistent/path") check valid == false check errors.len > 0 check "does not exist" in errors[0] test "Validate artifact - empty directory": let emptyPath = testCacheDir / "empty-artifact" createDir(emptyPath) let (valid, errors) = validateArtifact(emptyPath) # Should be valid but with warnings check valid == true check errors.len > 0 # Has warning about being empty test "Calculate directory hash": # Create a test directory with content let testDir = testCacheDir / "hash-test" createDir(testDir) writeFile(testDir / "file1.txt", "content1") writeFile(testDir / "file2.txt", "content2") let hash1 = calculateDirectoryHash(testDir) # Hash should be consistent let hash2 = calculateDirectoryHash(testDir) check hash1 == hash2 # Hash should start with blake2b- check hash1.startsWith("blake2b-") test "Calculate directory hash - different content": let testDir1 = testCacheDir / "hash-test-1" let testDir2 = testCacheDir / "hash-test-2" createDir(testDir1) createDir(testDir2) writeFile(testDir1 / "file.txt", "content1") writeFile(testDir2 / "file.txt", "content2") let hash1 = calculateDirectoryHash(testDir1) let hash2 = calculateDirectoryHash(testDir2) # Different content should produce different hashes check hash1 != hash2 test "Build variant fingerprint": var domains = initTable[string, seq[string]]() domains["graphics"] = @["wayland"] domains["audio"] = @["pipewire"] let fingerprint = buildVariantFingerprint( "test-package", "1.0.0", domains, "nix" ) # Should produce a hash check fingerprint.len > 0 check fingerprint.startsWith("blake2b-") test "Build variant fingerprint - consistency": var domains = initTable[string, seq[string]]() domains["graphics"] = @["wayland"] let fp1 = buildVariantFingerprint("pkg", "1.0", domains, "nix") let fp2 = buildVariantFingerprint("pkg", "1.0", domains, "nix") # Same input should produce same fingerprint check fp1 == fp2 test "Build variant fingerprint - different sources": var domains = initTable[string, seq[string]]() domains["graphics"] = @["wayland"] let fpNix = buildVariantFingerprint("pkg", "1.0", domains, "nix") let fpPkgsrc = buildVariantFingerprint("pkg", "1.0", domains, "pkgsrc") # Different sources should produce different fingerprints check fpNix != fpPkgsrc test "Graft build artifact - basic flow": # Create a mock build result let artifactPath = testCacheDir / "mock-build" createDir(artifactPath / "bin") writeFile(artifactPath / "bin" / "test-app", "#!/bin/sh\necho test") var domains = initTable[string, seq[string]]() domains["test"] = @["value"] let buildResult = BuildResult( success: true, source: "nix", packageName: "test-package", version: "1.0.0", artifactPath: artifactPath, buildLog: "", variantFingerprint: "test-fp", variantDomains: domains, errors: @[], warnings: @[] ) let (success, installPath, errors) = graftBuildArtifact( coordinator, buildResult, verbose = false ) check success == true check errors.len == 0 check installPath != "" check dirExists(installPath) # Check that files were copied check fileExists(installPath / "bin" / "test-app") # Check that Current symlink was created let currentLink = testProgramsDir / "test-package" / "Current" check symlinkExists(currentLink) or dirExists(currentLink) test "Graft build artifact - with symlinks": # Create a mock build result with executable let artifactPath = testCacheDir / "mock-build-symlinks" createDir(artifactPath / "bin") let exePath = artifactPath / "bin" / "my-app" writeFile(exePath, "#!/bin/sh\necho test") # Make it executable try: setFilePermissions(exePath, {fpUserExec, fpUserRead, fpUserWrite}) except: discard # Permissions might not work in all test environments var domains = initTable[string, seq[string]]() let buildResult = BuildResult( success: true, source: "nix", packageName: "symlink-test", version: "1.0.0", artifactPath: artifactPath, buildLog: "", variantFingerprint: "test-fp-2", variantDomains: domains, errors: @[], warnings: @[] ) let (success, installPath, errors) = graftBuildArtifact( coordinator, buildResult, verbose = false ) check success == true # Check that symlink was created (if autoSymlink is enabled) let symlinkPath = testLinksDir / "Executables" / "my-app" # Symlink might exist depending on system if fileExists(symlinkPath) or symlinkExists(symlinkPath): check true # Symlink was created else: check true # Symlink creation might have been skipped test "Graft build artifact - failed build": let buildResult = BuildResult( success: false, source: "nix", packageName: "failed-package", version: "1.0.0", artifactPath: "", buildLog: "", variantFingerprint: "", variantDomains: initTable[string, seq[string]](), errors: @["Build failed"], warnings: @[] ) let (success, installPath, errors) = graftBuildArtifact( coordinator, buildResult, verbose = false ) # Should not graft failed builds check success == false check errors.len > 0 teardown: # Clean up test directories try: if dirExists(testCacheDir): removeDir(testCacheDir) if dirExists(testProgramsDir): removeDir(testProgramsDir) if dirExists(testLinksDir): removeDir(testLinksDir) except: discard # Cleanup failures are not critical