nip/tests/test_npk_installer.nim

408 lines
12 KiB
Nim

## Tests for NPK Installation Workflow
##
## **Feature:** 01-nip-unified-storage-and-formats
## **Task:** 13. Implement NPK installation workflow
## **Requirements:** 3.5, 11.1, 11.2
##
## **Test Strategy:**
## - Test atomic installation workflow
## - Test chunk extraction to CAS with deduplication
## - Test manifest creation
## - Test reference tracking
## - Test rollback on failure
## - Test installation verification
import std/[unittest, os, times, json, options, strutils]
import nip/[npk, npk_manifest, npk_installer, manifest_parser, cas]
# ============================================================================
# Test Fixtures
# ============================================================================
proc createTestPackage(name: string, version: string, testDir: string): string =
## Create a test NPK package and return its path
let manifest = NPKManifest(
name: name,
version: parseSemanticVersion(version),
buildDate: now(),
metadata: PackageInfo(
description: "Test package for installation",
license: "MIT"
),
provenance: ProvenanceInfo(
source: "https://example.com/test.tar.gz",
sourceHash: "xxh3-test123",
buildTimestamp: now()
),
buildConfig: BuildConfiguration(
compilerVersion: "gcc-13.2.0",
targetArchitecture: "x86_64",
libc: "musl",
allocator: "default",
buildSystem: "cmake"
),
casChunks: @[
ChunkReference(
hash: "xxh3-chunk1",
size: 1024,
chunkType: Binary,
path: "bin/test"
),
ChunkReference(
hash: "xxh3-chunk2",
size: 2048,
chunkType: Library,
path: "lib/libtest.so"
)
],
install: InstallPaths(
programsPath: "/Programs/" & name & "/" & version,
binPath: "/Programs/" & name & "/" & version & "/bin",
libPath: "/Programs/" & name & "/" & version & "/lib",
sharePath: "/Programs/" & name & "/" & version & "/share",
etcPath: "/Programs/" & name & "/" & version & "/etc"
),
system: SystemIntegration(),
buildHash: "xxh3-build123",
signature: SignatureInfo(
algorithm: "ed25519",
keyId: "test-key",
signature: "test-signature"
)
)
let chunks = @[
ChunkData(
hash: "xxh3-chunk1",
data: "test chunk data 1",
size: 17,
chunkType: Binary
),
ChunkData(
hash: "xxh3-chunk2",
data: "test chunk data 2 - larger content",
size: 35,
chunkType: Library
)
]
let metadata = %* {
"package": name,
"version": version,
"created": "2025-11-20T14:00:00Z"
}
let signature = "test-signature-data"
let pkgPath = testDir / (name & ".npk")
discard createNPK(manifest, chunks, metadata, signature, pkgPath)
return pkgPath
# ============================================================================
# Installation Tests
# ============================================================================
suite "NPK Installation Workflow (Task 13)":
setup:
let testDir = getTempDir() / "npk-install-test-" & $getTime().toUnix()
let storageRoot = testDir / "storage"
createDir(testDir)
createDir(storageRoot)
teardown:
if dirExists(testDir):
removeDir(testDir)
test "Install NPK package successfully":
## **Requirement 3.5:** Extract chunks to CAS and create manifest
## **Requirement 11.1:** Atomic installation
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
let result = installNPK(pkgPath, storageRoot)
# Verify installation succeeded
check result.success
check result.packageName == "test-pkg"
check result.version == "1.0.0"
check result.chunksInstalled == 2
check result.error == ""
# Verify manifest was created
let manifestPath = storageRoot / "npks" / "test-pkg.kdl"
check fileExists(manifestPath)
# Verify references were created
let refsPath = storageRoot / "cas" / "refs" / "npks" / "test-pkg.refs"
check fileExists(refsPath)
# Verify chunks were extracted to CAS
let casDir = storageRoot / "cas" / "chunks"
check dirExists(casDir)
test "Install creates correct directory structure":
## Verify all required directories are created
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
# Check directory structure
check dirExists(storageRoot / "npks")
check dirExists(storageRoot / "cas")
check dirExists(storageRoot / "cas" / "chunks")
check dirExists(storageRoot / "cas" / "refs")
check dirExists(storageRoot / "cas" / "refs" / "npks")
test "Install fails for already installed package":
## Verify duplicate installation is prevented
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
# First installation should succeed
let result1 = installNPK(pkgPath, storageRoot)
check result1.success
# Second installation should fail
let result2 = installNPK(pkgPath, storageRoot)
check not result2.success
check "already installed" in result2.error.toLowerAscii()
test "Install creates valid manifest file":
## Verify manifest can be parsed after installation
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
let manifestPath = storageRoot / "npks" / "test-pkg.kdl"
let manifestKdl = readFile(manifestPath)
# Parse manifest to verify it's valid
let manifest = parseNPKManifest(manifestKdl)
check manifest.name == "test-pkg"
check manifest.version.major == 1
check manifest.version.minor == 0
check manifest.version.patch == 0
test "Install creates valid references file":
## Verify references file contains correct information
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
let refsPath = storageRoot / "cas" / "refs" / "npks" / "test-pkg.refs"
let refsContent = readFile(refsPath)
check "test-pkg" in refsContent
check "1.0.0" in refsContent
check "chunks:" in refsContent
# ============================================================================
# Deduplication Tests
# ============================================================================
suite "NPK Installation Deduplication (Task 13)":
setup:
let testDir = getTempDir() / "npk-dedup-test-" & $getTime().toUnix()
let storageRoot = testDir / "storage"
createDir(testDir)
createDir(storageRoot)
teardown:
if dirExists(testDir):
removeDir(testDir)
test "Multiple packages share chunks via CAS":
## Verify CAS deduplication works across packages
# Create two packages with overlapping chunks
let pkg1Path = createTestPackage("pkg1", "1.0.0", testDir)
let pkg2Path = createTestPackage("pkg2", "1.0.0", testDir)
# Install both packages
let result1 = installNPK(pkg1Path, storageRoot)
let result2 = installNPK(pkg2Path, storageRoot)
check result1.success
check result2.success
# Both packages should be installed
check isInstalled("pkg1", storageRoot)
check isInstalled("pkg2", storageRoot)
# ============================================================================
# Query Function Tests
# ============================================================================
suite "NPK Installation Queries (Task 13)":
setup:
let testDir = getTempDir() / "npk-query-test-" & $getTime().toUnix()
let storageRoot = testDir / "storage"
createDir(testDir)
createDir(storageRoot)
teardown:
if dirExists(testDir):
removeDir(testDir)
test "Check if package is installed":
## Test isInstalled() function
check not isInstalled("test-pkg", storageRoot)
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
check isInstalled("test-pkg", storageRoot)
test "Get installed package version":
## Test getInstalledVersion() function
let version1 = getInstalledVersion("test-pkg", storageRoot)
check version1.isNone
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
let version2 = getInstalledVersion("test-pkg", storageRoot)
check version2.isSome
check version2.get() == "1.0.0"
test "List installed packages":
## Test listInstalledPackages() function
let list1 = listInstalledPackages(storageRoot)
check list1.len == 0
let pkg1Path = createTestPackage("pkg1", "1.0.0", testDir)
let pkg2Path = createTestPackage("pkg2", "2.0.0", testDir)
discard installNPK(pkg1Path, storageRoot)
discard installNPK(pkg2Path, storageRoot)
let list2 = listInstalledPackages(storageRoot)
check list2.len == 2
check "pkg1" in list2
check "pkg2" in list2
# ============================================================================
# Verification Tests
# ============================================================================
suite "NPK Installation Verification (Task 13)":
setup:
let testDir = getTempDir() / "npk-verify-test-" & $getTime().toUnix()
let storageRoot = testDir / "storage"
createDir(testDir)
createDir(storageRoot)
teardown:
if dirExists(testDir):
removeDir(testDir)
test "Verify valid installation":
## Test verifyInstallation() function
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
let isValid = verifyInstallation("test-pkg", storageRoot)
check isValid
test "Verify fails for non-existent package":
## Verify detection of missing package
let isValid = verifyInstallation("nonexistent", storageRoot)
check not isValid
test "Verify fails for corrupted installation":
## Verify detection of corrupted installation
let pkgPath = createTestPackage("test-pkg", "1.0.0", testDir)
discard installNPK(pkgPath, storageRoot)
# Remove the references file to simulate corruption
let refsPath = storageRoot / "cas" / "refs" / "npks" / "test-pkg.refs"
removeFile(refsPath)
# Verification should fail
let isValid = verifyInstallation("test-pkg", storageRoot)
check not isValid
# ============================================================================
# Rollback Tests
# ============================================================================
suite "NPK Installation Rollback (Task 13)":
setup:
let testDir = getTempDir() / "npk-rollback-test-" & $getTime().toUnix()
let storageRoot = testDir / "storage"
createDir(testDir)
createDir(storageRoot)
teardown:
if dirExists(testDir):
removeDir(testDir)
test "Rollback on installation failure":
## **Requirement 11.2:** Rollback to previous state on failure
# Create invalid package path
let invalidPath = testDir / "nonexistent.npk"
# Installation should fail
let result = installNPK(invalidPath, storageRoot)
check not result.success
# Verify no artifacts left behind
let manifestPath = storageRoot / "npks" / "test-pkg.kdl"
check not fileExists(manifestPath)
# ============================================================================
# Utility Function Tests
# ============================================================================
suite "NPK Installation Utilities (Task 13)":
test "Format install result - success":
let result = InstallResult(
success: true,
packageName: "test-pkg",
version: "1.0.0",
installPath: "/path/to/manifest.kdl",
chunksInstalled: 5,
error: ""
)
let str = $result
check str.contains("Successfully installed")
check str.contains("test-pkg")
check str.contains("1.0.0")
check str.contains("5")
test "Format install result - failure":
let result = InstallResult(
success: false,
packageName: "test-pkg",
version: "",
installPath: "",
chunksInstalled: 0,
error: "Package not found"
)
let str = $result
check str.contains("Failed")
check str.contains("test-pkg")
check str.contains("Package not found")