# tests/test_install_manager.nim # Unit tests for install_manager module import unittest, os, times, json, tables, strutils, sequtils import ../src/nimpak/install_manager import ../src/nimpak/cas suite "InstallManager Tests": var testRoot: string var manager: InstallManager var config: InstallConfig setup: # Create temporary test directory testRoot = getTempDir() / "nip_test_" & $epochTime().int createDir(testRoot) # Configure test environment config = InstallConfig( programsDir: testRoot / "Programs", linksDir: testRoot / "System" / "Links", cacheDir: testRoot / "cache", dbFile: testRoot / "db" / "packages.json", autoSymlink: true, checkConflicts: true, verbose: false ) # Create manager manager = newInstallManager(config) teardown: # Clean up test directory if dirExists(testRoot): removeDir(testRoot) test "Create install manager with default config": let defaultMgr = newInstallManager(defaultConfig()) check defaultMgr != nil check defaultMgr.config.programsDir == "/Programs" check defaultMgr.config.linksDir == "/System/Links" check defaultMgr.config.autoSymlink == true check defaultMgr.config.checkConflicts == true test "Install package to temporary directory": # Create a mock package source directory let sourcePath = testRoot / "source" / "testpkg" createDir(sourcePath / "bin") createDir(sourcePath / "lib") # Create test files writeFile(sourcePath / "bin" / "testcmd", "#!/bin/sh\necho test") writeFile(sourcePath / "lib" / "libtest.so.1", "mock library") # Install the package let metadata = %*{"description": "Test package"} let result = manager.installPackage( packageName = "testpkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-abc123", metadata = metadata ) # Verify installation succeeded check result.success == true check result.packageName == "testpkg" check result.version == "1.0.0" check result.errors.len == 0 # Verify package directory was created check dirExists(result.installPath) check fileExists(result.installPath / "bin" / "testcmd") check fileExists(result.installPath / "lib" / "libtest.so.1") # Verify package is in database check manager.isInstalled("testpkg") let pkg = manager.getInstalledPackage("testpkg") check pkg.name == "testpkg" check pkg.version == "1.0.0" check pkg.source == "test" test "Install package with variant descriptor": let sourcePath = testRoot / "source" / "varpkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "varcmd", "#!/bin/sh\necho variant") # Install with custom variant let result = manager.installPackage( packageName = "varpkg", version = "2.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-def456", metadata = newJObject(), variantDescriptor = "optimized" ) check result.success == true check "optimized" in result.installPath test "Symlink creation for executables": let sourcePath = testRoot / "source" / "binpkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "cmd1", "#!/bin/sh\necho cmd1") writeFile(sourcePath / "bin" / "cmd2", "#!/bin/sh\necho cmd2") let result = manager.installPackage( packageName = "binpkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-bin123" ) check result.success == true check result.symlinksCreated.len == 2 # Verify symlinks were created let execDir = config.linksDir / "Executables" check symlinkExists(execDir / "cmd1") check symlinkExists(execDir / "cmd2") test "Symlink creation for libraries": let sourcePath = testRoot / "source" / "libpkg" createDir(sourcePath / "lib") writeFile(sourcePath / "lib" / "libfoo.so.1", "library") writeFile(sourcePath / "lib" / "libbar.so.2.0", "library") let result = manager.installPackage( packageName = "libpkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-lib123" ) check result.success == true check result.symlinksCreated.len == 2 # Verify library symlinks let libDir = config.linksDir / "Libraries" check symlinkExists(libDir / "libfoo.so.1") check symlinkExists(libDir / "libbar.so.2.0") test "Package removal and cleanup": # First install a package let sourcePath = testRoot / "source" / "rmpkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "rmcmd", "#!/bin/sh\necho remove") let installResult = manager.installPackage( packageName = "rmpkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-rm123" ) check installResult.success == true check manager.isInstalled("rmpkg") # Now remove it let removeResult = manager.removePackage("rmpkg") check removeResult.success == true check removeResult.packageName == "rmpkg" check removeResult.errors.len == 0 # Verify package was removed check not manager.isInstalled("rmpkg") check not dirExists(installResult.installPath) # Verify symlinks were removed let execDir = config.linksDir / "Executables" check not symlinkExists(execDir / "rmcmd") test "Remove non-existent package": let result = manager.removePackage("nonexistent") check result.success == false check result.errors.len > 0 check "not installed" in result.errors[0] test "Conflict detection - executable": # Install first package let source1 = testRoot / "source" / "pkg1" createDir(source1 / "bin") writeFile(source1 / "bin" / "conflict", "#!/bin/sh\necho pkg1") let result1 = manager.installPackage( packageName = "pkg1", version = "1.0.0", source = "test", sourcePath = source1, graftHash = "blake2b-pkg1" ) check result1.success == true # Try to install second package with same executable let source2 = testRoot / "source" / "pkg2" createDir(source2 / "bin") writeFile(source2 / "bin" / "conflict", "#!/bin/sh\necho pkg2") let result2 = manager.installPackage( packageName = "pkg2", version = "1.0.0", source = "test", sourcePath = source2, graftHash = "blake2b-pkg2" ) # Installation should succeed but with warnings check result2.success == true check result2.warnings.len > 0 check "conflict" in result2.warnings[0].toLowerAscii() test "Conflict detection - library": # Install first package with library let source1 = testRoot / "source" / "libpkg1" createDir(source1 / "lib") writeFile(source1 / "lib" / "libconflict.so.1", "lib1") let result1 = manager.installPackage( packageName = "libpkg1", version = "1.0.0", source = "test", sourcePath = source1, graftHash = "blake2b-libpkg1" ) check result1.success == true # Try to install second package with same library let source2 = testRoot / "source" / "libpkg2" createDir(source2 / "lib") writeFile(source2 / "lib" / "libconflict.so.1", "lib2") let result2 = manager.installPackage( packageName = "libpkg2", version = "1.0.0", source = "test", sourcePath = source2, graftHash = "blake2b-libpkg2" ) check result2.success == true check result2.warnings.len > 0 test "List installed packages": # Install multiple packages for i in 1..3: let sourcePath = testRoot / "source" / "pkg" & $i createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "cmd" & $i, "test") let result = manager.installPackage( packageName = "pkg" & $i, version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-pkg" & $i ) check result.success == true # List packages let installed = manager.listInstalled() check installed.len == 3 # Verify package names let names = installed.mapIt(it.name) check "pkg1" in names check "pkg2" in names check "pkg3" in names test "Get system status": # Install a package let sourcePath = testRoot / "source" / "statuspkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "statuscmd", "test") discard manager.installPackage( packageName = "statuspkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-status" ) # Get status let status = manager.getStatus() check status.hasKey("total_packages") check status["total_packages"].getInt() == 1 check status.hasKey("by_source") check status.hasKey("total_size_mb") check status.hasKey("programs_dir") check status.hasKey("links_dir") test "Database persistence": # Install a package let sourcePath = testRoot / "source" / "dbpkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "dbcmd", "test") let result = manager.installPackage( packageName = "dbpkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-db123" ) check result.success == true # Create new manager to test database loading let manager2 = newInstallManager(config) # Verify package was loaded from database check manager2.isInstalled("dbpkg") let pkg = manager2.getInstalledPackage("dbpkg") check pkg.name == "dbpkg" check pkg.version == "1.0.0" test "Database backup creation": # Install first package let source1 = testRoot / "source" / "backup1" createDir(source1 / "bin") writeFile(source1 / "bin" / "cmd1", "test") discard manager.installPackage( packageName = "backup1", version = "1.0.0", source = "test", sourcePath = source1, graftHash = "blake2b-backup1" ) # Install second package (should create backup) let source2 = testRoot / "source" / "backup2" createDir(source2 / "bin") writeFile(source2 / "bin" / "cmd2", "test") discard manager.installPackage( packageName = "backup2", version = "1.0.0", source = "test", sourcePath = source2, graftHash = "blake2b-backup2" ) # Check for backup files let dbDir = parentDir(config.dbFile) var backupFound = false for file in walkDir(dbDir): if ".backup." in extractFilename(file.path): backupFound = true break check backupFound == true test "Install with missing source directory": let result = manager.installPackage( packageName = "missing", version = "1.0.0", source = "test", sourcePath = testRoot / "nonexistent", graftHash = "blake2b-missing" ) check result.success == false check result.errors.len > 0 check "does not exist" in result.errors[0] test "Install duplicate package": # Install package first time let sourcePath = testRoot / "source" / "duppkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "dupcmd", "test") let result1 = manager.installPackage( packageName = "duppkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-dup123" ) check result1.success == true # Try to install again let result2 = manager.installPackage( packageName = "duppkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-dup123" ) check result2.success == false check result2.errors.len > 0 check "already installed" in result2.errors[0] test "Symlink creation with auto-symlink disabled": # Create manager with auto-symlink disabled var noSymlinkConfig = config noSymlinkConfig.autoSymlink = false let noSymlinkMgr = newInstallManager(noSymlinkConfig) let sourcePath = testRoot / "source" / "nosympkg" createDir(sourcePath / "bin") writeFile(sourcePath / "bin" / "nosymcmd", "test") let result = noSymlinkMgr.installPackage( packageName = "nosympkg", version = "1.0.0", source = "test", sourcePath = sourcePath, graftHash = "blake2b-nosym" ) check result.success == true check result.symlinksCreated.len == 0 test "Current symlink creation and switching": # Install first variant let source1 = testRoot / "source" / "varpkg1" createDir(source1 / "bin") writeFile(source1 / "bin" / "varcmd", "variant1") let result1 = manager.installPackage( packageName = "varpkg", version = "1.0.0", source = "test", sourcePath = source1, graftHash = "blake2b-var1", variantDescriptor = "default" ) check result1.success == true # Verify Current symlink exists let versionDir = config.programsDir / "varpkg" / "1.0.0" let currentLink = versionDir / "Current" check symlinkExists(currentLink) # Install second variant (should switch Current) let source2 = testRoot / "source" / "varpkg2" createDir(source2 / "bin") writeFile(source2 / "bin" / "varcmd", "variant2") let result2 = manager.installPackage( packageName = "varpkg", version = "1.0.0", source = "test", sourcePath = source2, graftHash = "blake2b-var2", variantDescriptor = "optimized" ) check result2.success == true check symlinkExists(currentLink) # Current should now point to optimized variant let target = expandSymlink(currentLink) check "optimized" in target