nip/tests/test_build_synthesis_proper...

204 lines
7.7 KiB
Nim

## Property-Based Tests for Build Synthesis
##
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## Property: For any build configuration, calculating the build hash multiple times
## produces the same result. This ensures reproducibility - same inputs always
## produce same outputs.
import std/[unittest, random, strutils, sequtils, times]
import ../src/nip/resolver/build_synthesis
import ../src/nip/resolver/variant_types
# Generator for random variant profiles
proc generateRandomVariantProfile(): VariantProfile =
var profile = newVariantProfile()
# Add random domains
let domains = @["optimization", "security", "features", "network"]
let flags = @["lto", "hardened", "wayland", "vulkan", "ipv6", "ssl", "zstd"]
for domain in domains:
if rand(0..1) == 0: # 50% chance to include domain
let numFlags = rand(1..3)
for _ in 0..<numFlags:
let flag = flags[rand(0..flags.len-1)]
profile.addFlag(domain, flag)
profile.calculateHash()
return profile
# Generator for random build configs
proc generateRandomBuildConfig(): BuildConfig =
let packageNames = @["nginx", "postgresql", "redis", "openssl", "zlib"]
let versions = @["1.0.0", "2.1.0", "3.0.0", "1.24.0", "15.3"]
let compilers = @["gcc-13.2.0", "clang-17.0.0", "gcc-12.0.0"]
let archs = @["x86_64", "aarch64", "riscv64"]
let libcs = @["musl", "glibc"]
let allocators = @["jemalloc", "tcmalloc", "default"]
let profile = generateRandomVariantProfile()
let config = newBuildConfig(
packageName = packageNames[rand(0..packageNames.len-1)],
packageVersion = versions[rand(0..versions.len-1)],
variantProfile = profile,
sourceHash = "blake3-" & toHex(rand(0..0xFFFFFF)),
compilerVersion = compilers[rand(0..compilers.len-1)],
compilerFlags = @["-O2", "-march=native"],
configureFlags = @["--with-ssl"],
targetArchitecture = archs[rand(0..archs.len-1)],
libc = libcs[rand(0..libcs.len-1)],
allocator = allocators[rand(0..allocators.len-1)]
)
return config
suite "Build Synthesis Property Tests":
test "Property 7: Build Determinism - Hash calculation is deterministic":
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## For any build configuration, calculating the build hash multiple times
## produces the same result. This ensures reproducibility.
##
## Property: ∀ config ∈ BuildConfig: calculateBuildHash(config) = calculateBuildHash(config)
# Run property test with 100 iterations
for iteration in 0..<100:
# Generate random build config
let config = generateRandomBuildConfig()
# Calculate hash multiple times
let hash1 = calculateBuildHash(config)
let hash2 = calculateBuildHash(config)
let hash3 = calculateBuildHash(config)
# All hashes should be identical
check hash1 == hash2
check hash2 == hash3
check hash1 == hash3
# Hash should have correct format
check hash1.startsWith("xxh3-")
test "Property 7: Build Determinism - Synthesis produces deterministic hashes":
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## For any variant profile, synthesizing a build multiple times produces
## the same build hash. This ensures reproducibility across builds.
##
## Property: ∀ profile ∈ VariantProfile: synthesizeBuild(..., profile).buildHash = synthesizeBuild(..., profile).buildHash
# Run property test with 100 iterations
for iteration in 0..<100:
# Generate random variant profile
let profile = generateRandomVariantProfile()
# Synthesize builds multiple times
let result1 = synthesizeBuild(
packageName = "test-package",
packageVersion = "1.0.0",
variantProfile = profile,
sourceHash = "blake3-abc123"
)
let result2 = synthesizeBuild(
packageName = "test-package",
packageVersion = "1.0.0",
variantProfile = profile,
sourceHash = "blake3-abc123"
)
let result3 = synthesizeBuild(
packageName = "test-package",
packageVersion = "1.0.0",
variantProfile = profile,
sourceHash = "blake3-abc123"
)
# All build hashes should be identical
check result1.buildHash == result2.buildHash
check result2.buildHash == result3.buildHash
check result1.buildHash == result3.buildHash
test "Property 7: Build Determinism - Canonical representation is deterministic":
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## For any build configuration, the canonical representation is deterministic.
## This ensures that the same configuration always produces the same hash.
##
## Property: ∀ config ∈ BuildConfig: toCanonical(config) = toCanonical(config)
# Run property test with 100 iterations
for iteration in 0..<100:
# Generate random build config
let config = generateRandomBuildConfig()
# Get canonical representation multiple times
let canonical1 = config.toCanonical()
let canonical2 = config.toCanonical()
let canonical3 = config.toCanonical()
# All canonical representations should be identical
check canonical1 == canonical2
check canonical2 == canonical3
check canonical1 == canonical3
test "Property 7: Build Determinism - Hash verification is consistent":
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## For any build configuration and its hash, verification always produces
## the same result. This ensures consistent verification.
##
## Property: ∀ config ∈ BuildConfig, hash = calculateBuildHash(config):
## verifyBuildHash(hash, config) = verifyBuildHash(hash, config)
# Run property test with 100 iterations
for iteration in 0..<100:
# Generate random build config
let config = generateRandomBuildConfig()
# Calculate hash
let hash = calculateBuildHash(config)
# Verify hash multiple times
let verify1 = verifyBuildHash(hash, config)
let verify2 = verifyBuildHash(hash, config)
let verify3 = verifyBuildHash(hash, config)
# All verifications should be identical and true
check verify1 == verify2
check verify2 == verify3
check verify1 == true
test "Property 7: Build Determinism - Different configs produce different hashes":
## **Feature: nip-dependency-resolution, Property 7: Build Determinism**
## **Validates: Requirements 8.1, 8.2, 8.3**
##
## For any two different build configurations, they should produce different
## hashes (with very high probability). This ensures uniqueness.
##
## Property: ∀ config1, config2 ∈ BuildConfig where config1 ≠ config2:
## calculateBuildHash(config1) ≠ calculateBuildHash(config2)
# Run property test with 50 iterations (fewer because we need pairs)
for iteration in 0..<50:
# Generate two random build configs
let config1 = generateRandomBuildConfig()
let config2 = generateRandomBuildConfig()
# Calculate hashes
let hash1 = calculateBuildHash(config1)
let hash2 = calculateBuildHash(config2)
# If configs are different, hashes should be different
# (with very high probability - hash collisions are extremely rare)
if config1.toCanonical() != config2.toCanonical():
check hash1 != hash2