fix(station): stat-Reihenfolge, Phase nur im Migrationsmodus, eingerueckte Konfig-Werte, kein /tmp-Zugriff im Test

Fixrunde 2 zu Task 4:

- N1 (wichtig): file_mode() in test_cli.sh/test_setup_pi.sh probierte
  "stat -f '%Lp'" vor "stat -c '%a'" - auf GNU-coreutils (dem Pi) deutet
  "-f" dort "Dateisystemstatus" und liefert Muell statt der Rechte-Zahl,
  faellt aber trotzdem in den "||"-Zweig durch, der zusaetzlich "644"
  anhaengt. Reihenfolge umgedreht und mit einem per gstat erzwungenen
  GNU-Testlauf zusaetzlich abgesichert.
- N2 (wichtig): status zeigte nach einem Wechsel zurueck in den
  Rettungsmodus weiterhin die letzte Migrationsphase aus einer liegen
  gebliebenen control.json (moeglich seit RuntimeDirectoryPreserve=
  restart aus Fixrunde 1). Phase wird jetzt nur noch angezeigt, wenn
  Modus tatsaechlich "migration" ist.
- N3 (klein): die Schluesselerkennung in setup_pi.sh war rein
  zeilenverankert ("^KEY=") und haette einen eingerueckten Bestandswert
  als fehlend gewertet - beim naechsten Sourcen der Konfiguration haette
  die zusaetzlich angehaengte Vorlagenzeile das eigene Passwort
  ueberschrieben. Muster auf fuehrenden Leerraum/export gehaertet.
- N4 (klein): test_setup_pi.sh liess setup_pi.sh ueber den scp-Stub echte
  Dateien im /tmp des Entwicklungsrechners anlegen und wieder loeschen.
  Neue Variable NINLIGHT_STAGE_DIR biegt das auf eine Sandbox um.

Alle vier Punkte mit Regressionstests, N2 und N3 zusaetzlich per
Mutationsprobe (Fix zurueckgenommen, Test wird rot) verifiziert.
This commit is contained in:
Michael Sauer
2026-09-13 19:28:42 +02:00
parent 1c077a15d0
commit 74198f6d31
4 changed files with 164 additions and 14 deletions
+62 -2
View File
@@ -26,6 +26,18 @@ FAILED=0
pass() { echo "ok - $1"; }
fail() { echo "FEHLER - $1" >&2; FAILED=1; }
file_mode() {
# $1 = Pfad. GNU-stat (Linux/Pi) muss zuerst probiert werden, nicht BSD-stat
# (macOS): "stat -f '%Lp' datei" deutet auf GNU-coreutils "-f" als
# "Dateisystemstatus" und '%Lp' als zweites Dateiargument, scheitert dabei
# zwar (rc != 0), schreibt aber vorher schon mehrere Zeilen Dateisystem-Infos
# auf stdout - der "||"-Zweig haengt danach zusaetzlich "600"/"644" an, und
# der Aufrufer sieht Muell statt der reinen Zahl (Fixrunde 2, N1). BSD-"stat
# -c" bricht dagegen sofort mit einem Fehler auf stderr ab, ganz ohne
# Ausgabe auf stdout.
stat -c '%a' "$1" 2>/dev/null || stat -f '%Lp' "$1" 2>/dev/null
}
setup_stubbin() {
local bindir=$1 log=$2
mkdir -p "$bindir"
@@ -100,16 +112,23 @@ setup_state() {
local state=$1
rm -rf "$state"
mkdir -p "$state/etc" "$state/opt" "$state/usr/local/bin" "$state/etc/systemd/system" \
"$state/etc/NetworkManager/system-connections"
"$state/etc/NetworkManager/system-connections" "$state/tmp"
}
# Fuehrt setup_pi.sh mit gestubbtem PATH und auf die Sandbox umgebogenen
# Systempfaden aus. $1 = state-Verzeichnis, $2 = stub-bin-Verzeichnis.
#
# NINLIGHT_STAGE_DIR biegt auch die per scp "uebertragenen" Zwischendateien auf
# die Sandbox um (Fixrunde 2, N4): ohne das wuerden scp/ssh-Stub sie unter dem
# echten /tmp des Entwicklungsrechners ablegen und am Ende per "rm -f" wieder
# loeschen - mit denselben, leicht kollidierenden Namen wie
# "ninlight-station.conf.example" oder "server.py".
run_setup() {
local state=$1 stubbin=$2
PATH="$stubbin:$PATH" \
NINLIGHT_SSH_BIN="$stubbin/ssh" \
NINLIGHT_SCP_BIN="$stubbin/scp" \
NINLIGHT_STAGE_DIR="$state/tmp" \
NINLIGHT_STATION_CONF="$state/etc/ninlight-station.conf" \
NINLIGHT_STATION_ROOT="$state/opt/ninlight-station" \
NINLIGHT_STATION_BIN="$state/usr/local/bin/ninlight-station" \
@@ -172,7 +191,7 @@ for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
fail "Schluessel $KEY fehlt weiterhin in der Konfiguration"
fi
done
MODE=$(stat -f '%Lp' "$CONF_OUT" 2>/dev/null || stat -c '%a' "$CONF_OUT" 2>/dev/null)
MODE=$(file_mode "$CONF_OUT")
[ "$MODE" = "600" ] && pass "Konfigurationsdatei bleibt 0600" || fail "Konfigurationsdatei hat falsche Rechte: $MODE"
echo
@@ -187,6 +206,47 @@ echo "$OUT" | grep -q "alle Schluessel vorhanden" && pass "meldet 'alle Schluess
DUP=$(grep -c '^EPD_REVISION=' "$CONF_OUT")
[ "$DUP" -eq 1 ] && pass "EPD_REVISION steht nur einmal in der Datei (kein doppeltes Anhaengen)" || fail "EPD_REVISION steht $DUP mal in der Datei"
echo
echo "=== Szenario 4 (Fixrunde 2, N3): ein eingerueckter Bestandswert gilt nicht als fehlend ==="
STATE="$TMPROOT/s4-state"; STUBBIN="$TMPROOT/bin4"; LOG="$TMPROOT/s4.log"
setup_state "$STATE"
setup_stubbin "$STUBBIN" "$LOG"
# Fuehrendes Leerzeichen vor PSK= - gueltiges ". datei"-Shell, aber eine rein
# zeilenverankerte Erkennung ("^PSK=") wuerde das als "fehlt" werten und die
# Vorlagenzeile anhaengen. Da das CLI die Datei per ". $CONF" sourct, gewinnt
# dann die LETZTE Zuweisung - das eigene Passwort waere durch den oeffentlichen
# Default aus der Vorlage ersetzt, ohne dass die Datei das irgendwo anzeigt.
cat >"$STATE/etc/ninlight-station.conf" <<'CONF'
SSID='eigene-ssid'
PSK='eigenes-eingeruecktes-passwort'
IMAGE_DIR='/opt/ninlight-station/images'
HOME_CONNECTION='preconfigured'
AP_CONNECTION='ninlight-recovery'
CONF
chmod 0600 "$STATE/etc/ninlight-station.conf"
: >"$LOG"
set +e
OUT=$(run_setup "$STATE" "$STUBBIN" 2>&1)
RC=$?
set -e
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC, Ausgabe: $OUT"
CONF_OUT4="$STATE/etc/ninlight-station.conf"
PSK_LINES=$(grep -cE '^[[:space:]]*PSK=' "$CONF_OUT4")
[ "$PSK_LINES" -eq 1 ] && pass "PSK steht weiterhin nur einmal in der Datei (nicht dupliziert)" || fail "PSK steht $PSK_LINES mal in der Datei: $(cat "$CONF_OUT4")"
if grep -Fq "eigenes-eingeruecktes-passwort" "$CONF_OUT4"; then
pass "der eingerueckte Bestandswert von PSK blieb erhalten"
else
fail "der eingerueckte Bestandswert von PSK wurde ersetzt: $(cat "$CONF_OUT4")"
fi
if grep -Fqx "PSK='ninlight-recovery'" "$CONF_OUT4"; then
fail "der oeffentliche Vorlagen-Default fuer PSK wurde zusaetzlich angehaengt - eigenes Passwort waere beim Sourcen ueberschrieben worden"
else
pass "der oeffentliche Vorlagen-Default fuer PSK wurde NICHT angehaengt"
fi
for KEY in MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
grep -Eq "^${KEY}=" "$CONF_OUT4" && pass "fehlender Schluessel $KEY wurde trotzdem ergaenzt" || fail "Schluessel $KEY fehlt weiterhin"
done
echo
if [ "$FAILED" -eq 0 ]; then
echo "Alle Tests bestanden."