217 lines
6.4 KiB
Nim
217 lines
6.4 KiB
Nim
## Property-Based Tests for Unified Storage
|
|
##
|
|
## Feature: 01-nip-unified-storage-and-formats
|
|
## Property 2: Chunk Integrity
|
|
## Validates: Requirements 2.2
|
|
##
|
|
## This test verifies that xxh3 hash integrity is maintained for all chunks:
|
|
## For any CAS chunk, the xxh3 hash SHALL match the chunk content after decompression
|
|
|
|
import std/[unittest, os, random, strutils]
|
|
import ../src/nip/unified_storage
|
|
import ../src/nip/xxhash
|
|
|
|
# Simple property-based testing framework
|
|
type
|
|
TestCase = object
|
|
data: string
|
|
description: string
|
|
|
|
proc generateRandomData(size: int): string =
|
|
## Generate random data for testing
|
|
result = newString(size)
|
|
for i in 0..<size:
|
|
result[i] = char(rand(255))
|
|
|
|
proc generateTestCases(count: int): seq[TestCase] =
|
|
## Generate test cases with various data patterns
|
|
result = @[]
|
|
|
|
# Edge cases
|
|
result.add(TestCase(data: "", description: "empty string"))
|
|
result.add(TestCase(data: "a", description: "single character"))
|
|
result.add(TestCase(data: "Hello, World!", description: "simple text"))
|
|
|
|
# Random data of various sizes
|
|
for i in 0..<count:
|
|
let size = rand(1..10000)
|
|
result.add(TestCase(
|
|
data: generateRandomData(size),
|
|
description: "random data size=" & $size
|
|
))
|
|
|
|
# Repeated patterns
|
|
result.add(TestCase(data: "a".repeat(1000), description: "repeated character"))
|
|
result.add(TestCase(data: "ab".repeat(500), description: "repeated pattern"))
|
|
|
|
# Binary data
|
|
var binaryData = ""
|
|
for i in 0..255:
|
|
binaryData.add(char(i))
|
|
result.add(TestCase(data: binaryData, description: "all byte values"))
|
|
|
|
suite "Unified Storage - Property Tests":
|
|
|
|
setup:
|
|
# Create temporary test directory
|
|
let testRoot = getTempDir() / "nip_test_" & $rand(100000)
|
|
let storageRoot = initStorageRoot(testRoot)
|
|
discard createStorageStructure(storageRoot)
|
|
|
|
teardown:
|
|
# Clean up test directory
|
|
try:
|
|
removeDir(testRoot)
|
|
except:
|
|
discard
|
|
|
|
test "Property 2: Chunk Integrity - Hash matches content after storage":
|
|
## **Feature: 01-nip-unified-storage-and-formats, Property 2: Chunk Integrity**
|
|
## **Validates: Requirements 2.2**
|
|
##
|
|
## For any chunk data, calculating the hash and then verifying it
|
|
## should always succeed. This ensures hash integrity is maintained.
|
|
|
|
let testCases = generateTestCases(100)
|
|
var passCount = 0
|
|
var failCount = 0
|
|
|
|
for tc in testCases:
|
|
# Calculate hash
|
|
let hash = calculateXXH3(tc.data)
|
|
|
|
# Verify hash matches original data
|
|
let verified = verifyXXH3(tc.data, hash)
|
|
|
|
if verified:
|
|
passCount.inc
|
|
else:
|
|
failCount.inc
|
|
echo "FAILED: ", tc.description
|
|
echo " Data length: ", tc.data.len
|
|
echo " Hash: ", $hash
|
|
|
|
echo "\nProperty Test Results:"
|
|
echo " Total cases: ", testCases.len
|
|
echo " Passed: ", passCount
|
|
echo " Failed: ", failCount
|
|
|
|
check failCount == 0
|
|
check passCount == testCases.len
|
|
|
|
test "Property 2: Chunk Integrity - Hash is deterministic":
|
|
## Hashing the same data multiple times should produce the same hash
|
|
|
|
let testData = "NexusOS unified storage test data"
|
|
var hashes: seq[XXH3Hash] = @[]
|
|
|
|
# Calculate hash 100 times
|
|
for i in 0..<100:
|
|
hashes.add(calculateXXH3(testData))
|
|
|
|
# All hashes should be identical
|
|
let firstHash = hashes[0]
|
|
for h in hashes:
|
|
check h == firstHash
|
|
|
|
test "Property 2: Chunk Integrity - Different data produces different hashes":
|
|
## Different data should produce different hashes (collision resistance)
|
|
|
|
var hashes: seq[XXH3Hash] = @[]
|
|
var dataSet: seq[string] = @[]
|
|
|
|
# Generate 1000 different data samples
|
|
for i in 0..<1000:
|
|
let data = generateRandomData(rand(10..1000))
|
|
dataSet.add(data)
|
|
hashes.add(calculateXXH3(data))
|
|
|
|
# Check for collisions
|
|
var collisions = 0
|
|
for i in 0..<hashes.len:
|
|
for j in (i+1)..<hashes.len:
|
|
if hashes[i] == hashes[j] and dataSet[i] != dataSet[j]:
|
|
collisions.inc
|
|
echo "COLLISION DETECTED:"
|
|
echo " Data 1 length: ", dataSet[i].len
|
|
echo " Data 2 length: ", dataSet[j].len
|
|
echo " Hash: ", $hashes[i]
|
|
|
|
echo "\nCollision Test Results:"
|
|
echo " Total comparisons: ", (hashes.len * (hashes.len - 1)) div 2
|
|
echo " Collisions found: ", collisions
|
|
|
|
check collisions == 0
|
|
|
|
test "Property 2: Chunk Integrity - Byte sequence hashing":
|
|
## Hashing byte sequences should work correctly
|
|
|
|
let testCases = generateTestCases(50)
|
|
|
|
for tc in testCases:
|
|
# Convert string to byte sequence
|
|
var bytes: seq[byte] = @[]
|
|
for c in tc.data:
|
|
bytes.add(byte(c))
|
|
|
|
# Hash both string and bytes
|
|
let stringHash = calculateXXH3(tc.data)
|
|
let bytesHash = calculateXXH3(bytes)
|
|
|
|
# They should produce the same hash
|
|
check stringHash == bytesHash
|
|
|
|
test "Storage Structure Creation":
|
|
## Verify that storage structure is created correctly
|
|
|
|
let root = initStorageRoot()
|
|
check createStorageStructure(root)
|
|
check verifyStorageStructure(root)
|
|
|
|
# Verify all directories exist
|
|
check dirExists(root.basePath)
|
|
check dirExists(root.casPath)
|
|
check dirExists(root.casPath / "chunks")
|
|
check dirExists(root.casPath / "refs")
|
|
check dirExists(root.casPath / "refs" / "npks")
|
|
check dirExists(root.casPath / "refs" / "nips")
|
|
check dirExists(root.casPath / "refs" / "nexters")
|
|
check dirExists(root.npksPath)
|
|
check dirExists(root.nipsPath)
|
|
check dirExists(root.nextersPath)
|
|
check fileExists(root.auditLogPath)
|
|
|
|
test "CAS Store Initialization":
|
|
## Verify CAS store can be initialized
|
|
|
|
let root = initStorageRoot()
|
|
discard createStorageStructure(root)
|
|
|
|
let store = initCASStore(root.casPath)
|
|
check store.rootPath == root.casPath
|
|
check store.chunksPath == root.casPath / "chunks"
|
|
check store.refsPath == root.casPath / "refs"
|
|
check store.auditLog == root.auditLogPath
|
|
|
|
test "Audit Log Writing":
|
|
## Verify audit log entries can be written
|
|
|
|
let root = initStorageRoot()
|
|
discard createStorageStructure(root)
|
|
|
|
let store = initCASStore(root.casPath)
|
|
store.logAuditEntry("TEST", "Test audit entry")
|
|
|
|
# Verify log file was updated
|
|
check fileExists(store.auditLog)
|
|
let logContent = readFile(store.auditLog)
|
|
check "TEST" in logContent
|
|
check "Test audit entry" in logContent
|
|
|
|
when isMainModule:
|
|
# Run tests with random seed for reproducibility
|
|
randomize(12345)
|
|
echo "Running Unified Storage Property Tests..."
|
|
echo "Random seed: 12345"
|
|
echo ""
|