nip/tests/test_nip_launcher.nim

323 lines
10 KiB
Nim

## NIP Launcher Tests
##
## Tests for the NIP launcher that runs applications in isolated namespaces.
## This verifies that applications can be launched with proper sandbox restrictions.
import std/[unittest, os, tempfiles, options, strutils, posix]
import nip/namespace
import nip/manifest_parser
import nip/nip_installer
import nip/cas
suite "NIP Launcher Tests":
setup:
let tempDir = createTempDir("nip_test_launcher_", "")
let casRoot = tempDir / "cas"
let installRoot = tempDir / "nips"
createDir(casRoot)
createDir(installRoot)
discard initCasManager(casRoot, casRoot)
teardown:
removeDir(tempDir)
test "Create Launcher from Manifest":
## Verify launcher can be created from a manifest
let manifest = PackageManifest(
name: "test-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher != nil
check launcher.manifest.name == "test-app"
check launcher.installDir == installRoot
check launcher.casRoot == casRoot
test "Launcher with Sandbox Configuration":
## Verify launcher respects sandbox configuration
var manifest = PackageManifest(
name: "sandboxed-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStrict,
namespaces: @["user", "mount", "pid", "ipc"]
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.level == SandboxStrict
check "user" in sb.namespaces
check "mount" in sb.namespaces
test "Launcher with Desktop Integration":
## Verify launcher works with desktop-integrated applications
var manifest = PackageManifest(
name: "desktop-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.desktop = some(DesktopIntegration(
displayName: "Desktop Application",
categories: @["Utility", "Development"],
icon: some("app-icon"),
terminal: false
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.desktop.isSome
let dt = launcher.manifest.desktop.get()
check dt.displayName == "Desktop Application"
check dt.icon.isSome
check dt.icon.get() == "app-icon"
test "Launcher with Seccomp Profile":
## Verify launcher respects seccomp configuration
var manifest = PackageManifest(
name: "seccomp-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStandard,
namespaces: @["user", "mount"],
seccompProfile: some("strict")
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.seccompProfile.isSome
check sb.seccompProfile.get() == "strict"
test "Launcher with Capabilities":
## Verify launcher respects capability restrictions
var manifest = PackageManifest(
name: "cap-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStandard,
namespaces: @["user", "mount"],
capabilities: @["CAP_NET_ADMIN", "CAP_SYS_ADMIN"]
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.capabilities.len == 2
check "CAP_NET_ADMIN" in sb.capabilities
check "CAP_SYS_ADMIN" in sb.capabilities
test "Launcher with Pledge (BSD)":
## Verify launcher respects pledge configuration (OpenBSD)
var manifest = PackageManifest(
name: "pledge-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStandard,
namespaces: @["user"],
pledge: some("stdio rpath wpath inet")
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.pledge.isSome
check sb.pledge.get() == "stdio rpath wpath inet"
test "Launcher Isolation Levels":
## Verify launcher supports different isolation levels
for level in [SandboxStrict, SandboxStandard]:
var manifest = PackageManifest(
name: "isolation-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: level,
namespaces: @["user", "mount"]
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
check launcher.manifest.sandbox.get().level == level
test "Launcher with CAS Root":
## Verify launcher correctly references CAS root for read-only mounts
let manifest = PackageManifest(
name: "cas-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.casRoot == casRoot
# The launcher should mount casRoot as read-only in the namespace
# This is verified by the namespace setup code
test "Launcher with Multiple Namespaces":
## Verify launcher can create multiple namespace types
var manifest = PackageManifest(
name: "multi-ns-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStrict,
namespaces: @["user", "mount", "pid", "net", "ipc"]
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.namespaces.len == 5
check "user" in sb.namespaces
check "mount" in sb.namespaces
check "pid" in sb.namespaces
check "net" in sb.namespaces
check "ipc" in sb.namespaces
test "Launcher Manifest Validation":
## Verify launcher validates manifest before launch
let manifest = PackageManifest(
name: "valid-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
let launcher = newLauncher(manifest, installRoot, casRoot)
# Launcher should have valid manifest
check launcher.manifest.name.len > 0
check launcher.manifest.version.major >= 0
check launcher.manifest.license.len > 0
test "Launcher with No Sandbox (Unrestricted)":
## Verify launcher can run without sandbox for trusted applications
let manifest = PackageManifest(
name: "unrestricted-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
# No sandbox configuration = unrestricted
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isNone
# Launcher should still work, just without isolation
test "Launcher with Minimal Sandbox":
## Verify launcher works with minimal sandbox configuration
var manifest = PackageManifest(
name: "minimal-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
manifest.sandbox = some(SandboxConfig(
level: SandboxStandard,
namespaces: @[]
))
let launcher = newLauncher(manifest, installRoot, casRoot)
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.level == SandboxStandard
check sb.namespaces.len == 0
## Property-Based Tests
suite "NIP Launcher Property Tests":
test "Property: Launcher preserves manifest integrity":
## Verify launcher doesn't modify manifest during creation
let manifest = PackageManifest(
name: "prop-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
let originalName = manifest.name
let originalVersion = manifest.version
let originalLicense = manifest.license
let launcher = newLauncher(manifest, "/tmp/install", "/tmp/cas")
check launcher.manifest.name == originalName
check launcher.manifest.version == originalVersion
check launcher.manifest.license == originalLicense
test "Property: Launcher paths are correctly set":
## Verify launcher stores paths correctly
let manifest = PackageManifest(
name: "path-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
let installDir = "/custom/install"
let casRoot = "/custom/cas"
let launcher = newLauncher(manifest, installDir, casRoot)
check launcher.installDir == installDir
check launcher.casRoot == casRoot
test "Property: Launcher supports all namespace combinations":
## Verify launcher can handle all namespace combinations
var manifest = PackageManifest(
name: "ns-app",
version: parseSemanticVersion("1.0.0"),
license: "MIT",
artifactHash: "hash123"
)
# Test various namespace combinations
let namespaceCombos = @[
@["user"],
@["user", "mount"],
@["user", "mount", "pid"],
@["user", "mount", "pid", "net"],
@["user", "mount", "pid", "net", "ipc"]
]
for namespaces in namespaceCombos:
manifest.sandbox = some(SandboxConfig(
level: SandboxStandard,
namespaces: namespaces
))
let launcher = newLauncher(manifest, "/tmp/install", "/tmp/cas")
check launcher.manifest.sandbox.isSome
let sb = launcher.manifest.sandbox.get()
check sb.namespaces == namespaces