#!/bin/bash # kappa init-switching integration test via systemd-nspawn # Requires: systemd-nspawn, debootstrap (or pacstrap), kappa binary set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' KAPPA_BIN="${KAPPA_BIN:-./build/kappa}" ROOTFS="./test-rootfs" PASS=0 FAIL=0 cleanup() { echo "" echo "Cleaning up..." rm -rf "$ROOTFS" /tmp/kappa-test-*.kap 2>/dev/null || true } trap cleanup EXIT check() { local desc="$1" cmd="$2" expect="$3" echo -n " $desc ... " local out out=$(eval "$cmd" 2>&1) || true if echo "$out" | grep -q "$expect"; then echo -e "${GREEN}OK${NC}" PASS=$((PASS + 1)) else echo -e "${RED}FAIL${NC}" echo " expected: $expect" echo " got: $(echo "$out" | head -3 | tr '\n' ' ')" FAIL=$((FAIL + 1)) fi } echo "" echo "============================================" echo " kappa init-switching integration tests" echo "============================================" echo "" # ------------------------------------------------------------------ # 1. Create a minimal rootfs directory structure # ------------------------------------------------------------------ echo "--- Setting up test rootfs ---" mkdir -p "$ROOTFS"/{etc,usr/local/bin,kappa/{boot,db,store,temp},var/service} cp "$KAPPA_BIN" "$ROOTFS/usr/local/bin/kappa" chmod +x "$ROOTFS/usr/local/bin/kappa" export KAPPA_ROOT="$ROOTFS/kappa" # Create a minimal package registry (simulating installed packages) mkdir -p "$KAPPA_ROOT/db" # Simulate an installed system with nginx (has services), zlib (no services), # and postgresql (multi-service, uses ${enabledinit}) cat > /tmp/kappa-test-nginx.kap << 'EOF' package "nginx" { version = "1.24" source = "https://nginx.org/nginx-1.24.tar.gz" service { exec = "/usr/bin/nginx" type = "forking" ports = [80, 443] user = "www-data" description = "Nginx web server" } build { ./configure --prefix=${prefix}; make -j${jobs} } install { make DESTDIR=${destdir} install } } EOF cat > /tmp/kappa-test-zlib.kap << 'EOF' package "zlib" { version = "1.3" source = "https://zlib.net/zlib-1.3.tar.gz" build { ./configure --prefix=${prefix}; make -j${jobs} } install { make DESTDIR=${destdir} install } } EOF cat > /tmp/kappa-test-postgres.kap << 'EOF' package "postgresql" { version = "16.3" source = "https://ftp.postgresql.org/source/postgresql-16.3.tar.gz" service main { exec = "/usr/bin/postgres" type = "forking" ports = [5432] user = "postgres" description = "PostgreSQL database server" } service checkpointer { exec = "/usr/bin/postgres-checkpointer" type = "longrun" user = "postgres" } build { case ${enabledinit} in systemd) ./configure --with-systemd --prefix=${prefix} ;; *) ./configure --prefix=${prefix} ;; esac make -j${jobs} } install { make DESTDIR=${destdir} install } } EOF # Write installed DB (simulate nginx, zlib, postgresql already installed) # Format: name version hash [provides...] mkdir -p "$(dirname "$KAPPA_ROOT/db/installed")" cat > "$KAPPA_ROOT/db/installed" << 'EOF' nginx 1.24 a1b2c3d4e5f6a7b8 zlib 1.3 b2c3d4e5f6a7b8c9 postgresql 16.3 c3d4e5f6a7b8c9d0 init a1b2c3d4e5f6a7b8 kernel d4e5f6a7b8c9d0e1 bootloader e5f6a7b8c9d0e1f2 EOF echo "" # ------------------------------------------------------------------ # 2. Init system switching tests # ------------------------------------------------------------------ echo "--- Init system switching ---" # Base config template config_template() { local init="$1" cat << EOF system { hostname = "kappa-test" } packages { nginx {} zlib {} postgresql {} $init {} } services { nginx { enable = true } postgresql { enable = true } postgresql.checkpointer { enable = true } } boot { kernel = "linux" init = "$init" root = "/dev/sda1" bootloader = "limine" } groups { wheel { gid = 998 } } users { root { shell = "/bin/zsh" } } EOF } # Test systemd config config_template "systemd" > /tmp/kappa-test-systemd.kap check "parse systemd config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-systemd.kap" \ 'systemd — system and service manager' check "systemd bootloader in config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-systemd.kap" \ 'Limine' # Test openrc config config_template "openrc" > /tmp/kappa-test-openrc.kap check "parse openrc config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-openrc.kap" \ 'OpenRC — dependency-based init' # Test s6 config config_template "s6" > /tmp/kappa-test-s6.kap check "parse s6 config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-s6.kap" \ 's6 — s6 supervision suite' # Test runit config config_template "runit" > /tmp/kappa-test-runit.kap check "parse runit config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-runit.kap" \ 'runit — supervision suite' # Test dinit config config_template "dinit" > /tmp/kappa-test-dinit.kap check "parse dinit config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-dinit.kap" \ 'dinit — service manager' echo "" # ------------------------------------------------------------------ # 3. Service generation verification # ------------------------------------------------------------------ echo "--- Service file generation ---" # Verify service specs are parsed correctly per init for init in systemd openrc s6 runit dinit; do check "service block present for $init config" \ "$KAPPA_BIN format /tmp/kappa-test-$init.kap" \ 'enable = true' done # Verify the format output contains correct service fields check "nginx service has type forking" \ "$KAPPA_BIN format /tmp/kappa-test-nginx.kap" \ 'type = "forking"' check "postgresql has named service checkpointer" \ "$KAPPA_BIN format /tmp/kappa-test-postgres.kap" \ 'service checkpointer' check "postgresql build uses enabledinit" \ "$KAPPA_BIN format /tmp/kappa-test-postgres.kap" \ 'enabledinit' echo "" # ------------------------------------------------------------------ # 4. Rebuild impact analysis # ------------------------------------------------------------------ echo "--- Rebuild impact analysis ---" # Write an initial installed state matching s6 config # (reuse the same installed DB from above) config_template "s6" > /tmp/kappa-test-rebuild-s6.kap # The packages list includes "s6" which isn't in the installed DB, # so rebuild correctly shows it as a new package to build. check "rebuild detects new init package" \ "$KAPPA_BIN rebuild --dry-run /tmp/kappa-test-rebuild-s6.kap 2>&1" \ 'packages to rebuild' # Now test switching FROM s6 TO systemd config_template "systemd" > /tmp/kappa-test-rebuild-systemd.kap # The rebuild should detect init_changed # Note: this requires the installed DB to have the old init hash check "rebuild detects init change" \ "$KAPPA_BIN rebuild --dry-run /tmp/kappa-test-rebuild-systemd.kap 2>&1" \ 'packages to rebuild' echo "" # ------------------------------------------------------------------ # 5. Bootloader switching tests # ------------------------------------------------------------------ echo "--- Bootloader switching ---" for bl in grub limine; do cat > "/tmp/kappa-test-bl-$bl.kap" << KAPEOF system { hostname = "test" } packages { nginx {} } services {} boot { kernel = "linux" init = "s6" root = "/dev/sda1" bootloader = "$bl" } users { root { shell = "/bin/sh" } } KAPEOF check "bootloader $bl recognized in config" \ "$KAPPA_BIN parse-config /tmp/kappa-test-bl-$bl.kap" \ "$bl" done # Test unknown bootloader cat > /tmp/kappa-test-badbl.kap << 'KAPEOF' system { hostname = "test" } packages {} services {} boot { kernel = "linux" init = "s6" root = "/dev/sda1" bootloader = "lilo" } users { root { shell = "/bin/sh" } } KAPEOF check "doctor warns on unknown bootloader" \ "$KAPPA_BIN doctor /tmp/kappa-test-badbl.kap 2>&1" \ 'not a recognized bootloader' echo "" # ------------------------------------------------------------------ # 6. Groups and conflicts # ------------------------------------------------------------------ echo "--- Groups and conflicts ---" cat > /tmp/kappa-test-groups2.kap << 'KAPEOF' system { hostname = "test" } packages {} services {} boot { kernel = "linux" init = "s6" root = "/dev/sda1" bootloader = "limine" } groups { wheel { gid = 998 } audio {} docker { gid = 995 } } users { root { shell = "/bin/sh" } } KAPEOF check "groups with gid format correctly" \ "$KAPPA_BIN format /tmp/kappa-test-groups2.kap" \ 'gid = 998' check "groups without gid format as empty" \ "$KAPPA_BIN format /tmp/kappa-test-groups2.kap" \ 'audio {}' # Conflicts test cat > /tmp/kappa-test-conflict-pkg.kap << 'KAPEOF' package "systemd" { version = "255" source = "https://example.com/systemd.tar.gz" provides = ["udev", "logind"] conflicts = ["eudev", "elogind"] build { make } install { make install } } KAPEOF check "conflicts parse correctly" \ "$KAPPA_BIN parse-package /tmp/kappa-test-conflict-pkg.kap" \ 'valid' check "conflicts format round-trips" \ "$KAPPA_BIN format /tmp/kappa-test-conflict-pkg.kap" \ 'conflicts = \[' echo "" echo "============================================" echo -e " Results: ${GREEN}$PASS passed${NC}, ${RED}$FAIL failed${NC}" echo "============================================" [ "$FAIL" -eq 0 ] || exit 1