import std/[unittest, os, posix, strutils] import nip/namespace import nip/manifest_parser # Import unshare for testing availability proc unshare(flags: cint): cint {.importc: "unshare", header: "".} const CLONE_NEWUSER = 0x10000000 # Note: Testing namespaces usually requires root or unprivileged user namespace support enabled. # Most CI/Docker environments might block this. # We will check if we can unshare first. proc canUnshare(): bool = # Try to unshare user namespace if unshare(CLONE_NEWUSER) == 0: return true return false suite "NIP Namespace Isolation Tests": test "Initialize Launcher": let manifest = PackageManifest(name: "test-app", version: parseSemanticVersion("1.0.0"), license: "MIT") let launcher = newLauncher(manifest, "/tmp/install", "/tmp/cas") check launcher != nil check launcher.manifest.name == "test-app" test "Sandbox Execution (Mock/Check)": # This test is tricky without actually running it. # We verify the logic compiles and basic object creation works. # Actual execution requires a binary to run. if not canUnshare(): echo "User namespaces not supported in this environment - SKIPPING" else: # If we can unshare, we can try a minimal test # But 'run' does execv which replaces the process. # We need to fork first in the test. let pid = fork() if pid == 0: # Child try: let manifest = PackageManifest(name: "true", version: parseSemanticVersion("1.0.0"), license: "MIT") # We point installDir to /bin so it finds 'true' (assuming logic looks in bin/) # Wait, the logic looks in installDir/bin/ # So we need to fake that. createDir("/tmp/nip_test_ns/bin") copyFile("/bin/true", "/tmp/nip_test_ns/bin/true") let launcher = newLauncher(manifest, "/tmp/nip_test_ns", "/tmp") # This will replace the process launcher.run(@[]) # Should not reach here quit(1) except Exception as e: if "Operation not permitted" in e.msg: echo "SKIPPING: Unshare not permitted in child (likely CI restriction)" quit(0) else: echo "Child exception: ", e.msg quit(1) else: # Parent var status: cint discard waitpid(pid, status, 0) check WIFEXITED(status) check WEXITSTATUS(status) == 0