Stage 1's point is collecting eating history, so the logging path is the one that has to be frictionless: pick a dish, tick sides, done. Kirjaa: - dishes ordered and sized by how often they have been eaten, so the likely answer is the biggest target on the screen - picking a dish opens the sides step; a dish with has_sides false says so instead of offering an empty list - saving redirects, so a refresh cannot double-post - a day already logged shows the entry with edit and delete. Editing reopens the sides step with the existing sides ticked, which makes editing and creating the same screen - day switcher and a server-side search over the catalog Historia walks back day by day to the oldest entry, so a day nobody wrote down appears as an explicit gap rather than quietly missing. No JavaScript is involved: every interaction is a link or a form, and the checked-chip styling is :has(input:checked). Datastar stays loaded but unused until an interaction genuinely needs to avoid a page load. Also fix a Makefile ordering bug: lint did not depend on generate, so `go vet` could run against templ output that was being rewritten. check now runs its phases as sub-makes so `make -j` cannot interleave them. Records two features that are specified but not built: dish CRUD in the UI (§7.3, only import exists today) and a per-device light/dark switch (§7.4). The switch must show the state that is active, not the one clicking produces.
123 lines
3.7 KiB
Bash
Executable File
123 lines
3.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# End-to-end check of a running Foodster: auth, static assets and the bundle
|
|
# import flow. Builds its own binary, uses a scratch database and a spare
|
|
# port, and cleans up after itself, so it never touches a real instance.
|
|
#
|
|
# Run it with `make smoke`.
|
|
|
|
set -eu
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
addr=127.0.0.1:8099
|
|
pass=smoke
|
|
tmp=$(mktemp -d)
|
|
trap 'kill ${srv:-0} 2>/dev/null || true; rm -rf "$tmp"' EXIT
|
|
|
|
go build -o "$tmp/foodster" ./cmd/foodster
|
|
|
|
FOODSTER_PASSWORD="$pass" FOODSTER_DB="$tmp/smoke.db" FOODSTER_ADDR="$addr" \
|
|
"$tmp/foodster" >"$tmp/server.log" 2>&1 &
|
|
srv=$!
|
|
|
|
i=0
|
|
while ! curl -sf "http://$addr/healthz" >/dev/null 2>&1; do
|
|
i=$((i + 1))
|
|
if [ "$i" -gt 50 ]; then
|
|
echo "server did not start:"
|
|
cat "$tmp/server.log"
|
|
exit 1
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
|
|
fail=0
|
|
|
|
# check <name> <haystack> <needle>
|
|
check() {
|
|
if printf '%s' "$2" | grep -qF -- "$3"; then
|
|
echo " ok $1"
|
|
else
|
|
echo " FAIL $1 (expected to find: $3)"
|
|
fail=1
|
|
fi
|
|
}
|
|
|
|
echo "smoke: http://$addr"
|
|
|
|
check "unauthenticated request is refused" \
|
|
"$(curl -s -o /dev/null -w '%{http_code}' "http://$addr/")" "401"
|
|
|
|
check "healthz needs no password" \
|
|
"$(curl -s -o /dev/null -w '%{http_code}' "http://$addr/healthz")" "200"
|
|
|
|
check "datastar client is served" \
|
|
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" "http://$addr/static/datastar.js")" "200"
|
|
|
|
check "catalog starts empty" \
|
|
"$(curl -s -u ":$pass" "http://$addr/ruoat")" "0 pääruokaa"
|
|
|
|
out=$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruoat/tuonti")
|
|
check "file upload imports the seed bundle" "$out" "Lisätty 22, ohitettu 0"
|
|
check "counts update after import" "$out" "16 pääruokaa, 6 lisuketta"
|
|
|
|
check "re-import refuses duplicates" \
|
|
"$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruoat/tuonti")" \
|
|
"jo listalla"
|
|
|
|
check "pasted JSON imports" \
|
|
"$(curl -s -u ":$pass" -F 'json={"mains":[],"sides":[{"name":"Perunasalaatti"}]}' \
|
|
"http://$addr/ruoat/tuonti")" "Lisätty 1"
|
|
|
|
check "unknown category is reported" \
|
|
"$(curl -s -u ":$pass" -F 'json={"mains":[{"name":"Rikki","categories":["kana"]}],"sides":[]}' \
|
|
"http://$addr/ruoat/tuonti")" "tuntematon kategoria"
|
|
|
|
check "empty submit is explained" \
|
|
"$(curl -s -u ":$pass" -F 'json=' "http://$addr/ruoat/tuonti")" "Ei tuotavaa"
|
|
|
|
check "malformed JSON is explained" \
|
|
"$(curl -s -u ":$pass" -F 'json={nope' "http://$addr/ruoat/tuonti")" "JSON ei kelpaa"
|
|
|
|
# ---- the log flow, against the dishes imported above --------------------
|
|
|
|
board=$(curl -s -u ":$pass" "http://$addr/")
|
|
check "board lists imported dishes" "$board" "Lihapullat"
|
|
|
|
# Pull a real dish id out of the board rather than assuming one.
|
|
ruoka=$(printf '%s' "$board" | grep -o 'ruoka=[0-9]*' | head -n1 | cut -d= -f2)
|
|
if [ -z "$ruoka" ]; then
|
|
echo " FAIL could not find a dish link on the board"
|
|
fail=1
|
|
ruoka=1
|
|
fi
|
|
|
|
check "picking a dish opens the sides step" \
|
|
"$(curl -s -u ":$pass" "http://$addr/?ruoka=$ruoka")" "Tallenna"
|
|
|
|
check "saving redirects back to the day" \
|
|
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
|
-d "pvm=2026-09-05&ruoka=$ruoka" "http://$addr/kirjaa")" "303"
|
|
|
|
check "the saved day shows what was eaten" \
|
|
"$(curl -s -u ":$pass" "http://$addr/?pvm=2026-09-05")" "kirjattu"
|
|
|
|
check "history lists the entry" \
|
|
"$(curl -s -u ":$pass" "http://$addr/historia")" "syyskuu"
|
|
|
|
check "deleting redirects back" \
|
|
"$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" \
|
|
-d "pvm=2026-09-05" "http://$addr/poista")" "303"
|
|
|
|
check "the day is empty again" \
|
|
"$(curl -s -u ":$pass" "http://$addr/?pvm=2026-09-05")" "Etsi"
|
|
|
|
check "search filters the board" \
|
|
"$(curl -s -u ":$pass" "http://$addr/?haku=keitto")" "keitto"
|
|
|
|
if [ "$fail" -ne 0 ]; then
|
|
echo "smoke: FAILED"
|
|
exit 1
|
|
fi
|
|
echo "smoke: all passed"
|