## test_nix_adapter.nim ## Unit tests for NixAdapter import std/[unittest, tables, os, strutils, options] import ../src/nimpak/build/[types, nix_adapter] suite "NixAdapter Tests": test "NixAdapter initialization": let adapter = newNixAdapter() check adapter != nil check adapter.name == "nix" check adapter.nixpkgsPath == "" check adapter.storeDir == "/nix/store" check adapter.packageCount == 100000 test "NixAdapter availability check": let adapter = newNixAdapter() let available = adapter.isAvailable() # Should match actual system state check available == dirExists("/nix") test "Package name validation - valid names": let adapter = newNixAdapter() let validNames = @[ "firefox", "nixpkgs.firefox", "my-package", "package_name", "package.with.dots", "Package123" ] for name in validNames: let request = BuildRequest( packageName: name, version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir(), verbose: false ) # Should not raise ValidationError try: discard adapter.buildPackage(request) except ValidationError: fail() test "Package name validation - invalid names": let adapter = newNixAdapter() let invalidNames = @[ "", "../etc/passwd", "/absolute/path", "package;rm -rf /", "package`whoami`", "package$(whoami)", "a" & "b".repeat(300) # Too long ] for name in invalidNames: let request = BuildRequest( packageName: name, version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir(), verbose: false ) let result = adapter.buildPackage(request) check result.success == false check result.errors.len > 0 test "Nix expression generation - no overrides": let adapter = newNixAdapter() # We can't directly test the private generateNixExpression, # but we can test it through buildPackage let request = BuildRequest( packageName: "hello", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) # This will generate the expression file discard adapter.buildPackage(request) # Check that expression file was created let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-hello.nix" if fileExists(exprFile): let content = readFile(exprFile) check "with import {};" in content check "hello" in content test "Nix expression generation - with overrides": let adapter = newNixAdapter() let request = BuildRequest( packageName: "firefox", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @["waylandSupport = true", "enableLTO = true"], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) discard adapter.buildPackage(request) let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-firefox.nix" if fileExists(exprFile): let content = readFile(exprFile) check "with import {};" in content check "firefox" in content check ".override" in content check "waylandSupport = true" in content check "enableLTO = true" in content test "Override key validation - valid keys": let adapter = newNixAdapter() let validFlags = @[ "waylandSupport = true", "enable-feature = false", "with_option = true", "flag123 = true" ] let request = BuildRequest( packageName: "test", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: validFlags, cacheDir: getTempDir() / "nip-test-cache", verbose: false ) # Should not raise ValidationError let result = adapter.buildPackage(request) # May fail at nix-build stage, but not at validation if not result.success and result.errors.len > 0: check "Invalid override key" notin result.errors[0] test "Override key validation - invalid keys": let adapter = newNixAdapter() # Test with malicious override key let request = BuildRequest( packageName: "test", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @["bad;key = true"], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) let result = adapter.buildPackage(request) check result.success == false test "Build result structure": let adapter = newNixAdapter() let request = BuildRequest( packageName: "test-package", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) let result = adapter.buildPackage(request) # Check result structure check result.source == "nix" check result.packageName == "test-package" # success may be false if nix-build fails, which is expected in test environment test "Cache directory creation": let cacheDir = getTempDir() / "nip-test-cache-new" # Remove if exists if dirExists(cacheDir): removeDir(cacheDir) let adapter = newNixAdapter() let request = BuildRequest( packageName: "test", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: cacheDir, verbose: false ) discard adapter.buildPackage(request) # Check that cache directory was created check dirExists(cacheDir / "nix") test "Package name sanitization for filenames": let adapter = newNixAdapter() # Package name with dots should be sanitized for filename let request = BuildRequest( packageName: "nixpkgs.firefox", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) discard adapter.buildPackage(request) # Check that file was created with sanitized name let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-nixpkgs_firefox.nix" check fileExists(exprFile) test "Variant flags in build result": let adapter = newNixAdapter() var variantFlags = initTable[string, seq[string]]() variantFlags["graphics"] = @["wayland", "vulkan"] variantFlags["audio"] = @["pipewire"] let request = BuildRequest( packageName: "test", version: "", variantFlags: variantFlags, sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) let result = adapter.buildPackage(request) # Variant flags should be preserved in result check result.variantDomains.hasKey("graphics") check result.variantDomains.hasKey("audio") test "Error handling - build failure": let adapter = newNixAdapter() # Use a package name that will definitely fail let request = BuildRequest( packageName: "this-package-definitely-does-not-exist-12345", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: false ) let result = adapter.buildPackage(request) # Should fail gracefully check result.success == false check result.errors.len > 0 test "Verbose mode": let adapter = newNixAdapter() let request = BuildRequest( packageName: "test", version: "", variantFlags: initTable[string, seq[string]](), sourceFlags: @[], cacheDir: getTempDir() / "nip-test-cache", verbose: true ) # Should not crash with verbose mode discard adapter.buildPackage(request) # Only run search tests if Nix is actually available if dirExists("/nix"): suite "NixAdapter Search Tests (Nix Available)": test "Search for existing package": let adapter = newNixAdapter() # Search for a package that should exist let result = adapter.searchPackage("hello") # May or may not find it depending on nixpkgs availability if result.isSome: let info = result.get() check info.name == "hello" check info.source == "nix" check info.available == true test "Search for non-existent package": let adapter = newNixAdapter() let result = adapter.searchPackage("this-package-does-not-exist-xyz123") # Should return none check result.isNone test "Search with invalid package name": let adapter = newNixAdapter() let result = adapter.searchPackage("../etc/passwd") # Should return none due to validation check result.isNone