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.
68 lines
4.9 KiB
Bash
Executable file
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
|