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")