koopa-admin-log/scripts/taler-landing/test-landing-stats.sh
Hernâni Marques fb1a0b0b8e
landing: kGOA units, full money stats, rootless resource display
Public bank/exchange/merchant landing stats and performance UI only.
Secret goa-ui overlay stays on local/merchant-secret-ui (never push).
2026-07-17 18:13:13 +02:00

249 lines
8.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Outside-in checks for bank / exchange / merchant landing stats + alt UI assets.
# Usage: ./test-landing-stats.sh
# Exit 0 only if all critical checks pass.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
PY="${PYTHON:-python3}"
BANK="${BANK_PUBLIC:-https://bank.hacktivism.ch}"
EX="${EXCHANGE_PUBLIC:-https://exchange.hacktivism.ch}"
MER="${MERCHANT_PUBLIC:-https://taler.hacktivism.ch}"
fail=0
pass=0
note() { printf ' %s\n' "$*"; }
ok() { pass=$((pass + 1)); printf ' [OK] %s\n' "$*"; }
bad() { fail=$((fail + 1)); printf ' [FAIL] %s\n' "$*"; }
http_code() {
curl -skS -m 15 -o /tmp/landing-test.body -w '%{http_code}' "$1" 2>/dev/null || echo 000
}
echo "=== local repo checks ==="
for f in \
configs/shared/goa-amount.js \
configs/bank-landing/goa-amount.js \
configs/exchange-landing/goa-amount.js \
configs/merchant-landing/goa-amount.js \
configs/bank-landing/index.html \
configs/exchange-landing/index.html \
configs/merchant-landing/index.html \
scripts/taler-landing/goa_amounts.py \
scripts/taler-landing/collect_bank_stats.py
do
if [ -f "$ROOT/$f" ]; then ok "file $f"
else bad "missing $f"
fi
done
for html in bank-landing exchange-landing merchant-landing; do
if grep -q 'goa-amount.js' "$ROOT/configs/$html/index.html" \
&& grep -q 'GoaAmount' "$ROOT/configs/$html/index.html"; then
ok "$html index wires GoaAmount"
else
bad "$html index missing goa-amount / GoaAmount"
fi
if grep -q 'Fallback if goa-amount.js' "$ROOT/configs/$html/index.html"; then
ok "$html has offline fallback"
else
bad "$html missing offline GoaAmount fallback"
fi
done
echo
echo "=== unit: goa_amounts enrich (all three shapes) ==="
if "$PY" - <<PY
import sys
sys.path.insert(0, "$ROOT/scripts/taler-landing")
from goa_amounts import enrich_stats_tree, DEFAULT_ALT
bank = {
"ok": True,
"balance_explorer": "GOA:40614.67",
"withdraws": {"total_amount": "GOA:120944.66", "last_24h": {"amount": "GOA:9494.66"}},
"flow": {"withdraw": {"amount": "GOA:120944.66"}, "incoming": {"amount": "GOA:1000"}},
"recent_withdraws": [{"amount": "GOA:4503599627370495"}],
}
enrich_stats_tree(bank, DEFAULT_ALT)
assert "kGOA" in bank["withdraws"]["total_amount_alt"]
assert "PGOA" in bank["recent_withdraws"][0]["amount_alt"]
ex = {
"ok": True,
"wire_in_amount": "GOA:1267008731299764.85",
"withdraw_amount": "GOA:1783661.57",
"coins_remaining_amount": "GOA:0",
"by_denom": [{"value": "GOA:1000"}],
}
enrich_stats_tree(ex, DEFAULT_ALT)
assert "PGOA" in ex["wire_in_amount_alt"]
assert "MGOA" in ex["withdraw_amount_alt"]
assert ex["coins_remaining_amount_alt"] in ("GOA:0", "0 GOA")
mer = {
"ok": True,
"by_currency": [
{"currency": "GOA", "amount_sum": "GOA:294.05", "amount_paid_sum": "GOA:294.05"},
{"currency": "CHF", "amount_sum": "CHF:64000", "amount_paid_sum": "CHF:64000"},
],
"recent_activity_by_currency": [
{"currency": "GOA", "items": [{"amount": "GOA:2500"}]},
],
}
enrich_stats_tree(mer, DEFAULT_ALT)
# CHF must NOT become kGOA
assert mer["by_currency"][1]["amount_paid_sum_alt"].startswith("CHF")
assert "kGOA" in mer["recent_activity_by_currency"][0]["items"][0]["amount_alt"]
assert mer.get("alt_unit_legend")
print("unit ok")
PY
then ok "enrich shapes bank/exchange/merchant"
else bad "enrich unit failed"
fi
echo
echo "=== unit: merge_resources + mem labels ==="
if "$PY" - <<PY
import json, sys, tempfile, os
sys.path.insert(0, "$ROOT/scripts/taler-landing")
# import as script module path
import importlib.util
spec = importlib.util.spec_from_file_location("merge_resources", "$ROOT/scripts/taler-landing/merge_resources.py")
mr = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mr)
stats = {"ok": True, "performance": {"config_ms": 12, "memory": {"container_rss_human": "—"}}}
res = {
"ok": True,
"loadavg": "0.5,0.4,0.3",
"source": "mem-snapshot",
"memory": {
"container_rss_bytes": 726663168,
"container_rss_human": "693.4 MiB",
"cgroup_limit_bytes": 2147483648,
"cgroup_limit_human": "2.00 GiB",
"postgres_rss_human": "281.1 MiB",
"postgres_n": 17,
"java_rss_human": "353.7 MiB",
"java_n": 2,
"top": [{"rss_bytes": 367312896, "rss_human": "350.3 MiB", "comm": "java",
"cmd": "java -classpath /usr/lib/libeufin-bank-all.jar " + ("x" * 120)}],
},
}
mr.merge(stats, res)
mem = stats["performance"]["memory"]
assert mem["container_rss_label"].startswith("693.4 MiB")
assert "/ 2.00 GiB" in mem["container_rss_label"]
assert stats["performance"]["loadavg"] == "0.5,0.4,0.3"
assert len(mem["top"][0]["cmd"]) <= 75
assert mem["top"][0].get("cmd_full")
print("merge ok", mem["container_rss_label"])
PY
then ok "merge_resources compact labels"
else bad "merge_resources unit failed"
fi
# mem-snapshot syntax
if bash -n "$ROOT/scripts/taler-shared/mem-snapshot.sh" \
&& bash -n "$ROOT/scripts/taler-landing/collect_container_resources.sh"; then
ok "mem-snapshot + collect_container_resources bash -n"
else
bad "bash -n resources helpers"
fi
echo
echo "=== public HTTPS (live stack) ==="
for name_base in "bank|$BANK" "exchange|$EX" "merchant|$MER"; do
name="${name_base%%|*}"
base="${name_base#*|}"
echo "-- $name ($base) --"
code=$(http_code "$base/intro/")
if [ "$code" = "200" ]; then ok "$name /intro/ HTTP 200"
else bad "$name /intro/ HTTP $code"
fi
# live HTML may lag deploy — warn only if missing script after we expect it
if grep -q 'goa-amount.js' /tmp/landing-test.body 2>/dev/null; then
ok "$name live HTML references goa-amount.js"
else
note "[WARN] $name live HTML has no goa-amount.js yet (deploy pending)"
fi
code=$(http_code "$base/intro/stats.json")
if [ "$code" = "200" ] && "$PY" -c 'import json,sys; d=json.load(open("/tmp/landing-test.body")); sys.exit(0 if d.get("ok") else 1)'; then
gen=$("$PY" -c 'import json; d=json.load(open("/tmp/landing-test.body")); print(d.get("generated_at_human") or d.get("generated_at") or "?")')
ok "$name stats.json ok (gen=$gen)"
# enrich live payload
if "$PY" "$ROOT/scripts/taler-landing/enrich_stats_alt.py" /tmp/landing-test.body -o /tmp/landing-test.enriched.json >/dev/null 2>&1; then
ok "$name live stats enrichable with amount_alt"
else
bad "$name live stats enrich failed"
fi
# resources / memory block
if "$PY" - <<'PY'
import json, sys
d = json.load(open("/tmp/landing-test.body"))
p = d.get("performance") or {}
mem = p.get("memory") or {}
fail = []
if not p:
fail.append("no performance")
# latency probes differ per site but config_ms is common
if p.get("config_ms") is None and p.get("keys_ms") is None:
fail.append("no latency ms")
ctr = mem.get("container_rss_human") or ""
if not ctr or ctr in ("—", "-", "0"):
fail.append("container_rss_human missing")
if not isinstance(mem.get("top"), list) or len(mem.get("top") or []) < 1:
fail.append("top processes empty")
# role groups should exist as keys
for k in ("postgres_rss_human", "nginx_rss_human"):
if k not in mem:
fail.append("missing " + k)
if fail:
print("resources issues:", ", ".join(fail))
sys.exit(1)
print("resources ok container=", ctr, "top_n=", len(mem.get("top") or []),
"loadavg=", p.get("loadavg"))
sys.exit(0)
PY
then ok "$name performance.memory present (RSS + top)"
else bad "$name performance.memory incomplete"
fi
# merge dry-run on live stats (re-apply compact labels)
if "$PY" "$ROOT/scripts/taler-landing/merge_resources.py" /tmp/landing-test.body \
<("$PY" -c 'import json; d=json.load(open("/tmp/landing-test.body")); p=d.get("performance") or {}; print(json.dumps({"ok":True,"loadavg":p.get("loadavg") or "","memory":p.get("memory") or {}}))') \
-o /tmp/landing-test.resmerged.json 2>/dev/null; then
ok "$name resource merge on live payload"
else
# fallback without process substitution for macOS bash
"$PY" -c 'import json; d=json.load(open("/tmp/landing-test.body")); p=d.get("performance") or {}; json.dump({"ok":True,"loadavg":p.get("loadavg") or "","memory":p.get("memory") or {}}, open("/tmp/landing-test.res.json","w"))'
if "$PY" "$ROOT/scripts/taler-landing/merge_resources.py" /tmp/landing-test.body /tmp/landing-test.res.json -o /tmp/landing-test.resmerged.json 2>/dev/null; then
ok "$name resource merge on live payload"
else
bad "$name resource merge failed"
fi
fi
else
bad "$name stats.json HTTP $code / not ok"
fi
code=$(http_code "$base/intro/stats-run.json")
if [ "$code" = "200" ]; then ok "$name stats-run.json HTTP 200"
else bad "$name stats-run.json HTTP $code"
fi
code=$(http_code "$base/intro/goa-amount.js")
if [ "$code" = "200" ] && grep -q 'GoaAmount' /tmp/landing-test.body; then
ok "$name goa-amount.js live"
else
note "[WARN] $name goa-amount.js not live yet (HTTP $code) — deploy-landings.sh needed"
fi
done
echo
echo "=== result: pass=$pass fail=$fail ==="
if [ "$fail" -gt 0 ]; then
exit 1
fi
exit 0