## Unit Tests for Build Synthesis ## ## Tests for the build synthesis module which creates deterministic builds ## from unified variant profiles and stores them in the CAS. import std/[unittest, options, tables, os, tempfiles, strutils, times] import ../src/nip/resolver/build_synthesis import ../src/nip/resolver/variant_types suite "Build Synthesis Tests": test "Build hash calculation is deterministic": ## Test that the same configuration always produces the same hash # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.addFlag("security", "hardened") profile.calculateHash() # Create build config let config = newBuildConfig( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", compilerVersion = "gcc-13.2.0", compilerFlags = @["-O2", "-march=native"], configureFlags = @["--with-http_ssl_module"], targetArchitecture = "x86_64", libc = "musl", allocator = "jemalloc" ) # Calculate hash twice let hash1 = calculateBuildHash(config) let hash2 = calculateBuildHash(config) # Hashes should be identical check hash1 == hash2 check hash1.startsWith("xxh3-") test "Different configurations produce different hashes": ## Test that different configurations produce different hashes # Create first variant profile var profile1 = newVariantProfile() profile1.addFlag("optimization", "lto") profile1.calculateHash() # Create second variant profile (different) var profile2 = newVariantProfile() profile2.addFlag("optimization", "o3") profile2.calculateHash() # Create configs with different profiles let config1 = newBuildConfig( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile1, sourceHash = "blake3-abc123" ) let config2 = newBuildConfig( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile2, sourceHash = "blake3-abc123" ) # Hashes should be different let hash1 = calculateBuildHash(config1) let hash2 = calculateBuildHash(config2) check hash1 != hash2 test "Build synthesis creates valid result": ## Test that build synthesis creates a valid BuildSynthesisResult # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize build let result = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) # Verify result check result.buildHash.startsWith("xxh3-") check result.casID == result.buildHash check result.buildConfig.packageName == "nginx" check result.buildConfig.packageVersion == "1.24.0" test "CAS storage and retrieval": ## Test storing and retrieving builds from CAS # Create temporary CAS directory let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix() createDir(casRoot) try: # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize build let result = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) # Store in CAS let casID = storeBuildInCAS(result, casRoot) # Verify CAS ID check casID == result.casID check casID.startsWith("xxh3-") # Retrieve from CAS let retrieved = retrieveBuildFromCAS(casID, casRoot) # Verify retrieved build check retrieved.buildHash == result.buildHash check retrieved.buildConfig.packageName == "nginx" check retrieved.buildConfig.packageVersion == "1.24.0" finally: # Clean up removeDir(casRoot) test "Build hash verification": ## Test verifying build hashes # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Create config let config = newBuildConfig( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) # Calculate hash let hash = calculateBuildHash(config) # Verify hash check verifyBuildHash(hash, config) == true # Verify with wrong hash fails check verifyBuildHash("xxh3-wronghash", config) == false test "Build identity comparison": ## Test comparing builds for identity # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize two builds with same config let result1 = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) let result2 = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) # Builds should be identical check isBuildIdentical(result1, result2) == true test "Different source hashes produce different builds": ## Test that different source hashes produce different build hashes # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize builds with different source hashes let result1 = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123" ) let result2 = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-def456" ) # Build hashes should be different check result1.buildHash != result2.buildHash test "Canonical representation is deterministic": ## Test that canonical representation is deterministic # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.addFlag("security", "hardened") profile.calculateHash() # Create config let config = newBuildConfig( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", compilerFlags = @["-O2", "-march=native", "-fPIC"], configureFlags = @["--with-ssl", "--with-http2"] ) # Get canonical representation twice let canonical1 = config.toCanonical() let canonical2 = config.toCanonical() # Should be identical check canonical1 == canonical2 # Should contain all components check canonical1.contains("nginx") check canonical1.contains("1.24.0") check canonical1.contains("blake3-abc123") check canonical1.contains("gcc-13.2.0") test "Build synthesis with custom compiler flags": ## Test build synthesis with custom compiler flags # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "o3") profile.calculateHash() # Synthesize with custom flags let result = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", compilerVersion = "clang-17.0.0", compilerFlags = @["-O3", "-march=native", "-flto"], configureFlags = @["--with-http_ssl_module", "--with-http_v2_module"] ) # Verify custom flags are in config check result.buildConfig.compilerVersion == "clang-17.0.0" check result.buildConfig.compilerFlags.contains("-O3") check result.buildConfig.compilerFlags.contains("-flto") check result.buildConfig.configureFlags.contains("--with-http_ssl_module") test "Build synthesis with different architectures": ## Test build synthesis with different target architectures # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize for x86_64 let resultX86 = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", targetArchitecture = "x86_64" ) # Synthesize for aarch64 let resultARM = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", targetArchitecture = "aarch64" ) # Build hashes should be different check resultX86.buildHash != resultARM.buildHash check resultX86.buildConfig.targetArchitecture == "x86_64" check resultARM.buildConfig.targetArchitecture == "aarch64" test "Build synthesis with different libc": ## Test build synthesis with different libc types # Create variant profile var profile = newVariantProfile() profile.addFlag("optimization", "lto") profile.calculateHash() # Synthesize with musl let resultMusl = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", libc = "musl" ) # Synthesize with glibc let resultGlibc = synthesizeBuild( packageName = "nginx", packageVersion = "1.24.0", variantProfile = profile, sourceHash = "blake3-abc123", libc = "glibc" ) # Build hashes should be different check resultMusl.buildHash != resultGlibc.buildHash check resultMusl.buildConfig.libc == "musl" check resultGlibc.buildConfig.libc == "glibc"