131 lines
7.6 KiB
Bash
Executable File
131 lines
7.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# Sync-Test: lokaler Server plus headless Shell, Änderungen in beide Richtungen: dev/tests/sync.sh
|
|
DIR=$(cd "$(dirname "$0")/.." && pwd)
|
|
ROOT=$(cd "$DIR/.." && pwd)
|
|
E="$DIR/eval.sh"
|
|
OUT=/tmp/desktop-app-nested/out.txt
|
|
PORT=8041
|
|
URL="http://localhost:$PORT"
|
|
SERVER_DATA=/tmp/desktop-app-sync-server
|
|
AUTH="Authorization: Bearer test"
|
|
FAILED=0
|
|
|
|
PRE='const ext = Main.extensionManager.lookup("desktop@marha.de").stateObj;
|
|
const notes = ext._root.get_child_at_index(0);
|
|
const todos = ext._root.get_child_at_index(1).get_child_at_index(1);
|
|
const store = ext._store;
|
|
const read = p => new TextDecoder().decode(GLib.file_get_contents(p)[1]);
|
|
const exists = p => GLib.file_test(p, GLib.FileTest.EXISTS);
|
|
const state = () => JSON.parse(read(store.dir + "/sync-state.json"));
|
|
const St = imports.gi.St;
|
|
const labels = a => { const out = []; const walk = n => { if (n instanceof St.Label && n.text) out.push(n.text); n.get_children().forEach(walk); }; walk(a); return out; };
|
|
'
|
|
val() {
|
|
"$E" "$PRE GLib.file_set_contents('$OUT', String($1)); 'w'" | grep -v "^OK '\"w\"')$"
|
|
cat "$OUT"
|
|
}
|
|
api() { curl -s -H "$AUTH" -H "Content-Type: application/json" "$@"; }
|
|
now() { date -u +%Y-%m-%dT%H:%M:%S.000Z; }
|
|
push() { api -X POST "$URL/api/sync" -d "$1" >/dev/null; }
|
|
check() {
|
|
local name=$1 actual=$2 expected=$3
|
|
if [ "$actual" == "$expected" ]; then
|
|
echo "ok $name"
|
|
else
|
|
echo "FAIL $name"; echo " ist: $actual"; echo " soll: $expected"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
}
|
|
cleanup() {
|
|
kill $SERVER_PID 2>/dev/null
|
|
"$DIR/stop.sh"
|
|
pkill -f "^gnome-shell --(nested|headless)" 2>/dev/null
|
|
wait 2>/dev/null
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "=== Server starten"
|
|
rm -rf "$SERVER_DATA"
|
|
(cd "$ROOT/backend" && DATA_DIR="$SERVER_DATA" SYNC_TOKEN=test .venv/bin/uvicorn app.main:app --port $PORT > /tmp/desktop-app-sync-server.log 2>&1 &)
|
|
for i in $(seq 1 30); do curl -s "$URL/api/health" >/dev/null && break; sleep 0.5; done
|
|
SERVER_PID=$(pgrep -f "uvicorn app.main:app --port $PORT")
|
|
check "Server läuft" "$(curl -s $URL/api/health)" '{"ok":true}'
|
|
|
|
echo "=== Shell mit Sync starten"
|
|
SYNC_URL="$URL" "$DIR/restart.sh" headless >/dev/null
|
|
check "Sync aktiv" "$(val 'ext._sync.active')" "true"
|
|
|
|
echo "=== Lokal → Server"
|
|
"$E" "$PRE store.saveEvent({title: 'Zahnarzt', date: '2026-09-21', start: '09:00', end: '10:00', endDate: null}); store.saveTodo({title: 'Einkaufen', date: null, time: null}); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Termin auf dem Server" "$(api "$URL/api/events" | python3 -c 'import json,sys; print([e["title"] for e in json.load(sys.stdin)])')" "['Zahnarzt']"
|
|
check "Todo auf dem Server" "$(api "$URL/api/todos" | python3 -c 'import json,sys; print([t["title"] for t in json.load(sys.stdin)])')" "['Einkaufen']"
|
|
check "Stand gemerkt" "$(val 'state().cursor > 0')" "true"
|
|
|
|
echo "=== Server → lokal"
|
|
push '{"todos": [{"id": "r1", "updated": "2026-09-19T10:00:00.000Z", "title": "Vom Handy", "date": null, "time": null, "done": false}], "notes": [{"id": "Remote/Idee.md", "updated": "2026-09-19T10:00:00.000Z", "content": "# Vom Server"}]}'
|
|
sleep 3
|
|
check "Todo vom Server in der Liste" "$(val 'store.sortedTodos().map(t => t.title).sort().join(",")')" "Einkaufen,Vom Handy"
|
|
check "Todo vom Server im Panel" "$(val 'labels(todos).includes("Vom Handy")')" "true"
|
|
check "Notiz vom Server als Datei" "$(val 'exists(store.notesDir + "/Remote/Idee.md") + " " + JSON.stringify(read(store.notesDir + "/Remote/Idee.md"))')" 'true "# Vom Server"'
|
|
check "Notiz vom Server im Baum" "$(val 'labels(notes).includes("▸ Remote")')" "true"
|
|
check "Übernommene Notiz wird nicht zurückgeschickt" "$(val 'state().pushed.notes["Remote/Idee.md"]')" "2026-09-19T10:00:00.000Z"
|
|
|
|
echo "=== Konflikt: neuester gewinnt"
|
|
push '{"todos": [{"id": "r1", "updated": "2026-09-19T09:00:00.000Z", "title": "Älter", "date": null, "time": null, "done": false}]}'
|
|
sleep 3
|
|
check "Älterer Stand verliert" "$(val 'store.todos.find(t => t.id === "r1").title')" "Vom Handy"
|
|
push '{"todos": [{"id": "r1", "updated": "'$(now)'", "title": "Neuer", "date": null, "time": null, "done": false}]}'
|
|
sleep 3
|
|
check "Neuerer Stand gewinnt" "$(val 'store.todos.find(t => t.id === "r1").title')" "Neuer"
|
|
sleep 1
|
|
"$E" "$PRE store.toggleTodo('r1'); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Lokale Änderung ist neuer und landet auf dem Server" "$(api "$URL/api/todos" | python3 -c 'import json,sys; print([t["done"] for t in json.load(sys.stdin) if t["id"] == "r1"])')" "[True]"
|
|
push '{"todos": [{"id": "r1", "updated": "2026-09-19T09:30:00.000Z", "title": "Verspätet", "date": null, "time": null, "done": false}]}'
|
|
sleep 3
|
|
check "Verspäteter älterer Stand ändert nichts" "$(val 'store.todos.find(t => t.id === "r1").title + " " + store.todos.find(t => t.id === "r1").done')" "Neuer true"
|
|
|
|
echo "=== Löschen in beide Richtungen"
|
|
"$E" "$PRE store.removeTodo(store.todos.find(t => t.title === 'Einkaufen').id); store.deleteNote(store.notesDir + '/Remote/Idee.md'); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Gelöschtes Todo auf dem Server weg" "$(api "$URL/api/todos" | python3 -c 'import json,sys; print(sorted(t["title"] for t in json.load(sys.stdin)))')" "['Neuer']"
|
|
check "Gelöschte Notiz auf dem Server weg" "$(api "$URL/api/notes" | python3 -c 'import json,sys; print([n["id"] for n in json.load(sys.stdin)])')" "[]"
|
|
"$E" "$PRE store.createNote(store.notesDir, 'Lokal'); store.writeNote(store.notesDir + '/Lokal.md', 'lokal'); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Lokale Notiz auf dem Server" "$(api "$URL/api/notes" | python3 -c 'import json,sys; print([(n["id"], n["content"]) for n in json.load(sys.stdin)])')" "[('Lokal.md', 'lokal')]"
|
|
push '{"notes": [{"id": "Lokal.md", "updated": "'$(now)'", "deleted": true}]}'
|
|
sleep 3
|
|
check "Vom Server gelöschte Notiz lokal weg" "$(val 'exists(store.notesDir + "/Lokal.md")')" "false"
|
|
|
|
echo "=== Offene Notiz bleibt unangetastet"
|
|
"$E" "$PRE store.createNote(store.notesDir, 'Offen'); store.writeNote(store.notesDir + '/Offen.md', 'meins'); notes._select({type: 'note', path: store.notesDir + '/Offen.md', name: 'Offen'}); 'ok'" >/dev/null
|
|
sleep 3
|
|
"$E" "$PRE notes._startEdit(); 'ok'" >/dev/null
|
|
CURSOR=$(val 'state().cursor')
|
|
push '{"notes": [{"id": "Offen.md", "updated": "'$(now)'", "content": "vom server"}]}'
|
|
sleep 3
|
|
check "Datei während des Tippens unverändert" "$(val 'read(store.notesDir + "/Offen.md") + " " + (state().cursor === '"$CURSOR"')')" "meins true"
|
|
"$E" "$PRE notes._stopEdit(); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Nach dem Schließen übernommen" "$(val 'read(store.notesDir + "/Offen.md") + " " + notes._content.text')" "vom server vom server"
|
|
|
|
echo "=== Umbenennen"
|
|
sleep 1
|
|
"$E" "$PRE store.renameNote(store.notesDir + '/Offen.md', 'Neu'); 'ok'" >/dev/null
|
|
sleep 3
|
|
check "Umbenannt auf dem Server" "$(api "$URL/api/notes" | python3 -c 'import json,sys; print([n["id"] for n in json.load(sys.stdin)])')" "['Neu.md']"
|
|
|
|
echo "=== Server weg und wieder da"
|
|
kill $SERVER_PID; sleep 1
|
|
"$E" "$PRE store.saveTodo({title: 'Offline', date: null, time: null}); 'ok'" >/dev/null
|
|
sleep 3
|
|
(cd "$ROOT/backend" && DATA_DIR="$SERVER_DATA" SYNC_TOKEN=test .venv/bin/uvicorn app.main:app --port $PORT >> /tmp/desktop-app-sync-server.log 2>&1 &)
|
|
for i in $(seq 1 30); do curl -s "$URL/api/health" >/dev/null && break; sleep 0.5; done
|
|
SERVER_PID=$(pgrep -f "uvicorn app.main:app --port $PORT")
|
|
sleep 4
|
|
check "Offline-Änderung nachgeliefert" "$(api "$URL/api/todos" | python3 -c 'import json,sys; print(sorted(t["title"] for t in json.load(sys.stdin)))')" "['Neuer', 'Offline']"
|
|
check "Keine JS-Fehler" "$(grep -c 'JS ERROR' /tmp/desktop-app-nested.log)" "0"
|
|
echo
|
|
if [ $FAILED -eq 0 ]; then echo "alle ok"; else echo "$FAILED FEHLER"; grep -n "JS ERROR" -A5 /tmp/desktop-app-nested.log | head -30; grep "desktop-app sync" /tmp/desktop-app-nested.log | tail -5; fi
|