feat: harden dashcam transfer tegen flakke SD-lezer + stale-mount cleanup

- dashcam-transfer.py: log echte OSError (shutil.copy2 i.p.v. cp), pre-flight
  leesbaarheidscheck voor ruimte-wissen, retry (3x) + early-abort (5x), guards
  op bestandssysteem-calls, WhatsApp GESTOPT bij defecte kaart
- dashcam-transfer-mount.sh: skip als service al draait, read-only fsck.exfat
  health check, %k device-naam doorgeven
- dashcam-transfer-cleanup.sh: nieuw - ruimt stale mount op bij udev REMOVE
- udev/99-dashcam-trigger.rules: ADD + REMOVE regels met %k
- tests: 20 python + 7 mount + 12 cleanup checks
- README: architectuur, robuustheid, deploy-instructies
This commit is contained in:
Eddy de Vink 2026-08-11 20:23:24 +02:00
commit 8cc04ea409
9 changed files with 779 additions and 76 deletions

118
tests/test_cleanup_script.sh Executable file
View file

@ -0,0 +1,118 @@
#!/bin/bash
# Functional test for dashcam-transfer-cleanup.sh using fake tools on PATH.
set -u
SCRIPT="$(cd "$(dirname "$0")/.." && pwd)/dashcam-transfer-cleanup.sh"
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
FAKEBIN="$WORK/bin"
mkdir -p "$FAKEBIN"
LOGGER_FILE="$WORK/logger.log"
UMOUNT_MARKER="$WORK/umounted"
LAZY_MARKER="$WORK/lazy"
STOP_MARKER="$WORK/stopped"
export LOGGER_FILE UMOUNT_MARKER LAZY_MARKER STOP_MARKER FAKE_SRC FAKE_UMOUNT_FAIL
cat > "$FAKEBIN/logger" <<'EOF'
#!/bin/bash
echo "logger: $*" >> "$LOGGER_FILE"
EOF
cat > "$FAKEBIN/findmnt" <<'EOF'
#!/bin/bash
if [ -z "$FAKE_SRC" ]; then
exit 1
fi
echo "$FAKE_SRC"
EOF
cat > "$FAKEBIN/systemctl" <<'EOF'
#!/bin/bash
if [ "$1" = "stop" ]; then
touch "$STOP_MARKER"
fi
EOF
cat > "$FAKEBIN/umount" <<'EOF'
#!/bin/bash
if [ "${1:-}" = "-l" ]; then
touch "$LAZY_MARKER"
exit 0
fi
if [ "$FAKE_UMOUNT_FAIL" = "1" ]; then
exit 1
fi
touch "$UMOUNT_MARKER"
EOF
chmod +x "$FAKEBIN"/*
pass=0
fail=0
check() { # $1=desc, $2=condition
if eval "$2"; then
echo "PASS: $1"
pass=$((pass + 1))
else
echo "FAIL: $1"
fail=$((fail + 1))
fi
}
reset() {
rm -f "$LOGGER_FILE" "$UMOUNT_MARKER" "$LAZY_MARKER" "$STOP_MARKER"
unset FAKE_UMOUNT_FAIL
}
# Test 1: niet gemount → niets te doen
reset
FAKE_SRC=""
PATH="$FAKEBIN:$PATH" "$SCRIPT"
check "niet gemount → geen umount" 'test ! -e "$UMOUNT_MARKER"'
check "niet gemount → geen service stop" 'test ! -e "$STOP_MARKER"'
# Test 2: device nog aanwezig → mount blijft
reset
FAKE_SRC="$WORK/fake-device"
touch "$FAKE_SRC"
PATH="$FAKEBIN:$PATH" "$SCRIPT"
check "device aanwezig → geen umount" 'test ! -e "$UMOUNT_MARKER"'
check "device aanwezig → geen service stop" 'test ! -e "$STOP_MARKER"'
# Test 3: device verdwenen → stop + umount
reset
FAKE_SRC="$WORK/gone-device"
PATH="$FAKEBIN:$PATH" "$SCRIPT"
check "device weg → service gestopt" 'test -e "$STOP_MARKER"'
check "device weg → umount uitgevoerd" 'test -e "$UMOUNT_MARKER"'
check "device weg → geen lazy nodig" 'test ! -e "$LAZY_MARKER"'
# Test 4: umount mislukt → lazy umount
reset
FAKE_SRC="$WORK/gone-device"
export FAKE_UMOUNT_FAIL=1
PATH="$FAKEBIN:$PATH" "$SCRIPT"
check "umount faalt → lazy umount" 'test -e "$LAZY_MARKER"'
# Test 5: node bestaat nog maar source = verwijderd device → stale
reset
mkdir -p "$WORK/dev"
touch "$WORK/dev/sde1"
FAKE_SRC="$WORK/dev/sde1"
PATH="$FAKEBIN:$PATH" "$SCRIPT" sde1
check "source matcht verwijderd device → umount" 'test -e "$UMOUNT_MARKER"'
check "source matcht verwijderd device → service gestopt" 'test -e "$STOP_MARKER"'
# Test 6: ander device verwijdert, sdcard-healthy → niets doen
reset
touch "$WORK/dev/sdb2"
FAKE_SRC="$WORK/dev/sde1"
PATH="$FAKEBIN:$PATH" "$SCRIPT" sdb2
check "ander device → geen umount" 'test ! -e "$UMOUNT_MARKER"'
check "ander device → geen service stop" 'test ! -e "$STOP_MARKER"'
echo "=== $pass pass, $fail fail ==="
[ "$fail" -eq 0 ]