## Property-Based Tests for Package Metadata (metadata.json) ## ## **Feature:** 01-nip-unified-storage-and-formats ## **Property 10:** Provenance Preservation ## **Validates:** Requirements 7.1, 7.2, 7.3 ## ## **Property Statement:** ## For any package, the metadata.json SHALL accurately reflect the complete build chain ## ## **TDD Strategy:** ## Test-driven approach to ensure complete provenance tracking across all formats. import std/[unittest, times, options, tables, json] import nip/package_metadata import nip/manifest_parser # ============================================================================ # Helper: Create Maximally Populated Metadata # ============================================================================ proc createFullMetadata*(formatType: string): PackageMetadata = ## Create maximally populated metadata for testing ## Tests all fields including optional ones var envVars = initTable[string, string]() envVars["CC"] = "gcc" envVars["CFLAGS"] = "-O2" envVars["PATH"] = "/usr/bin:/bin" var extensibleMeta = initTable[string, string]() extensibleMeta["custom_field"] = "custom_value" extensibleMeta["build_id"] = "12345" result = PackageMetadata( formatType: formatType, formatVersion: "1.0", name: "test-package", version: "1.2.3-alpha.1+build.2", description: "Test package for metadata validation", license: "MIT", tags: @["test", "metadata", "provenance"], source: SourceProvenance( origin: "https://github.com/test/package", sourceHash: "xxh3-source-abc123", upstream: some("https://upstream.org/package"), upstreamVersion: some("1.2.3"), fetchedAt: parse("2025-11-20T10:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"), fetchMethod: "git" ), build: BuildProvenance( buildTimestamp: parse("2025-11-20T11:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"), builder: "ci-bot@nexus.os", buildHost: "build-server-01", buildDuration: some(3600), buildEnvironment: BuildEnvironment( compilerVersion: "gcc-13.2.0", compilerFlags: @["-O3", "-march=native", "-flto"], configureFlags: @["--prefix=/usr", "--enable-shared"], targetArchitecture: "x86_64", libc: "musl", allocator: "jemalloc", buildSystem: "cmake", environmentVars: envVars ) ), installation: InstallationProvenance( installedAt: parse("2025-11-20T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"), installedBy: "admin", installPath: "/Programs/TestPackage/1.2.3", installMethod: "nip install", installHost: "workstation-01" ), hashes: IntegrityHashes( sourceHash: "xxh3-source-abc123", buildHash: "xxh3-build-def456", artifactHash: "xxh3-artifact-ghi789", manifestHash: "xxh3-manifest-jkl012" ), signatures: @[ SignatureRecord( algorithm: "ed25519", keyId: "builder-key-2024", signature: "base64-signature-1", signedBy: "builder@nexus.os", signedAt: parse("2025-11-20T11:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'") ), SignatureRecord( algorithm: "ed25519", keyId: "maintainer-key-2024", signature: "base64-signature-2", signedBy: "maintainer@nexus.os", signedAt: parse("2025-11-20T11:45:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'") ) ], metadata: extensibleMeta ) # ============================================================================ # Property-Based Tests # ============================================================================ suite "Package Metadata Property Tests (Task 9.1)": test "Property 10: JSON Generation - Verify structure": ## **Phase 1:** Test JSON generation in isolation let metadata = createFullMetadata("npk") let jsonString = generateMetadataJson(metadata) echo "\n=== Generated JSON (Phase 1) ===\n" echo jsonString echo "\n=== End JSON ===\n" # Verify JSON structure let json = parseJson(jsonString) check json.hasKey("format_type") check json.hasKey("source_provenance") check json.hasKey("build_provenance") check json.hasKey("installation_provenance") check json.hasKey("integrity_hashes") check json.hasKey("signatures") # Verify required fields check json["format_type"].getStr() == "npk" check json["name"].getStr() == "test-package" check json["source_provenance"]["source_hash"].getStr().startsWith("xxh3-") check json["integrity_hashes"]["build_hash"].getStr().startsWith("xxh3-") test "Property 10: Full Roundtrip - ALL FIELDS": ## **Phase 2:** Full roundtrip test ## ## **Feature: 01-nip-unified-storage-and-formats, Property 10: Provenance Preservation** ## **Validates: Requirements 7.1, 7.2, 7.3** for formatType in ["npk", "nip", "nexter"]: let originalMetadata = createFullMetadata(formatType) # Step 1: Generate JSON let jsonString = generateMetadataJson(originalMetadata) check jsonString.len > 0 # Step 2: Parse JSON back let parsedMetadata = parseMetadataJson(jsonString) # Step 3: Verify ALL fields preserved check parsedMetadata.formatType == originalMetadata.formatType check parsedMetadata.name == originalMetadata.name check parsedMetadata.version == originalMetadata.version check parsedMetadata.hashes.buildHash == originalMetadata.hashes.buildHash check parsedMetadata.hashes.sourceHash == originalMetadata.hashes.sourceHash check parsedMetadata.build.builder == originalMetadata.build.builder check parsedMetadata.source.origin == originalMetadata.source.origin # Step 4: Verify deterministic generation let jsonString2 = generateMetadataJson(parsedMetadata) # Parse both to compare structure (JSON key order may vary) let json1 = parseJson(jsonString) let json2 = parseJson(jsonString2) check json1 == json2 suite "Package Metadata Validation Tests": test "Validate metadata with valid xxh3 hashes": let validMetadata = createFullMetadata("npk") let issues = validateMetadata(validMetadata) check issues.len == 0 test "Reject metadata with invalid hash format": var invalidMetadata = createFullMetadata("npk") invalidMetadata.hashes.buildHash = "sha256-invalid" let issues = validateMetadata(invalidMetadata) check issues.len > 0 check issues[0].contains("xxh3-128") test "Reject metadata with invalid signature algorithm": var invalidMetadata = createFullMetadata("npk") invalidMetadata.signatures[0].algorithm = "rsa" let issues = validateMetadata(invalidMetadata) check issues.len > 0 check issues[0].contains("ed25519") test "Property 10: Determinism - Same input produces same JSON structure": ## Verify that generateMetadataJson is deterministic ## (JSON key order may vary, but structure should be identical) let metadata = createFullMetadata("nip") # Generate JSON multiple times let json1 = parseJson(generateMetadataJson(metadata)) let json2 = parseJson(generateMetadataJson(metadata)) let json3 = parseJson(generateMetadataJson(metadata)) # All outputs should be structurally identical check json1 == json2 check json2 == json3 check json1 == json3