## Property-Based Tests for Package Metadata ## ## **Feature: 01-nip-unified-storage-and-formats, Property 10: Provenance Preservation** ## ## This test verifies that metadata.json accurately reflects the complete build chain ## from source to installation, ensuring full audit trail and traceability. ## ## **Validates: Requirements 7.1, 7.2, 7.3** import std/[unittest, times, json, options, strutils, random] import ../src/nip/metadata # Property-based test generator for random metadata proc generateRandomMetadata(rng: var Rand): PackageMetadata = ## Generate random but valid metadata for property testing let packageNames = @["nginx", "firefox", "postgresql", "redis", "docker"] let maintainers = @["John Doe ", "Jane Smith ", "ACME Corp"] let compilers = @["gcc-13.2.0", "clang-16.0.0", "gcc-12.3.0"] let architectures = @["x86_64", "aarch64", "riscv64"] let source = SourceInfo( origin: "https://github.com/example/" & packageNames[rng.rand(packageNames.high)], maintainer: maintainers[rng.rand(maintainers.high)], upstreamUrl: "https://example.com/" & packageNames[rng.rand(packageNames.high)], sourceHash: "xxh3-" & $rng.rand(1000000..9999999) ) let buildInfo = BuildInfo( compilerVersion: compilers[rng.rand(compilers.high)], compilerFlags: @["-O2", "-march=native"], targetArchitecture: architectures[rng.rand(architectures.high)], buildHash: "xxh3-" & $rng.rand(1000000..9999999), buildTimestamp: now() ) let provenance = ProvenanceChain( sourceDownload: ProvenanceStep( timestamp: now(), action: "source_download", hash: "xxh3-" & $rng.rand(1000000..9999999), verifiedBy: "nip-0.2.0" ), build: ProvenanceStep( timestamp: now(), action: "build", hash: "xxh3-" & $rng.rand(1000000..9999999), verifiedBy: "nip-0.2.0" ), installation: ProvenanceStep( timestamp: now(), action: "installation", hash: "xxh3-" & $rng.rand(1000000..9999999), verifiedBy: "nip-0.2.0" ) ) let dependencies = @[ DependencyInfo( name: "openssl", version: "3.0.0", buildHash: "xxh3-" & $rng.rand(1000000..9999999) ), DependencyInfo( name: "zlib", version: "1.2.13", buildHash: "xxh3-" & $rng.rand(1000000..9999999) ) ] result = generateMetadata( packageName = packageNames[rng.rand(packageNames.high)], version = "1.0.0", formatType = FormatType.NPK, source = source, buildInfo = buildInfo, provenance = some(provenance), dependencies = dependencies ) suite "Property-Based Tests: Provenance Preservation": test "Property 10: Provenance Preservation - Roundtrip preserves all provenance data": ## **Feature: 01-nip-unified-storage-and-formats, Property 10: Provenance Preservation** ## **Validates: Requirements 7.1, 7.2, 7.3** ## ## For any package metadata with provenance chain, serializing to JSON and ## deserializing back should preserve all provenance information exactly. var rng = initRand(42) # Fixed seed for reproducibility # Run 100 iterations with random metadata for i in 1..100: let original = generateRandomMetadata(rng) # Serialize to JSON let jsonStr = toJson(original) # Deserialize back let restored = fromJson(jsonStr) # Verify all fields preserved check restored.packageName == original.packageName check restored.version == original.version check restored.formatType == original.formatType # Verify source info preserved check restored.source.origin == original.source.origin check restored.source.maintainer == original.source.maintainer check restored.source.upstreamUrl == original.source.upstreamUrl check restored.source.sourceHash == original.source.sourceHash # Verify build info preserved check restored.buildInfo.compilerVersion == original.buildInfo.compilerVersion check restored.buildInfo.compilerFlags == original.buildInfo.compilerFlags check restored.buildInfo.targetArchitecture == original.buildInfo.targetArchitecture check restored.buildInfo.buildHash == original.buildInfo.buildHash # Verify provenance chain preserved (CRITICAL for audit trail) check restored.provenance.isSome if restored.provenance.isSome and original.provenance.isSome: let origProv = original.provenance.get() let restProv = restored.provenance.get() # Source download step check restProv.sourceDownload.action == origProv.sourceDownload.action check restProv.sourceDownload.hash == origProv.sourceDownload.hash check restProv.sourceDownload.verifiedBy == origProv.sourceDownload.verifiedBy # Build step check restProv.build.action == origProv.build.action check restProv.build.hash == origProv.build.hash check restProv.build.verifiedBy == origProv.build.verifiedBy # Installation step check restProv.installation.action == origProv.installation.action check restProv.installation.hash == origProv.installation.hash check restProv.installation.verifiedBy == origProv.installation.verifiedBy # Verify dependencies preserved check restored.dependencies.len == original.dependencies.len for j in 0.. 0 check prov.sourceDownload.hash.len > 0 check prov.sourceDownload.verifiedBy.len > 0 check prov.build.action.len > 0 check prov.build.hash.len > 0 check prov.build.verifiedBy.len > 0 check prov.installation.action.len > 0 check prov.installation.hash.len > 0 check prov.installation.verifiedBy.len > 0 test "Property 10: Build hash consistency across provenance chain": ## Verify that build hashes in provenance chain match metadata build hash var rng = initRand(789) for i in 1..50: let metadata = generateRandomMetadata(rng) # Build hash in buildInfo should match build step in provenance if metadata.provenance.isSome: let prov = metadata.provenance.get() # All hashes should use xxh3 format check metadata.buildInfo.buildHash.startsWith("xxh3-") check prov.build.hash.startsWith("xxh3-") # Source hash should match source download step check metadata.source.sourceHash.startsWith("xxh3-") check prov.sourceDownload.hash.startsWith("xxh3-") test "Property 10: Dependency build hashes are preserved in audit trail": ## Verify that dependency build hashes are preserved for complete audit trail var rng = initRand(101112) for i in 1..50: let metadata = generateRandomMetadata(rng) # All dependency build hashes should use xxh3 format for dep in metadata.dependencies: check dep.buildHash.startsWith("xxh3-") check dep.name.len > 0 check dep.version.len > 0 # Serialize and deserialize to verify preservation let jsonStr = toJson(metadata) let restored = fromJson(jsonStr) # Verify all dependency hashes preserved check restored.dependencies.len == metadata.dependencies.len for j in 0..