## Property-Based Tests for NEXTER Manifest - TDD Approach ## ## **Feature:** 01-nip-unified-storage-and-formats ## **Property 3:** Manifest Roundtrip ## **Validates:** Requirements 6.4 ## ## **Property Statement:** ## For any NEXTER manifest, parsing and regenerating SHALL produce semantically equivalent KDL ## ## **TDD Strategy:** ## Thistten FIRST to expose gaps in the implementation. ## Test failures will drive the implementation of parseNEXTERManifest and generateNEXTERManifest. ## We use a MAXIMALLY POPULATED manifest to ensure ALL fields are tested. import std/[unittest, times, options, strutils, tables] import nip/nexter_manifest import nip/manifest_parser # ============================================================================ # Helper: Create Maximally Populated NEXTER Manifest # ============================================================================ proc createFullNEXTERManifest*(): NEXTERManifest = ## Creates a maximally populated NEXTERManifest instance for roundtrip testing. ## This ensures we test ALL fields, not just the minimal required ones. ## ## **Philosophy:** Test the hardest case first. If this passes, simpler cases will too. result = NEXTERManifest( name: "dev-environment", version: parseSemanticVersion("2.1.0-beta.3+build.456"), buildDate: parse("2025-11-20T15:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"), metadata: ContainerInfo( description: "Development environment container with full toolchain.", license: "MIT", homepage: some("https://nexus.os/containers/dev-env"), author: some("DevOps Team"), maintainer: some("devops@nexus.os"), purpose: some("development"), tags: @["development", "toolchain", "isolated"] ), provenance: ProvenanceInfo( source: "https://nexus.os/sources/dev-env-2.1.0.tar.gz", sourceHash: "xxh3-container-source-hash-abc", upstream: some("https://github.com/nexusos/dev-containers"), buildTimestamp: parse("2025-11-20T15:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"), builder: some("ci-bot@nexus.os") ), buildConfig: BuildConfiguration( configureFlags: @["--enable-dev-tools", "--with-debugger"], compilerFlags: @["-O2", "-g", "-march=x86-64"], compilerVersion: "gcc-13.2.0", targetArchitecture: "x86_64", libc: "musl", allocator: "jemalloc", buildSystem: "custom" ), base: BaseConfig( baseImage: some("alpine"), baseVersion: some("3.18"), packages: @["bash", "git", "vim", "gcc", "make"] ), environment: { "PATH": "/usr/local/bin:/usr/bin:/bin", "HOME": "/home/developer", "LANG": "en_US.UTF-8", "TERM": "xterm-256color" }.toTable(), casChunks: @[ ChunkReference( hash: "xxh3-chunk1-base", size: 52428800, # 50MB chunkType: Base, path: "base/alpine-3.18.tar" ), ChunkReference( hash: "xxh3-chunk2-tools", size: 10485760, # 10MB chunkType: Tools, path: "tools/dev-tools.tar" ), ChunkReference( hash: "xxh3-chunk3-config", size: 8192, chunkType: Config, path: "config/container.conf" ) ], namespace: ContainerNamespace( isolationType: "full", capabilities: @["CAP_NET_ADMIN", "CAP_SYS_PTRACE"], mounts: @[ MountSpec( source: "/home/user/projects", target: "/workspace", mountType: "bind", readOnly: false, options: @["rw", "rbind"] ), MountSpec( source: "tmpfs", target: "/tmp", mountType: "tmpfs", readOnly: false, options: @["size=1G"] ) ], devices: @[ DeviceSpec( path: "/dev/null", deviceType: "c", major: 1, minor: 3, permissions: "rwm" ), DeviceSpec( path: "/dev/zero", deviceType: "c", major: 1, minor: 5, permissions: "rwm" ) ] ), startup: StartupConfig( command: @["/bin/bash", "-l"], workingDir: "/workspace", user: some("developer"), entrypoint: some("/usr/local/bin/container-init.sh") ), buildHash: "xxh3-container-build-hash", signature: SignatureInfo( algorithm: "ed25519", keyId: "container-builder-2024", signature: "base64-encoded-container-signature" ) ) # ============================================================================ # Property-Based Tests # ============================================================================ suite "NEXTER Manifest Roundtrip Property Tests (Task 8.1)": test "Property 3: KDL Generation - Verify output structure": ## **Phase 1:** Test KDL generation in isolation ## This verifies generateNEXTERManifest works before testing roundtrip let manifest = createFullNEXTERManifest() let kdlString = generateNEXTERManifest(manifest) echo "\n=== Generated KDL (Phase 1) ===\n" echo kdlString echo "=== End KDL ===\n" # Verify KDL structure check kdlString.contains("container \"dev-environment\"") check kdlString.contains("version \"2.1.0-beta.3+build.456\"") check kdlString.contains("metadata {") check kdlString.contains("provenance {") check kdlString.contains("build_config {") check kdlString.contains("base {") check kdlString.contains("environment {") check kdlString.contains("cas_chunks {") check kdlString.contains("namespace {") check kdlString.contains("startup {") check kdlString.contains("build_hash \"xxh3-container-build-hash\"") check kdlString.contains("signature {") test "Property 3: Full Roundtrip - ALL FIELDS": ## **Phase 2:** Full roundtrip test ## This is the MAIN test that will expose all gaps ## ## **Feature: 01-nip-unified-storage-and-formats, Property 3: Manifest Roundtrip** ## **Validates: Requirements 6.4** let originalManifest = createFullNEXTERManifest() # Step 1: Generate KDL let kdlString = generateNEXTERManifest(originalManifest) check kdlString.len > 0 # Step 2: Parse KDL back let parsedManifest = parseNEXTERManifest(kdlString) # Step 3: Verify ALL fields preserved if parsedManifest.name != originalManifest.name: echo "Name mismatch: ", parsedManifest.name, " != ", originalManifest.name check parsedManifest.name == originalManifest.name if parsedManifest.version != originalManifest.version: echo "Version mismatch:" echo " Parsed: ", parsedManifest.version echo " Original: ", originalManifest.version check parsedManifest.version == originalManifest.version if parsedManifest.buildHash != originalManifest.buildHash: echo "BuildHash mismatch: ", parsedManifest.buildHash, " != ", originalManifest.buildHash check parsedManifest.buildHash == originalManifest.buildHash # Step 4: Verify deterministic generation let kdlString2 = generateNEXTERManifest(parsedManifest) if kdlString != kdlString2: echo "KDL strings don't match!" echo "=== Original KDL ===" echo kdlString echo "=== Regenerated KDL ===" echo kdlString2 check kdlString == kdlString2 suite "NEXTER Manifest Validation Tests": test "Validate manifest with valid xxh3 hashes": let validManifest = NEXTERManifest( name: "valid-container", version: parseSemanticVersion("1.0.0"), buildDate: now(), metadata: ContainerInfo( description: "Valid container", license: "MIT" ), provenance: ProvenanceInfo( source: "https://example.com/valid.tar.gz", sourceHash: "xxh3-valid123", buildTimestamp: now() ), buildConfig: BuildConfiguration( compilerVersion: "gcc-13.2.0", targetArchitecture: "x86_64", libc: "musl", allocator: "default", buildSystem: "custom" ), base: BaseConfig( baseImage: some("alpine"), baseVersion: some("3.18"), packages: @[] ), environment: initTable[string, string](), casChunks: @[], namespace: ContainerNamespace( isolationType: "full", capabilities: @[], mounts: @[], devices: @[] ), startup: StartupConfig( command: @["/bin/sh"], workingDir: "/", user: none(string), entrypoint: none(string) ), buildHash: "xxh3-build123", signature: SignatureInfo( algorithm: "ed25519", keyId: "test-key", signature: "test-signature" ) ) let issues = validateNEXTERManifest(validManifest) check issues.len == 0 test "Property 3: Determinism - Same input produces same output": ## **Feature: 01-nip-unified-storage-and-formats, Property 9: Manifest Hash Determinism** ## **Validates: Requirements 6.4, 7.5** ## ## This test verifies that generateNEXTERManifest is deterministic let manifest = createFullNEXTERManifest() # Generate KDL multiple times let kdl1 = generateNEXTERManifest(manifest) let kdl2 = generateNEXTERManifest(manifest) let kdl3 = generateNEXTERManifest(manifest) # All outputs should be identical check kdl1 == kdl2 check kdl2 == kdl3 check kdl1 == kdl3