178 lines
5.5 KiB
Nim
178 lines
5.5 KiB
Nim
import std/[unittest, os, tempfiles, options, strutils]
|
|
import nip/system_integration
|
|
import nip/manifest_parser
|
|
import nip/cas
|
|
|
|
suite "System Integration Tests":
|
|
|
|
setup:
|
|
let tempDir = createTempDir("nip_test_", "")
|
|
let casRoot = tempDir / "cas"
|
|
let programsRoot = tempDir / "Programs"
|
|
let systemIndexRoot = tempDir / "System/Index"
|
|
|
|
createDir(casRoot)
|
|
createDir(programsRoot)
|
|
createDir(systemIndexRoot)
|
|
|
|
# Initialize CAS
|
|
discard initCasManager(casRoot, casRoot)
|
|
|
|
let si = newSystemIntegrator(casRoot, programsRoot, systemIndexRoot)
|
|
|
|
teardown:
|
|
removeDir(tempDir)
|
|
|
|
test "Install package with file reconstruction":
|
|
# 1. Prepare CAS content
|
|
let fileContent = "echo 'Hello World'"
|
|
let casObj = storeObject(fileContent, casRoot)
|
|
let fileHash = string(casObj.hash)
|
|
|
|
# 2. Create Manifest
|
|
var manifest = PackageManifest(
|
|
name: "hello-world",
|
|
version: parseSemanticVersion("1.0.0"),
|
|
license: "MIT",
|
|
artifactHash: "hash123"
|
|
)
|
|
|
|
manifest.files.add(FileSpec(
|
|
path: "bin/hello",
|
|
hash: fileHash,
|
|
size: fileContent.len,
|
|
permissions: "755"
|
|
))
|
|
|
|
# 3. Install
|
|
si.installPackage(manifest)
|
|
|
|
# 4. Verify
|
|
let installPath = programsRoot / "hello-world/1.0.0/hash123"
|
|
let binPath = installPath / "bin/hello"
|
|
|
|
check fileExists(binPath)
|
|
check readFile(binPath) == fileContent
|
|
|
|
# Verify permissions (basic check)
|
|
let perms = getFilePermissions(binPath)
|
|
check fpUserExec in perms
|
|
|
|
test "Create symlinks":
|
|
# 1. Prepare installed state (simulate previous step)
|
|
let installPath = programsRoot / "hello-world/1.0.0/hash123"
|
|
createDir(installPath / "bin")
|
|
writeFile(installPath / "bin/hello", "binary")
|
|
|
|
var manifest = PackageManifest(
|
|
name: "hello-world",
|
|
version: parseSemanticVersion("1.0.0"),
|
|
license: "MIT",
|
|
artifactHash: "hash123"
|
|
)
|
|
|
|
# 2. Run symlink creation (via installPackage or directly)
|
|
# We'll run installPackage which calls createSymlinks
|
|
# But we need files in manifest to avoid error in reconstructFiles?
|
|
# No, reconstructFiles iterates manifest.files. If empty, it does nothing.
|
|
# But we want to test symlinks, so we need the file to exist in installPath.
|
|
# We already created it manually.
|
|
# But reconstructFiles might overwrite it or fail if hash not in CAS.
|
|
# So we should add file to manifest AND CAS.
|
|
|
|
let fileContent = "binary"
|
|
let casObj = storeObject(fileContent, casRoot)
|
|
manifest.files.add(FileSpec(
|
|
path: "bin/hello",
|
|
hash: string(casObj.hash),
|
|
size: fileContent.len,
|
|
permissions: "755"
|
|
))
|
|
|
|
si.installPackage(manifest)
|
|
|
|
# 3. Verify Symlinks
|
|
let currentLink = programsRoot / "hello-world/Current"
|
|
check symlinkExists(currentLink)
|
|
check expandSymlink(currentLink) == installPath
|
|
|
|
let binLink = systemIndexRoot / "bin/hello"
|
|
check symlinkExists(binLink)
|
|
# The link should point to .../Current/bin/hello
|
|
check expandSymlink(binLink).contains("Current")
|
|
|
|
test "Service creation":
|
|
var manifest = PackageManifest(
|
|
name: "myservice",
|
|
version: parseSemanticVersion("1.0.0"),
|
|
license: "MIT",
|
|
artifactHash: "hash123"
|
|
)
|
|
|
|
manifest.services.add(ServiceSpec(
|
|
name: "myservice",
|
|
content: "[Unit]\nDescription=My Service",
|
|
enabled: true
|
|
))
|
|
|
|
# We need to run as root for systemctl, so this part might fail or need mocking.
|
|
# But writeFile should work if we have permissions on tempDir.
|
|
# execCmd("systemctl") will fail.
|
|
# We should probably mock execCmd or check for root/systemd.
|
|
# For this test, we just check file creation.
|
|
# But `manageServices` calls `execCmd`.
|
|
# We can't easily mock `execCmd` in Nim without dependency injection or mixins.
|
|
# However, `execCmd` failure just logs error in our implementation?
|
|
# No, `discard execCmd` ignores result.
|
|
|
|
si.installPackage(manifest)
|
|
|
|
let serviceFile = systemIndexRoot / "lib/systemd/system/myservice.service"
|
|
check fileExists(serviceFile)
|
|
check readFile(serviceFile) == "[Unit]\nDescription=My Service"
|
|
|
|
test "Remove package":
|
|
# 1. Setup: Install a package
|
|
let fileContent = "binary"
|
|
let casObj = storeObject(fileContent, casRoot)
|
|
|
|
var manifest = PackageManifest(
|
|
name: "removable-pkg",
|
|
version: parseSemanticVersion("1.0.0"),
|
|
license: "MIT",
|
|
artifactHash: "hash123"
|
|
)
|
|
|
|
manifest.files.add(FileSpec(
|
|
path: "bin/removable",
|
|
hash: string(casObj.hash),
|
|
size: fileContent.len,
|
|
permissions: "755"
|
|
))
|
|
|
|
si.installPackage(manifest)
|
|
|
|
# Verify installation
|
|
let installPath = programsRoot / "removable-pkg/1.0.0/hash123"
|
|
check fileExists(installPath / "bin/removable")
|
|
check symlinkExists(programsRoot / "removable-pkg/Current")
|
|
check symlinkExists(systemIndexRoot / "bin/removable")
|
|
|
|
# Verify CAS reference
|
|
check hasReferences(casRoot, casObj.hash)
|
|
|
|
# 2. Remove package
|
|
si.removePackage(manifest)
|
|
|
|
# 3. Verify removal
|
|
check not fileExists(installPath / "bin/removable")
|
|
check not dirExists(installPath)
|
|
check not symlinkExists(programsRoot / "removable-pkg/Current")
|
|
check not symlinkExists(systemIndexRoot / "bin/removable")
|
|
|
|
# Verify CAS reference removal
|
|
check not hasReferences(casRoot, casObj.hash)
|
|
|
|
# Verify package dir removal (since it was empty)
|
|
check not dirExists(programsRoot / "removable-pkg")
|