nip/tests/test_container_startup.nim

358 lines
9.8 KiB
Nim

## NEXTER Container Startup Tests
##
## Tests for container startup and lifecycle management.
## Verifies configuration validation, process setup, and execution.
import std/[unittest, os, tempfiles, options, strutils, times, tables]
import nip/container_startup
import nip/nexter_manifest
import nip/manifest_parser
# Helper to create a test manifest
proc createTestManifest(name: string, version: string): NEXTERManifest =
let buildDate = parse("2025-11-28T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
return NEXTERManifest(
name: name,
version: parseSemanticVersion(version),
buildDate: buildDate,
metadata: ContainerInfo(
description: "Test container",
license: "MIT"
),
provenance: ProvenanceInfo(
source: "https://example.com/source.tar.gz",
sourceHash: "xxh3-source-hash",
buildTimestamp: buildDate
),
buildConfig: BuildConfiguration(
configureFlags: @[],
compilerFlags: @[],
compilerVersion: "gcc-13",
targetArchitecture: "x86_64",
libc: "musl",
allocator: "jemalloc",
buildSystem: "custom"
),
base: BaseConfig(
baseImage: some("alpine"),
baseVersion: some("3.18")
),
environment: initTable[string, string](),
casChunks: @[],
namespace: ContainerNamespace(
isolationType: "full",
capabilities: @[],
mounts: @[],
devices: @[]
),
startup: StartupConfig(
command: @["/bin/sh"],
workingDir: "/",
user: none(string),
entrypoint: none(string)
),
buildHash: "xxh3-build-hash",
signature: SignatureInfo(
algorithm: "ed25519",
keyId: "test-key",
signature: "test-sig"
)
)
suite "Container Startup Configuration Tests":
test "Create startup config from manifest":
## Verify startup config can be created from manifest
let manifest = createTestManifest("startup-test", "1.0.0")
let config = createStartupConfig(manifest)
check config.command.len > 0
check config.workingDir == "/"
check config.user.isNone
test "Startup config with custom command":
## Verify custom command is preserved
let manifest = createTestManifest("cmd-test", "1.0.0")
var cmdManifest = manifest
cmdManifest.startup.command = @["/bin/bash", "-c", "echo hello"]
let config = createStartupConfig(cmdManifest)
check config.command.len == 3
check config.command[0] == "/bin/bash"
check config.command[1] == "-c"
check config.command[2] == "echo hello"
test "Startup config with working directory":
## Verify working directory is preserved
let manifest = createTestManifest("workdir-test", "1.0.0")
var workdirManifest = manifest
workdirManifest.startup.workingDir = "/app"
let config = createStartupConfig(workdirManifest)
check config.workingDir == "/app"
test "Startup config with user":
## Verify user is preserved
let manifest = createTestManifest("user-test", "1.0.0")
var userManifest = manifest
userManifest.startup.user = some("appuser")
let config = createStartupConfig(userManifest)
check config.user.isSome
check config.user.get() == "appuser"
test "Startup config with entrypoint":
## Verify entrypoint is preserved
let manifest = createTestManifest("entry-test", "1.0.0")
var entryManifest = manifest
entryManifest.startup.entrypoint = some("/app/entrypoint.sh")
let config = createStartupConfig(entryManifest)
check config.entrypoint.isSome
check config.entrypoint.get() == "/app/entrypoint.sh"
test "Startup config with environment":
## Verify environment variables are preserved
let manifest = createTestManifest("env-test", "1.0.0")
var envManifest = manifest
envManifest.environment["APP_ENV"] = "production"
envManifest.environment["DEBUG"] = "false"
let config = createStartupConfig(envManifest)
check config.environment["APP_ENV"] == "production"
check config.environment["DEBUG"] == "false"
suite "Container Startup Validation Tests":
test "Validate valid startup config":
## Verify valid config passes validation
let config = ContainerStartupConfig(
command: @["/bin/sh"],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
check validateStartupConfig(config)
test "Validate fails for empty command":
## Verify validation fails for empty command
let config = ContainerStartupConfig(
command: @[],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
check not validateStartupConfig(config)
test "Validate fails for empty user":
## Verify validation fails for empty user
let config = ContainerStartupConfig(
command: @["/bin/sh"],
workingDir: "/",
user: some(""),
entrypoint: none(string),
environment: initTable[string, string]()
)
check not validateStartupConfig(config)
test "Validate fails for empty entrypoint":
## Verify validation fails for empty entrypoint
let config = ContainerStartupConfig(
command: @["/bin/sh"],
workingDir: "/",
user: none(string),
entrypoint: some(""),
environment: initTable[string, string]()
)
check not validateStartupConfig(config)
suite "Container Process Tests":
test "Start container with valid config":
## Verify container can be started
let config = ContainerStartupConfig(
command: @["/bin/echo", "hello"],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
let process = startContainer(config)
# Process should be created (or fail gracefully)
check process.startTime <= now()
test "Start container with invalid config":
## Verify container startup fails gracefully for invalid config
let config = ContainerStartupConfig(
command: @[],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
let process = startContainer(config)
check process.status == Failed
check process.pid == -1
test "Get container logs":
## Verify logs can be retrieved
let process = ContainerProcess(
pid: 1234,
startTime: now(),
status: Running,
exitCode: none[int](),
output: "stdout output",
error: "stderr output"
)
let logs = getContainerLogs(process)
check "stdout output" in logs
check "stderr output" in logs
test "Get container status":
## Verify status can be retrieved
let process = ContainerProcess(
pid: 1234,
startTime: now(),
status: Running,
exitCode: none[int](),
output: "",
error: ""
)
let status = getContainerStatus(process)
check status == Running
test "Format startup config":
## Verify startup config formatting
let config = ContainerStartupConfig(
command: @["/bin/sh"],
workingDir: "/app",
user: some("appuser"),
entrypoint: some("/app/start.sh"),
environment: initTable[string, string]()
)
let formatted = $config
check "Container Startup Config" in formatted
check "/bin/sh" in formatted
check "/app" in formatted
check "appuser" in formatted
test "Format container process":
## Verify container process formatting
let process = ContainerProcess(
pid: 1234,
startTime: now(),
status: Running,
exitCode: none[int](),
output: "",
error: ""
)
let formatted = $process
check "Container Process" in formatted
check "1234" in formatted
check "Running" in formatted
suite "Container Startup Property Tests":
test "Property: Config preserves all manifest startup settings":
## Verify all startup settings are preserved
let manifest = createTestManifest("prop-startup", "1.0.0")
var testManifest = manifest
testManifest.startup.command = @["/bin/bash", "-c", "test"]
testManifest.startup.workingDir = "/test"
testManifest.startup.user = some("testuser")
testManifest.startup.entrypoint = some("/test/entry.sh")
let config = createStartupConfig(testManifest)
check config.command == @["/bin/bash", "-c", "test"]
check config.workingDir == "/test"
check config.user.get() == "testuser"
check config.entrypoint.get() == "/test/entry.sh"
test "Property: Process has valid start time":
## Verify process start time is set correctly
let beforeStart = now()
let config = ContainerStartupConfig(
command: @["/bin/echo"],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
let process = startContainer(config)
let afterStart = now()
check process.startTime >= beforeStart
check process.startTime <= afterStart
test "Property: Failed process has negative PID":
## Verify failed processes have negative PID
let config = ContainerStartupConfig(
command: @[],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
let process = startContainer(config)
if process.status == Failed:
check process.pid < 0
test "Property: Valid config always validates":
## Verify valid configs always pass validation
for i in 1..10:
let config = ContainerStartupConfig(
command: @["/bin/sh", "-c", "echo test-" & $i],
workingDir: "/",
user: none(string),
entrypoint: none(string),
environment: initTable[string, string]()
)
check validateStartupConfig(config)