#!/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 check() { if printf '%s' "$2" | grep -qF -- "$3"; then echo " ok $1" else echo " FAIL $1 (expected to find: $3)" fail=1 fi } # refute refute() { if printf '%s' "$2" | grep -qF -- "$3"; then echo " FAIL $1 (should not contain: $3)" fail=1 else echo " ok $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 "favicon is served" \ "$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" "http://$addr/static/favicon.svg")" "200" check "theme script is served" \ "$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" "http://$addr/static/theme.js")" "200" home=$(curl -s -u ":$pass" "http://$addr/") check "the header carries the brand" "$home" "Foodster" check "dark is the default without JavaScript" "$home" '' check "the theme toggle is present" "$home" "data-theme-toggle" check "both theme icons ship so CSS can pick one" "$home" 'class="i-moon"' check "apple touch icon is served" \ "$(curl -s -o /dev/null -w '%{http_code}' -u ":$pass" "http://$addr/static/apple-touch-icon.png")" "200" # A manifest served as octet-stream is silently ignored by the browser. check "manifest has the right content type" \ "$(curl -s -o /dev/null -w '%{content_type}' -u ":$pass" "http://$addr/static/manifest.webmanifest")" \ "application/manifest+json" check "catalog starts empty" \ "$(curl -s -u ":$pass" "http://$addr/ruuat")" "0 pääruokaa" out=$(curl -s -u ":$pass" -F "tiedosto=@seeds/testi.json" "http://$addr/ruuat/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/ruuat/tuonti")" \ "jo listalla" check "pasted JSON imports" \ "$(curl -s -u ":$pass" -F 'json={"mains":[],"sides":[{"name":"Perunasalaatti"}]}' \ "http://$addr/ruuat/tuonti")" "Lisätty 1" check "unknown category is reported" \ "$(curl -s -u ":$pass" -F 'json={"mains":[{"name":"Rikki","categories":["kana"]}],"sides":[]}' \ "http://$addr/ruuat/tuonti")" "tuntematon kategoria" check "empty submit is explained" \ "$(curl -s -u ":$pass" -F 'json=' "http://$addr/ruuat/tuonti")" "Ei tuotavaa" check "malformed JSON is explained" \ "$(curl -s -u ":$pass" -F 'json={nope' "http://$addr/ruuat/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" # Tähteet is loggable but is not food: on the board, never in the catalog. check "leftovers are on the board" "$board" "Tähteet" refute "leftovers are not in the catalog" \ "$(curl -s -u ":$pass" "http://$addr/ruuat")" "Tähteet" # 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" # The selected day expands inside the list rather than in a panel above it, # so the rows below do not shift when one is tapped. day=$(curl -s -u ":$pass" "http://$addr/?pvm=2026-09-05") check "the selected day expands in place" "$day" 'class="open"' check "and stays in the list rather than being lifted out" "$day" "kirjattu" # ---- the day list patches in place instead of navigating ---------------- dayp=$(curl -s -u ":$pass" -H 'Datastar-Request: true' "http://$addr/paiva?pvm=2026-09-05") check "opening a day patches the list" "$dayp" 'id="paivat"' refute "and returns a fragment, not a page" "$dayp" "