Robuste Darktable-Synchronisation: sequenzieller Ablauf, Versions- und Concurrent-Schutz
- Race Condition behoben: Pre-Sync wird vollstaendig abgewartet bevor Darktable startet - Post-Sync nach Schliessen von Darktable eingefuehrt (bisher fehlend) - .env aus festem Pfad ~/.config/darktable-sync/.env geladen (nicht mehr relativ) - Server-Erreichbarkeit per SSH statt ping (Firewall-sicher) - Darktable-Versionscheck (Major.Minor) vor Download mit Abbruch bei Konflikt - DB-Backup vor jedem Download (library.db.bak, data.db.bak) - sync_pending-Marker bei Offline/Fehler, Hinweis beim naechsten Start - darktable.active-Marker auf Server fuer Concurrent-Erkennung - Lock-Dateien vom Sync ausgeschlossen - systemd-Timer entfernt, Service bleibt als manueller Trigger - Gemeinsame Hilfsfunktionen in darktable_common.sh extrahiert - 20 BATS-Tests mit vollstaendigem Stub-System ohne GUI-Dialoge Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load helpers/setup
|
||||
|
||||
COMMON_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_common.sh"
|
||||
|
||||
@test "check_dependency schlaegt fehl wenn Tool fehlt" {
|
||||
run bash -c "source '$COMMON_SCRIPT'; check_dependency nicht_existierendes_tool"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"nicht_existierendes_tool"* ]]
|
||||
[[ "$output" == *"sudo apt install"* ]]
|
||||
}
|
||||
|
||||
@test "check_dependency besteht wenn Tool vorhanden" {
|
||||
run bash -c "source '$COMMON_SCRIPT'; check_dependency bash"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "load_config schlaegt fehl wenn .env fehlt" {
|
||||
rm -f "$CONFIG_DIR/.env"
|
||||
run bash -c "source '$COMMON_SCRIPT'; load_config"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"nicht gefunden"* ]]
|
||||
}
|
||||
|
||||
@test "load_config laedt .env erfolgreich" {
|
||||
create_valid_env
|
||||
run bash -c "source '$COMMON_SCRIPT'; load_config; echo \$SERVER_IP"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"192.168.1.100"* ]]
|
||||
}
|
||||
|
||||
@test "validate_config schlaegt fehl wenn Variable leer" {
|
||||
create_valid_env
|
||||
echo "SERVER_IP=" >> "$CONFIG_DIR/.env"
|
||||
run bash -c "source '$COMMON_SCRIPT'; load_config; validate_config"
|
||||
[ "$status" -eq 1 ]
|
||||
[[ "$output" == *"SERVER_IP"* ]]
|
||||
}
|
||||
|
||||
@test "server_reachable gibt false zurueck wenn SSH fehlschlaegt" {
|
||||
create_valid_env
|
||||
run_with_stubs bash -c "
|
||||
export SSH_STUB_FAIL=1
|
||||
source '$COMMON_SCRIPT'
|
||||
load_config
|
||||
server_reachable
|
||||
"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "server_reachable gibt true zurueck wenn SSH erfolgreich" {
|
||||
create_valid_env
|
||||
run_with_stubs bash -c "
|
||||
export SSH_STUB_FAIL=0
|
||||
source '$COMMON_SCRIPT'
|
||||
load_config
|
||||
server_reachable
|
||||
"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "ask_user: j-Eingabe gibt Exit 0" {
|
||||
TMP_SCRIPT=$(mktemp)
|
||||
echo "source '$COMMON_SCRIPT'; ask_user 'Titel' 'Frage?'" > "$TMP_SCRIPT"
|
||||
run bash -c "echo 'j' | env PATH='$STUBS_DIR:$PATH' bash '$TMP_SCRIPT'"
|
||||
rm -f "$TMP_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "ask_user: n-Eingabe gibt Exit 1" {
|
||||
TMP_SCRIPT=$(mktemp)
|
||||
echo "source '$COMMON_SCRIPT'; ask_user 'Titel' 'Frage?'" > "$TMP_SCRIPT"
|
||||
run bash -c "echo 'n' | env PATH='$STUBS_DIR:$PATH' bash '$TMP_SCRIPT'"
|
||||
rm -f "$TMP_SCRIPT"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load helpers/setup
|
||||
|
||||
SYNC_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_sync.sh"
|
||||
|
||||
setup() {
|
||||
create_valid_env
|
||||
mkdir -p "$HOME/.config/darktable"
|
||||
touch "$HOME/.config/darktable/library.db"
|
||||
touch "$HOME/.config/darktable/data.db"
|
||||
mkdir -p "$HOME/Pictures"
|
||||
export DISPLAY=:99
|
||||
}
|
||||
|
||||
@test "sync_pending wird gesetzt wenn Server nicht erreichbar" {
|
||||
run_with_stubs env SSH_STUB_FAIL=1 bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$CONFIG_DIR/sync_pending" ]
|
||||
}
|
||||
|
||||
@test "sync_pending wird entfernt bei erfolgreichem Sync" {
|
||||
touch "$CONFIG_DIR/sync_pending"
|
||||
run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -f "$CONFIG_DIR/sync_pending" ]
|
||||
}
|
||||
|
||||
@test "sync_pending wird gesetzt wenn rsync fehlschlaegt" {
|
||||
run_with_stubs env SSH_STUB_FAIL=0 RSYNC_STUB_FAIL=1 bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 1 ]
|
||||
[ -f "$CONFIG_DIR/sync_pending" ]
|
||||
}
|
||||
|
||||
@test "DB-Backup wird vor Download erstellt" {
|
||||
run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ -f "$HOME/.config/darktable/library.db.bak" ]
|
||||
[ -f "$HOME/.config/darktable/data.db.bak" ]
|
||||
}
|
||||
|
||||
@test "Versionskonflikt: gleiche Major.Minor gibt kein Exit 1" {
|
||||
run_with_stubs env SSH_STUB_FAIL=0 SSH_STUB_OUTPUT="this is darktable 5.0.1" \
|
||||
DARKTABLE_STUB_VERSION="5.0.1" bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "Versionskonflikt: andere Major.Minor gibt Exit 1" {
|
||||
run_with_stubs env SSH_STUB_FAIL=0 SSH_STUB_OUTPUT="this is darktable 4.8.0" \
|
||||
DARKTABLE_STUB_VERSION="5.0.0" bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 1 ]
|
||||
[ -f "$CONFIG_DIR/sync_pending" ]
|
||||
}
|
||||
|
||||
@test "Lockfile wird nach Abschluss entfernt" {
|
||||
run_with_stubs env SSH_STUB_FAIL=0 bash "$SYNC_SCRIPT"
|
||||
[ "$status" -eq 0 ]
|
||||
[ ! -f "/tmp/darktable_sync.sh.lock" ]
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load helpers/setup
|
||||
|
||||
WRAPPER_SCRIPT="$BATS_TEST_DIRNAME/../scripts/darktable_wrapper.sh"
|
||||
|
||||
setup() {
|
||||
create_valid_env
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
|
||||
# Sync-Stub: tut nichts
|
||||
cat > "$HOME/.local/bin/darktable_sync.sh" <<'EOF'
|
||||
#!/bin/bash
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$HOME/.local/bin/darktable_sync.sh"
|
||||
|
||||
# Lokale Stubs in einem eigenen Verzeichnis pro Test (kein Überschreiben der globalen Stubs)
|
||||
LOCAL_STUBS="$BATS_TMPDIR/stubs"
|
||||
mkdir -p "$LOCAL_STUBS"
|
||||
export LOCAL_STUBS
|
||||
|
||||
# Alle Stubs kopieren (verhindert echte Dialoge und GUI-Aufrufe)
|
||||
cp "$BATS_TEST_DIRNAME/stubs/ssh" "$LOCAL_STUBS/ssh"
|
||||
cp "$BATS_TEST_DIRNAME/stubs/notify-send" "$LOCAL_STUBS/notify-send"
|
||||
cp "$BATS_TEST_DIRNAME/stubs/darktable" "$LOCAL_STUBS/darktable"
|
||||
cp "$BATS_TEST_DIRNAME/stubs/pgrep" "$LOCAL_STUBS/pgrep"
|
||||
cp "$BATS_TEST_DIRNAME/stubs/zenity" "$LOCAL_STUBS/zenity"
|
||||
cp "$BATS_TEST_DIRNAME/stubs/kdialog" "$LOCAL_STUBS/kdialog"
|
||||
chmod +x "$LOCAL_STUBS/"*
|
||||
|
||||
export DISPLAY=:99
|
||||
}
|
||||
|
||||
@test "Server nicht erreichbar + Dialog abgelehnt: kein Darktable-Start, Exit 0" {
|
||||
run env PATH="$LOCAL_STUBS:$PATH" SSH_STUB_FAIL=1 \
|
||||
bash -c "echo 'n' | bash '$WRAPPER_SCRIPT'"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "Server nicht erreichbar + Dialog bestaetigt: Darktable startet" {
|
||||
STARTED_FILE="$BATS_TMPDIR/darktable_started"
|
||||
cat > "$LOCAL_STUBS/darktable" <<EOF
|
||||
#!/bin/bash
|
||||
if [[ "\${1:-}" == "--version" ]]; then echo "this is darktable 5.0.1"; exit 0; fi
|
||||
touch "$STARTED_FILE"
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$LOCAL_STUBS/darktable"
|
||||
|
||||
run env PATH="$LOCAL_STUBS:$PATH" SSH_STUB_FAIL=1 \
|
||||
bash -c "echo 'j' | bash '$WRAPPER_SCRIPT'"
|
||||
[ -f "$STARTED_FILE" ]
|
||||
}
|
||||
|
||||
@test "Post-Sync schlaegt fehl: sync_pending gesetzt" {
|
||||
SSH_CALL_COUNT="$BATS_TMPDIR/ssh_call_count"
|
||||
echo "0" > "$SSH_CALL_COUNT"
|
||||
|
||||
cat > "$LOCAL_STUBS/ssh" <<EOF
|
||||
#!/bin/bash
|
||||
count=\$(cat "$SSH_CALL_COUNT")
|
||||
count=\$((count + 1))
|
||||
echo "\$count" > "$SSH_CALL_COUNT"
|
||||
# Ab Aufruf 3 fehlschlagen (Post-Sync-Erreichbarkeitstest)
|
||||
if [ "\$count" -ge 3 ]; then exit 1; fi
|
||||
exit 0
|
||||
EOF
|
||||
chmod +x "$LOCAL_STUBS/ssh"
|
||||
|
||||
run env PATH="$LOCAL_STUBS:$PATH" SSH_STUB_FAIL=0 bash "$WRAPPER_SCRIPT"
|
||||
[ -f "$CONFIG_DIR/sync_pending" ]
|
||||
}
|
||||
|
||||
@test "Darktable laeuft bereits: Abbruch mit Exit 1" {
|
||||
run env PATH="$LOCAL_STUBS:$PATH" PGREP_STUB_FOUND=1 bash "$WRAPPER_SCRIPT"
|
||||
[ "$status" -eq 1 ]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# Gemeinsames Test-Setup
|
||||
|
||||
STUBS_DIR="$BATS_TEST_DIRNAME/stubs"
|
||||
|
||||
# Temporaere HOME anlegen
|
||||
export HOME="$BATS_TMPDIR/home"
|
||||
mkdir -p "$HOME/.config/darktable-sync"
|
||||
mkdir -p "$HOME/.config/darktable"
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
export CONFIG_DIR="$HOME/.config/darktable-sync"
|
||||
|
||||
create_valid_env() {
|
||||
cat > "$CONFIG_DIR/.env" <<EOF
|
||||
SERVER_USER=testuser
|
||||
SERVER_SSH_PORT=22
|
||||
SERVER_IP=192.168.1.100
|
||||
SERVER_DB_DIR=/remote/db
|
||||
SERVER_PHOTO_DIR=/remote/photos
|
||||
LOCAL_DARKTABLE_DB_DIR=$HOME/.config/darktable
|
||||
LOCAL_PHOTO_DIR=$HOME/Pictures
|
||||
DARKTABLE_BIN=darktable
|
||||
SYNC_BIN=$HOME/.local/bin/darktable_sync.sh
|
||||
EOF
|
||||
}
|
||||
|
||||
# Fuehrt ein Script mit dem Stubs-Verzeichnis vorne im PATH aus
|
||||
run_with_stubs() {
|
||||
run env PATH="$STUBS_DIR:$PATH" "$@"
|
||||
}
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# DARKTABLE_STUB_VERSION=x.y.z → gibt diese Version aus
|
||||
if [[ "${1:-}" == "--version" ]]; then
|
||||
echo "this is darktable ${DARKTABLE_STUB_VERSION:-5.0.1}"
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# kdialog-Stub fuer Tests: liest j/n aus stdin
|
||||
read -r ans
|
||||
[[ "$ans" =~ ^[jJyY] ]]
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
# notify-send-Stub: immer erfolgreich
|
||||
exit 0
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# pgrep-Stub: Verhalten per Umgebungsvariable steuerbar
|
||||
# PGREP_STUB_FOUND=1 → Prozess gefunden
|
||||
if [ "${PGREP_STUB_FOUND:-0}" = "1" ]; then
|
||||
echo "12345"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# rsync-Stub: Verhalten per Umgebungsvariable steuerbar
|
||||
# RSYNC_STUB_FAIL=1 → schlaegt fehl
|
||||
if [ "${RSYNC_STUB_FAIL:-0}" = "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# SSH_STUB_FAIL=1 → schlaegt fehl
|
||||
if [ "${SSH_STUB_FAIL:-0}" = "1" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "${SSH_STUB_OUTPUT:-}"
|
||||
exit 0
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# zenity-Stub fuer Tests: liest j/n aus stdin
|
||||
read -r ans
|
||||
[[ "$ans" =~ ^[jJyY] ]]
|
||||
Reference in New Issue
Block a user