## Integrity Manager Tests - Canonical Leaf Hashing & Parallel Processing ## Comprehensive test suite for the corrected Merkle tree implementation import std/[unittest, os, strutils, times, tables] import nip/integrity suite "Canonical Leaf Hashing - Determinism": setup: let testDir = "/tmp/test_canonical" createDir(testDir) createDir(testDir / "lib") createDir(testDir / "data") # Create identical files in different directories writeFile(testDir / "lib" / "foo.txt", "Identical content") writeFile(testDir / "data" / "foo.txt", "Identical content") writeFile(testDir / "unique.txt", "Unique content") teardown: removeDir("/tmp/test_canonical") test "Identical content in different paths produces different canonical hashes": # This is the CRITICAL test for path-aware hashing let libHash = calculateCanonicalHash("lib/foo.txt", "content-hash-123") let dataHash = calculateCanonicalHash("data/foo.txt", "content-hash-123") check libHash != dataHash echo " lib/foo.txt: " & libHash echo " data/foo.txt: " & dataHash test "Moving a file changes the Merkle root": var cache1 = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) # Calculate Merkle root with original structure let tree1 = buildMerkleTree(testDir, cache1) let root1 = tree1.rootHash # Move a file moveFile(testDir / "unique.txt", testDir / "lib" / "unique.txt") var cache2 = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) # Calculate Merkle root with new structure let tree2 = buildMerkleTree(testDir, cache2) let root2 = tree2.rootHash check root1 != root2 echo " Before move: " & root1 echo " After move: " & root2 test "Canonical leaves are sorted by relative path": var cache = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) let leaves = collectCanonicalLeaves(testDir, cache, parallel=false) # Verify sorting for i in 0..<(leaves.len - 1): check leaves[i].relativePath < leaves[i+1].relativePath echo " Sorted paths:" for leaf in leaves: echo " " & leaf.relativePath suite "Parallel Processing": setup: let testDir = "/tmp/test_parallel" createDir(testDir) # Create many files to benefit from parallelization for i in 1..50: writeFile(testDir / ("file" & $i & ".txt"), "Content " & $i) teardown: removeDir("/tmp/test_parallel") test "Parallel and sequential produce identical results": var cache1 = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) var cache2 = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) # Sequential processing let start1 = cpuTime() let leaves1 = collectCanonicalLeaves(testDir, cache1, parallel=false) let time1 = cpuTime() - start1 # Parallel processing let start2 = cpuTime() let leaves2 = collectCanonicalLeaves(testDir, cache2, parallel=true) let time2 = cpuTime() - start2 # Results must be identical check leaves1.len == leaves2.len for i in 0.. 0 test "Single file tree": let singleDir = "/tmp/test_single" createDir(singleDir) writeFile(singleDir / "only.txt", "Only file") defer: removeDir(singleDir) var cache = IntegrityCache( fileHashes: initTable[string, string](), dirHashes: initTable[string, string](), lastModified: initTable[string, int64]() ) let tree = buildMerkleTree(singleDir, cache) check tree.totalFiles == 1 check tree.rootHash.len > 0 suite "Content Verification": setup: let testDir = "/tmp/test_verify_canonical" createDir(testDir) writeFile(testDir / "test.txt", "Test content") teardown: removeDir("/tmp/test_verify_canonical") test "Successful verification": var manager = newIntegrityManager("/tmp") # Calculate expected hash let tree = buildMerkleTree(testDir, manager.cache) let expectedHash = tree.rootHash # Verify let result = verifyContent(testDir, expectedHash, manager) check result.success check result.actualHash == expectedHash check result.errors.len == 0 check result.verifiedFiles == 1 test "Failed verification - hash mismatch": var manager = newIntegrityManager("/tmp") let wrongHash = "blake2b-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" let result = verifyContent(testDir, wrongHash, manager) check not result.success check result.actualHash != wrongHash check result.errors.len > 0 check result.errors[0].errorType == HashMismatch suite "Build Hash Calculation": test "calculateBuildHash convenience function": let testDir = "/tmp/test_build_hash" createDir(testDir) writeFile(testDir / "main.nim", "echo \"Hello\"") writeFile(testDir / "config.nims", "switch(\"opt\", \"size\")") defer: removeDir(testDir) let buildHash = calculateBuildHash(testDir) check buildHash.len > 0 check buildHash.startsWith("blake2b-") # Should be deterministic let buildHash2 = calculateBuildHash(testDir) check buildHash == buildHash2 when isMainModule: echo "Running canonical leaf hashing and parallel processing tests..."