Prd/components/dialer-patch/tests/run.sh
Lucy Doupalů be9f14ce34 Helpdesk - operator console + patched GrapheneOS Dialer for call handling
A small helpdesk system: an office Pixel running a patched GrapheneOS Dialer
answers technician calls, records both call legs as separate channels, and a
Ruby backend transcribes them through Whisper and files an AI summary against
the caller.

Squashed to a single commit for sharing. No credentials are included; secrets
live outside the repo in /etc/helpdesk/env on the server or a gitignored
.claude/env.local locally. See .claude/env.local.example for the shape.

Start at README.md, then docs/architecture.md.
2026-07-27 18:50:32 +02:00

68 lines
4.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Off-device test runner for the helpdesk Dialer patch overlay.
# Compiles every overlay + test source with the tree's prebuilt JDK (the one the OS build uses),
# runs each *Test, and cross-checks the pure-logic ports against the REAL backend code.
# Run: bash components/dialer-patch/tests/run.sh
set -uo pipefail
cd "$(dirname "$0")/.." # -> components/dialer-patch
BACKEND=../backend/lib/helpdesk/domain
JAVAC=$(ls /build/grapheneos/prebuilts/jdk/jdk*/linux-x86/bin/javac 2>/dev/null | head -1); [ -x "$JAVAC" ] || JAVAC=$(command -v javac)
JAVA=$(ls /build/grapheneos/prebuilts/jdk/jdk*/linux-x86/bin/java 2>/dev/null | head -1); [ -x "$JAVA" ] || JAVA=$(command -v java)
[ -x "$JAVAC" ] || { echo "no javac found"; exit 1; }
OUT=$(mktemp -d); fail=0
# Compile only the Android-independent subpackages (http/util/command/queue) + tests. The device-
# coupled classes in the helpdesk/ root (Config/Registry/Dispatcher/Emitter — import android.*/incallui/
# org.json) are verified by `m Dialer`, not off-device.
H=overlay/java/com/android/dialer/helpdesk
mapfile -t SRCS < <(find "$H/http" "$H/util" "$H/command" "$H/queue" tests -name '*.java')
[ ${#SRCS[@]} -gt 0 ] || { echo "no sources found"; exit 1; }
"$JAVAC" -encoding UTF-8 -d "$OUT" "${SRCS[@]}" || { echo "COMPILE FAILED"; exit 1; }
echo "compiled ${#SRCS[@]} sources with $("$JAVAC" -version 2>&1)"
echo; echo "== unit suites =="
for f in $(find tests -name '*Test.java'); do
pkg=$(grep -m1 '^package ' "$f" | sed 's/package //; s/;//')
fqcn="$pkg.$(basename "$f" .java)"
echo "- $fqcn"
"$JAVA" -cp "$OUT" "$fqcn" || fail=1
done
echo; echo "== cross-checks vs real backend =="
# 1) auth signer signatures (ASCII + UTF-8 Czech body) == server.rb HMAC recipe
SOUT=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.http.DeviceAuthSignerTest)
JSIG=$(echo "$SOUT" | sed -n 's/^SIG=//p'); JSIGCZ=$(echo "$SOUT" | sed -n 's/^SIG_CZ=//p')
RSIG=$(ruby -e 'require "openssl"; b="{\"call_id\":\"c1\"}"; puts OpenSSL::HMAC.hexdigest("SHA256","dev-device-secret","1751932800.n0nce123.#{b}")')
RSIGCZ=$(ruby -e 'require "openssl"; b="{\"notes\":\"Děkujeme\"}"; puts OpenSSL::HMAC.hexdigest("SHA256","dev-device-secret","1751932800.n0nce123.#{b}")')
if [ -n "$JSIG" ] && [ "$JSIG" = "$RSIG" ]; then echo " ok DeviceAuthSigner (ASCII) == server.rb verifier"; else echo " FAIL signer ascii ($JSIG vs $RSIG)"; fail=1; fi
if [ -n "$JSIGCZ" ] && [ "$JSIGCZ" = "$RSIGCZ" ]; then echo " ok DeviceAuthSigner (UTF-8 Czech body, byte[] path) == server.rb"; else echo " FAIL signer utf8 ($JSIGCZ vs $RSIGCZ)"; fail=1; fi
# 2) E.164 normalizer == Helpdesk::Phone.e164 over a vector set
VEC=$(printf '%s\n' "601 234 567" "00420601234567" "+420601234567" "+420 601-234-567" "(602) 000 000" "hidden" "" "12345" "+441632960000" "777888999" "0042060123" " 606 606 606 ")
JX=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.util.CzE164Test xcheck <<<"$VEC")
RX=$(ruby -r"$BACKEND" -e 'STDIN.each_line{|l| v=Helpdesk::Phone.e164(l.chomp); puts(v.nil? ? "<nil>" : v)}' <<<"$VEC")
if [ "$JX" = "$RX" ]; then echo " ok CzE164 == Helpdesk::Phone.e164 over $(wc -l <<<"$VEC" | tr -d ' ') vectors";
else echo " FAIL E.164 cross-check"; diff <(echo "$JX") <(echo "$RX"); fail=1; fi
# 3) business-hours reflex == Helpdesk::BusinessHours over a full-year sweep (all weekends + holidays)
BH=../backend/lib/helpdesk/business_hours
SWEEP=$(for d in $(seq 0 364); do for hh in 07 08 10 15 16; do date -d "2026-01-01 +$d days" +%Y-%m-%dT$hh:00 2>/dev/null; done; done)
JBH=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.util.BusinessHoursTest xcheck <<<"$SWEEP")
RBH=$(ruby -r"$BH" -e 'STDIN.each_line{|l| l=l.chomp; next if l.empty?; y,mo,dy,h=l.scan(/\d+/).first(4).map(&:to_i); t=Time.new(y,mo,dy,h,0,0); puts(Helpdesk::BusinessHours.open?(t) ? "open" : "closed")}' <<<"$SWEEP")
if [ "$JBH" = "$RBH" ]; then echo " ok BusinessHours == Helpdesk::BusinessHours over $(wc -l <<<"$SWEEP" | tr -d ' ') datetimes";
else echo " FAIL business-hours cross-check"; diff <(echo "$JBH") <(echo "$RBH") | head; fail=1; fi
# 4) reverse-command verb set + DTMF rule == backend Helpdesk::COMMAND_VERBS / valid_dtmf?
JV=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.command.CommandGateTest verbs)
RV=$(ruby -r"$BACKEND" -e 'puts Helpdesk::COMMAND_VERBS.sort')
if [ "$JV" = "$RV" ]; then echo " ok CommandGate.VERBS == Helpdesk::COMMAND_VERBS";
else echo " FAIL command verbs"; diff <(echo "$JV") <(echo "$RV"); fail=1; fi
DARGS=$(printf '%s\n' 0 5 9 '*' '#' 'A' '55' '' ' ' 'x' '12')
JD=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.command.CommandGateTest dtmf <<<"$DARGS")
RD=$(ruby -e 'STDIN.each_line{|l| a=l.chomp; puts(a.match?(/\A[0-9*#]\z/) ? "valid":"invalid")}' <<<"$DARGS")
if [ "$JD" = "$RD" ]; then echo " ok isValidDtmf == valid_dtmf? over $(wc -l <<<"$DARGS" | tr -d ' ') args";
else echo " FAIL dtmf rule"; diff <(echo "$JD") <(echo "$RD"); fail=1; fi
echo; [ "$fail" = 0 ] && echo "ALL GREEN" || echo "FAILURES ABOVE"
exit $fail