diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 43cb56091..341a433fc 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -143,6 +143,9 @@ jobs: - name: Run bats (brew-preinstall) run: bats tests/test_brew_preinstall.bats + - name: Run bats (damask service and flatpak overrides) + run: bats tests/test_damask_service.bats + - name: Test Brewfile validator run: | shellcheck scripts/validate-brewfiles.sh tests/test_validate_brewfiles.bats diff --git a/Justfile b/Justfile index e030e6703..6a59b5155 100644 --- a/Justfile +++ b/Justfile @@ -43,6 +43,7 @@ test: bats tests/test_brew_tap_trust.bats bats tests/test_apps_just.bats bats tests/test_image_repo.bats + bats tests/test_damask_service.bats bats tests/test_suite_registration.bats bats tests/test_shared_just.bats bats tests/test_bluefin_countme.bats diff --git a/docs/TESTING.md b/docs/TESTING.md index 79963315a..4421c01b9 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -133,6 +133,7 @@ Do not add exemptions for scripts with branching logic. | `tests/test_image_repo.bats` | `usr/libexec/ublue-image-repo` — image-name/tag routing to upstream GitHub repos | | `tests/test_shared_just.bats` | `shared.just` — `powerwash` (double confirmation) and `toggle-tpm2` recipes | | `tests/test_escl_fixture.bats` | `tests/fixtures/escl-scanner/run-fixture.sh` — eSCL readiness probing, device detection, capture determinism/geometry/size assertions, DNS-SD discovery, with the simulator and client stubbed | +| `tests/test_damask_service.bats` | `damask.service`, Flatpak sandbox overrides, and tmpfiles symlink configuration | ## Quality Epic diff --git a/system_files/bluefin/usr/lib/systemd/user/damask.service b/system_files/bluefin/usr/lib/systemd/user/damask.service new file mode 100644 index 000000000..4bdd2a83a --- /dev/null +++ b/system_files/bluefin/usr/lib/systemd/user/damask.service @@ -0,0 +1,15 @@ +[Unit] +Description=Damask Wallpaper Slideshow Background Service +After=graphical-session.target +ConditionUser=!@system +ConditionPathExists=|%h/.local/share/flatpak/app/app.drey.Damask +ConditionPathExists=|/var/lib/flatpak/app/app.drey.Damask + +[Service] +Type=simple +ExecStart=/usr/bin/flatpak run app.drey.Damask --background +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=graphical-session.target diff --git a/system_files/bluefin/usr/lib/tmpfiles.d/damask-flatpak.conf b/system_files/bluefin/usr/lib/tmpfiles.d/damask-flatpak.conf new file mode 100644 index 000000000..63cd77cd5 --- /dev/null +++ b/system_files/bluefin/usr/lib/tmpfiles.d/damask-flatpak.conf @@ -0,0 +1 @@ +L /var/lib/flatpak/overrides/app.drey.Damask - - - - /usr/share/ublue-os/flatpak-overrides/app.drey.Damask diff --git a/system_files/bluefin/usr/share/ublue-os/flatpak-overrides/app.drey.Damask b/system_files/bluefin/usr/share/ublue-os/flatpak-overrides/app.drey.Damask new file mode 100644 index 000000000..763404bf5 --- /dev/null +++ b/system_files/bluefin/usr/share/ublue-os/flatpak-overrides/app.drey.Damask @@ -0,0 +1,2 @@ +[Context] +filesystems=host-os:ro;xdg-data/backgrounds:ro; diff --git a/tests/test_damask_service.bats b/tests/test_damask_service.bats new file mode 100644 index 000000000..115d0af38 --- /dev/null +++ b/tests/test_damask_service.bats @@ -0,0 +1,102 @@ +#!/usr/bin/env bats +# Tests for Damask Flatpak overrides, tmpfiles configuration, and systemd user service. +# +# Covers: +# - Flatpak sandbox override permissions and configuration +# - tmpfiles.d symlink rule syntax and pattern parity with Bazaar +# - Systemd user service unit definition, condition flags, and ordering +# - Systemd unit validation via systemd-analyze verify + +setup() { + REPO_ROOT="$BATS_TEST_DIRNAME/.." + OVERRIDE_FILE="$REPO_ROOT/system_files/bluefin/usr/share/ublue-os/flatpak-overrides/app.drey.Damask" + TMPFILES_FILE="$REPO_ROOT/system_files/bluefin/usr/lib/tmpfiles.d/damask-flatpak.conf" + SERVICE_FILE="$REPO_ROOT/system_files/bluefin/usr/lib/systemd/user/damask.service" + BAZAAR_TMPFILES="$REPO_ROOT/system_files/bluefin/usr/lib/tmpfiles.d/bazaar-flatpak.conf" + WORKDIR="$(mktemp -d)" +} + +teardown() { + rm -rf "${WORKDIR}" +} + +@test "damask flatpak override: file exists and specifies backgrounds filesystems" { + [ -f "${OVERRIDE_FILE}" ] + grep -Fxq "[Context]" "${OVERRIDE_FILE}" + grep -Fxq "filesystems=host-os:ro;xdg-data/backgrounds:ro;" "${OVERRIDE_FILE}" +} + +@test "damask tmpfiles: configuration exists and defines valid symlink override" { + [ -f "${TMPFILES_FILE}" ] + local expected="L /var/lib/flatpak/overrides/app.drey.Damask - - - - /usr/share/ublue-os/flatpak-overrides/app.drey.Damask" + local actual + actual="$(grep -v '^[[:space:]]*#' "${TMPFILES_FILE}" | grep -v '^[[:space:]]*$' | tr -s ' ')" + [ "${actual}" = "${expected}" ] + + # Verify parity with Bazaar's tmpfiles pattern + [ -f "${BAZAAR_TMPFILES}" ] + local bazaar_type + bazaar_type="$(awk '{print $1}' "${BAZAAR_TMPFILES}")" + local damask_type + damask_type="$(awk '{print $1}' "${TMPFILES_FILE}")" + [ "${damask_type}" = "${bazaar_type}" ] + [ "${damask_type}" = "L" ] + + # Validate tmpfiles format: 7 columns (Type Path Mode UID GID Age Argument) + local col_count + col_count="$(awk 'NF > 0 {print NF}' "${TMPFILES_FILE}")" + [ "${col_count}" -eq 7 ] +} + +@test "damask service: unit exists and specifies correct condition flags and lifecycle" { + [ -f "${SERVICE_FILE}" ] + grep -Fxq "Description=Damask Wallpaper Slideshow Background Service" "${SERVICE_FILE}" + grep -Fxq "After=graphical-session.target" "${SERVICE_FILE}" + grep -Fxq "ConditionUser=!@system" "${SERVICE_FILE}" + grep -Fxq "ConditionPathExists=|%h/.local/share/flatpak/app/app.drey.Damask" "${SERVICE_FILE}" + grep -Fxq "ConditionPathExists=|/var/lib/flatpak/app/app.drey.Damask" "${SERVICE_FILE}" + grep -Fxq "Type=simple" "${SERVICE_FILE}" + grep -Fxq "ExecStart=/usr/bin/flatpak run app.drey.Damask --background" "${SERVICE_FILE}" + grep -Fxq "Restart=on-failure" "${SERVICE_FILE}" + grep -Fxq "RestartSec=10" "${SERVICE_FILE}" + grep -Fxq "WantedBy=graphical-session.target" "${SERVICE_FILE}" +} + +@test "damask service: ConditionPathExists uses disjunction for user and system flatpak paths" { + local conditions + conditions="$(grep '^ConditionPathExists=' "${SERVICE_FILE}")" + local count + count="$(printf '%s\n' "${conditions}" | grep -c '^ConditionPathExists=|')" + [ "${count}" -eq 2 ] + + printf '%s\n' "${conditions}" | grep -q '|%h/\.local/share/flatpak/app/app\.drey\.Damask' + printf '%s\n' "${conditions}" | grep -q '|/var/lib/flatpak/app/app\.drey\.Damask' +} + +@test "damask service: systemd-analyze verify validates unit structure and ordering" { + if ! command -v systemd-analyze >/dev/null 2>&1; then + skip "systemd-analyze not available in environment" + fi + + local unit_dir="${WORKDIR}/systemd-user" + mkdir -p "${unit_dir}" + + sed 's|ExecStart=/usr/bin/flatpak run app.drey.Damask --background|ExecStart=/bin/true|' \ + "${SERVICE_FILE}" > "${unit_dir}/damask.service" + + cat > "${unit_dir}/graphical-session.target" <<'EOF' +[Unit] +Description=Test graphical session +StopWhenUnneeded=yes +EOF + + run env \ + SYSTEMD_LOG_LEVEL=debug \ + SYSTEMD_UNIT_PATH="${unit_dir}:/usr/lib/systemd/system" \ + systemd-analyze verify \ + graphical-session.target \ + damask.service + [ "${status}" -eq 0 ] + [[ "${output}" == *"After: graphical-session.target"* ]] + [[ "${output}" != *"ordering cycle"* ]] +}