Add Nix package, NixOS module, and tests for bambuddy
- packages.bambuddy: FastAPI backend + prebuilt React frontend assembled into the app-root layout upstream expects, behind a single `bambuddy` launcher (uvicorn --loop asyncio, ffmpeg on PATH, XDG state defaults). Pins upstream maziggy/bambuddy v1.2.5.7. - nixosModules.default: services.bambuddy with host/port/dataDir, openFirewall, environment + environmentFile, extraPackages, DynamicUser + StateDirectory, systemd hardening, and CAP_NET_BIND_SERVICE for the virtual-printer FTPS/RTSP ports. - checks.bambuddy-import: backend import + admin CLI smoke test. - checks.bambuddy-service: two-node NixOS VM test covering the health endpoint, UI/static assets/manifest, SQLite + logs in the StateDirectory, journal startup check, cross-node reachability with openFirewall, custom host/port/env node, and loopback-only binding. - overlay, run app, devShell, README, .gitignore.
This commit is contained in:
commit
5fb8e8d724
9 changed files with 761 additions and 0 deletions
27
tests/import-check.nix
Normal file
27
tests/import-check.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
lib,
|
||||
runCommand,
|
||||
bambuddy,
|
||||
}:
|
||||
# Fast package-level smoke test: import the full backend (which pulls in
|
||||
# every router, service and optional dependency) inside a sandbox with a
|
||||
# writable state dir, and exercise the admin CLI entrypoint. Catches broken
|
||||
# Python dependency closures long before the (much slower) VM test runs.
|
||||
runCommand "bambuddy-import-check"
|
||||
{
|
||||
nativeBuildInputs = [ bambuddy.passthru.pythonEnv ];
|
||||
meta = {
|
||||
description = "Bambuddy: verify the backend imports with the packaged dependency closure";
|
||||
};
|
||||
}
|
||||
''
|
||||
export DATA_DIR="$PWD/state" LOG_DIR="$PWD/state/logs" HOME="$PWD/state" MPLCONFIGDIR="$PWD/state/mpl"
|
||||
mkdir -p "$DATA_DIR" "$LOG_DIR"
|
||||
export PYTHONPATH="${bambuddy}/share/bambuddy"
|
||||
|
||||
python -c 'import backend.app.main; print("backend.app.main imports cleanly")'
|
||||
|
||||
python -m backend.app.cli --help > /dev/null
|
||||
|
||||
touch $out
|
||||
''
|
||||
92
tests/service-test.nix
Normal file
92
tests/service-test.nix
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
bambuddy,
|
||||
nixosModule,
|
||||
}:
|
||||
# Integration test: boots two NixOS VMs with the bambuddy NixOS module.
|
||||
#
|
||||
# * `server` uses the default configuration (port 8000, firewall open) and
|
||||
# checks that the service starts, the API responds, the prebuilt React
|
||||
# frontend is served with its static assets, and persistent state
|
||||
# (SQLite database, logs) lands in the StateDirectory.
|
||||
# * `custom` exercises the module options (custom host/port, extra
|
||||
# environment) and verifies the dashboard is reachable cross-node once
|
||||
# the firewall is opened.
|
||||
pkgs.testers.runNixOSTest (
|
||||
{ ... }:
|
||||
{
|
||||
name = "bambuddy-service";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [ nixosModule ];
|
||||
services.bambuddy = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
};
|
||||
environment.systemPackages = [ pkgs.curl ];
|
||||
};
|
||||
|
||||
custom =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [ nixosModule ];
|
||||
services.bambuddy = {
|
||||
enable = true;
|
||||
host = "127.0.0.1";
|
||||
port = 8123;
|
||||
environment.LOG_LEVEL = "DEBUG";
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
curl
|
||||
iproute2
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
server.start()
|
||||
custom.start()
|
||||
|
||||
server.wait_for_unit("bambuddy.service")
|
||||
server.wait_for_open_port(8000)
|
||||
|
||||
with subtest("health endpoint responds"):
|
||||
server.succeed("curl -fsS http://127.0.0.1:8000/health | grep -q healthy")
|
||||
|
||||
with subtest("uvicorn started cleanly"):
|
||||
server.succeed(
|
||||
"journalctl -u bambuddy.service --no-pager | grep -q 'Application startup complete'"
|
||||
)
|
||||
|
||||
with subtest("web UI is served from the packaged static build"):
|
||||
server.succeed("curl -fsS http://127.0.0.1:8000/ | grep -q '<title>Bambuddy</title>'")
|
||||
server.succeed("curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:8000/img/favicon-32x32.png | grep -q '^200$'")
|
||||
server.succeed("curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:8000/fonts/inter-latin.woff2 | grep -q '^200$'")
|
||||
server.succeed("curl -fsS http://127.0.0.1:8000/manifest.json | grep -qi bambuddy")
|
||||
|
||||
with subtest("persistent state lands in the StateDirectory"):
|
||||
server.succeed("test -f /var/lib/bambuddy/bambuddy.db")
|
||||
server.succeed("test -d /var/lib/bambuddy/logs")
|
||||
|
||||
with subtest("dashboard is reachable from another machine (openFirewall)"):
|
||||
custom.succeed("curl -m 10 -fsS http://server:8000/health | grep -q healthy")
|
||||
|
||||
with subtest("custom host/port options apply"):
|
||||
custom.wait_for_unit("bambuddy.service")
|
||||
custom.wait_for_open_port(8123)
|
||||
custom.succeed("curl -fsS http://127.0.0.1:8123/health | grep -q healthy")
|
||||
# host = 127.0.0.1 must not appear on any external address
|
||||
custom.fail("ss -tln | grep -E '0\\.0\\.0\\.0:8123|\\[::\\]:8123|\\*:8123'")
|
||||
server.fail("curl -m 5 -fsS http://custom:8123/health")
|
||||
|
||||
with subtest("extra environment reaches the service"):
|
||||
custom.succeed(
|
||||
"systemctl show bambuddy.service -p Environment | grep -q LOG_LEVEL=DEBUG"
|
||||
)
|
||||
'';
|
||||
}
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue