177 lines
6.3 KiB
Bash
177 lines
6.3 KiB
Bash
#!/usr/bin/env bash
|
|
# Durchlauf durch die Phase-2-Endpunkte. Legt eine Testliste an, füllt
|
|
# sie und prüft die gruppierte Ansicht.
|
|
#
|
|
# bash tools/smoke-test.sh http://localhost:8080 admin@example.de
|
|
#
|
|
# Das Passwort wird abgefragt, nicht als Argument übergeben. Damit landet
|
|
# es nicht in der Shell-History, und Sonderzeichen wie ! machen keinen
|
|
# Ärger: die History-Expansion der Bash greift nur bei interaktiver
|
|
# Eingabe auf der Kommandozeile, nicht bei "read".
|
|
#
|
|
# Alternativ per Umgebungsvariable, z.B. aus einem Passwortmanager:
|
|
# EA_PASSWORD="$(pass show einkaufsapp)" bash tools/smoke-test.sh URL MAIL
|
|
#
|
|
# Benötigt curl sowie jq oder python3.
|
|
|
|
set -euo pipefail
|
|
|
|
BASE="${1:-http://localhost:8080}"
|
|
EMAIL="${2:?E-Mail angeben}"
|
|
|
|
if [ -n "${EA_PASSWORD:-}" ]; then
|
|
PASSWORD="$EA_PASSWORD"
|
|
else
|
|
printf 'Passwort für %s: ' "$EMAIL" >&2
|
|
IFS= read -rs PASSWORD
|
|
printf '\n' >&2
|
|
fi
|
|
[ -n "$PASSWORD" ] || { echo "Kein Passwort angegeben." >&2; exit 1; }
|
|
|
|
# jq ist bequem, aber nicht zwingend. Ohne jq springt python3 ein -
|
|
# beides kann JSON korrekt erzeugen und lesen, was hier der Punkt ist:
|
|
# Passwoerter und Umlaute duerfen nicht per String-Bastelei in die
|
|
# Nutzlast wandern.
|
|
if command -v jq >/dev/null 2>&1; then
|
|
JSON_TOOL=jq
|
|
elif command -v python3 >/dev/null 2>&1; then
|
|
JSON_TOOL=python3
|
|
else
|
|
echo "Benötigt jq oder python3. Installieren mit: apt install jq" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# json_obj key value [key value ...] -> JSON-Objekt auf stdout
|
|
json_obj() {
|
|
if [ "$JSON_TOOL" = jq ]; then
|
|
local args=() filter="{" first=1 i=1
|
|
while [ $# -gt 0 ]; do
|
|
args+=(--arg "k$i" "$1" --arg "v$i" "$2")
|
|
[ $first -eq 1 ] || filter="$filter,"
|
|
filter="$filter(\$k$i): \$v$i"
|
|
first=0; i=$((i+1)); shift 2
|
|
done
|
|
jq -n "${args[@]}" "$filter}"
|
|
else
|
|
python3 -c 'import json,sys; a=sys.argv[1:]; print(json.dumps(dict(zip(a[::2], a[1::2]))))' "$@"
|
|
fi
|
|
}
|
|
|
|
# json_get <schluessel> - liest einen Wert aus JSON auf stdin
|
|
json_get() {
|
|
if [ "$JSON_TOOL" = jq ]; then
|
|
jq -r ".$1 // empty"
|
|
else
|
|
python3 -c 'import json,sys
|
|
try:
|
|
d = json.load(sys.stdin)
|
|
except Exception:
|
|
sys.exit(0)
|
|
v = d.get(sys.argv[1]) if isinstance(d, dict) else None
|
|
print("" if v is None else v)' "$1"
|
|
fi
|
|
}
|
|
|
|
JAR="$(mktemp)"
|
|
chmod 600 "$JAR"
|
|
trap 'rm -f "$JAR"' EXIT
|
|
|
|
say() { printf '\n\033[1m== %s\033[0m\n' "$1"; }
|
|
|
|
api() {
|
|
local method="$1" path="$2" body="${3:-}"
|
|
local csrf
|
|
csrf="$(awk '/ea_csrf/ {print $7}' "$JAR")"
|
|
if [ -n "$body" ]; then
|
|
curl -sS -b "$JAR" -c "$JAR" -X "$method" "$BASE$path" \
|
|
-H 'Content-Type: application/json' -H "X-CSRF-Token: $csrf" \
|
|
--data-binary "$body"
|
|
else
|
|
curl -sS -b "$JAR" -c "$JAR" -X "$method" "$BASE$path" \
|
|
-H "X-CSRF-Token: $csrf"
|
|
fi
|
|
}
|
|
|
|
say "Anmelden"
|
|
# JSON strukturiert erzeugen, nicht per String-Interpolation: sonst
|
|
# zerbricht jedes " oder \ im Passwort die Nutzlast.
|
|
LOGIN_JSON="$(json_obj email "$EMAIL" password "$PASSWORD")"
|
|
curl -sS -c "$JAR" -X POST "$BASE/api/auth/login" \
|
|
-H 'Content-Type: application/json' \
|
|
--data-binary "$LOGIN_JSON" > /tmp/ea_login.$$ 2>&1
|
|
WHO="$(json_get email < /tmp/ea_login.$$)"
|
|
if [ -z "$WHO" ]; then
|
|
echo " Anmeldung fehlgeschlagen: $(json_get detail < /tmp/ea_login.$$)" >&2
|
|
rm -f /tmp/ea_login.$$
|
|
exit 1
|
|
fi
|
|
echo " angemeldet als $WHO"
|
|
rm -f /tmp/ea_login.$$
|
|
unset PASSWORD LOGIN_JSON
|
|
|
|
say "CSRF-Schutz muss greifen (erwartet 403)"
|
|
code=$(curl -sS -o /dev/null -w '%{http_code}' -b "$JAR" -X POST "$BASE/api/lists" \
|
|
-H 'Content-Type: application/json' --data-binary '{"name":"Ohne CSRF"}')
|
|
[ "$code" = "403" ] && echo " ok: $code" || { echo " FEHLER: $code statt 403"; exit 1; }
|
|
|
|
say "Liste anlegen"
|
|
LIST=$(api POST /api/lists "$(json_obj name Wocheneinkauf)" | json_get id)
|
|
echo " list_id=$LIST"
|
|
|
|
say "Märkte anlegen"
|
|
EDEKA=$(api POST "/api/lists/$LIST/markets" '{"name":"Edeka","sort_order":1}' | json_get id)
|
|
DM=$(api POST "/api/lists/$LIST/markets" '{"name":"dm","sort_order":2}' | json_get id)
|
|
echo " Edeka=$EDEKA dm=$DM"
|
|
|
|
say "Doppelter Marktname muss abgelehnt werden (erwartet 409)"
|
|
code=$(curl -sS -o /dev/null -w '%{http_code}' -b "$JAR" -X POST "$BASE/api/lists/$LIST/markets" \
|
|
-H 'Content-Type: application/json' \
|
|
-H "X-CSRF-Token: $(awk '/ea_csrf/ {print $7}' "$JAR")" \
|
|
--data-binary '{"name":"Edeka"}')
|
|
[ "$code" = "409" ] && echo " ok: $code" || { echo " FEHLER: $code statt 409"; exit 1; }
|
|
|
|
say "Warengruppen anlegen"
|
|
MOLK=$(api POST "/api/lists/$LIST/categories" '{"name":"Molkerei","sort_order":1}' | json_get id)
|
|
OBST=$(api POST "/api/lists/$LIST/categories" '{"name":"Obst und Gemüse","sort_order":2}' | json_get id)
|
|
|
|
say "Artikel mit Attributen anlegen"
|
|
api POST "/api/lists/$LIST/articles" "$(cat <<JSON
|
|
{"name":"Vollmilch","barcode":"4001234567890",
|
|
"default_market_id":"$EDEKA","default_category_id":"$MOLK",
|
|
"attributes":[{"name":"Verpackungseinheit","value":"1 Liter"},
|
|
{"name":"Fettgehalt","value":"3,5 %"}],
|
|
"available_market_ids":["$EDEKA"]}
|
|
JSON
|
|
)" | json_get name | sed 's/^/ Artikel angelegt: /'
|
|
|
|
say "Einträge anlegen (Artikel wird bei Bedarf erzeugt)"
|
|
for a in Butter Äpfel Bananen Zahnpasta; do
|
|
api POST "/api/lists/$LIST/items" "$(json_obj article_name "$a")" > /dev/null
|
|
done
|
|
MILCH=$(api POST "/api/lists/$LIST/items" \
|
|
'{"article_name":"Vollmilch","quantity":2,"unit":"l"}' | json_get id)
|
|
|
|
say "Zuordnungen setzen"
|
|
api PATCH "/api/lists/$LIST/items/$MILCH" \
|
|
"{\"market_id\":\"$EDEKA\",\"category_id\":\"$MOLK\",\"price_cents\":129}" \
|
|
| json_get article_name | sed 's/^/ Preis gesetzt für: /'
|
|
|
|
say "Barcode-Suche"
|
|
api GET "/api/lists/$LIST/articles/by-barcode/4001234567890" | json_get name | sed 's/^/ gefunden: /'
|
|
|
|
say "Gruppierte Ansicht"
|
|
api GET "/api/lists/$LIST/view" | python3 "$(dirname "$0")/render_view.py"
|
|
|
|
say "Abhaken und aufräumen"
|
|
api PATCH "/api/lists/$LIST/items/$MILCH" '{"status":"bought"}' | json_get status | sed 's/^/ neuer Status: /'
|
|
api POST "/api/lists/$LIST/items/clear-bought" | json_get removed | sed 's/^/ entfernte Einträge: /'
|
|
|
|
say "Fremde Liste darf nicht sichtbar sein (erwartet 404)"
|
|
code=$(curl -sS -o /dev/null -w '%{http_code}' -b "$JAR" \
|
|
"$BASE/api/lists/00000000-0000-0000-0000-000000000000")
|
|
[ "$code" = "404" ] && echo " ok: $code" || { echo " FEHLER: $code statt 404"; exit 1; }
|
|
|
|
say "Fertig"
|
|
echo "Testliste $LIST bleibt bestehen. Löschen mit:"
|
|
echo " curl -b <cookiejar> -X DELETE $BASE/api/lists/$LIST -H 'X-CSRF-Token: ...'"
|