import std/[unittest, os, tempfiles, options, strutils] import nip/nip_installer import nip/manifest_parser import nip/cas suite "NIP Installer Tests": setup: let tempDir = createTempDir("nip_test_installer_", "") let casRoot = tempDir / "cas" # Mock home directory structure let mockHome = tempDir / "home" createDir(mockHome / ".local/share/nexus/nips") createDir(mockHome / ".local/share/applications") createDir(casRoot) discard initCasManager(casRoot, casRoot) # We need to override getHomeDir or pass paths manually. # nip_installer uses getHomeDir(). We should probably allow overriding paths in constructor. # Wait, newNipInstaller takes casRoot. It calculates others from getHomeDir(). # I should modify newNipInstaller to accept overrides or use environment variable. # Or just subclass/mock? Nim doesn't do classes like that easily. # Let's modify nip_installer to allow path overrides for testing. # For now, I'll manually construct the object since fields are exported? # No, fields are exported (*). var ni = NipInstaller( casRoot: casRoot, installRoot: mockHome / ".local/share/nexus/nips", appsRoot: mockHome / ".local/share/applications", dryRun: false ) teardown: removeDir(tempDir) test "Install NIP with Desktop Integration": # 1. Prepare CAS let fileContent = "binary data" let casObj = storeObject(fileContent, ni.casRoot) # 2. Create Manifest var manifest = PackageManifest( name: "test-app", version: parseSemanticVersion("1.0.0"), license: "MIT", artifactHash: "hash123" ) manifest.files.add(FileSpec( path: "bin/app", hash: string(casObj.hash), size: fileContent.len, permissions: "755" )) manifest.desktop = some(DesktopIntegration( displayName: "Test App", categories: @["Utility"], terminal: true )) # 3. Install ni.installNip(manifest) # 4. Verify Files let installPath = ni.installRoot / "test-app/1.0.0/hash123" check fileExists(installPath / "bin/app") check readFile(installPath / "bin/app") == fileContent # 5. Verify Symlink let currentLink = ni.installRoot / "test-app/Current" check symlinkExists(currentLink) check expandSymlink(currentLink) == installPath # 6. Verify Desktop File let desktopFile = ni.appsRoot / "test-app.desktop" check fileExists(desktopFile) let content = readFile(desktopFile) check content.contains("Name=Test App") check content.contains("Exec=nip run test-app") check content.contains("Categories=Utility;") check content.contains("Terminal=true") # 7. Verify CAS Reference check hasReferences(ni.casRoot, casObj.hash) test "Remove NIP": # 1. Setup (Install first) let fileContent = "data" let casObj = storeObject(fileContent, ni.casRoot) var manifest = PackageManifest( name: "remove-me", version: parseSemanticVersion("1.0.0"), license: "MIT", artifactHash: "hash123" ) manifest.files.add(FileSpec(path: "file", hash: string(casObj.hash))) manifest.desktop = some(DesktopIntegration(displayName: "Remove Me")) ni.installNip(manifest) check fileExists(ni.appsRoot / "remove-me.desktop") # 2. Remove ni.removeNip(manifest) # 3. Verify check not fileExists(ni.appsRoot / "remove-me.desktop") check not dirExists(ni.installRoot / "remove-me") check not hasReferences(ni.casRoot, casObj.hash)