Prd/components/dialer-patch/tests/DeviceAuthSignerTest.java
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

41 lines
2.3 KiB
Java

package com.android.dialer.helpdesk.http;
import java.util.Map;
/** Standalone off-device test for {@link DeviceAuthSigner} (plain JDK, no Android, no JUnit). */
public class DeviceAuthSignerTest {
static int pass = 0, fail = 0;
static void ok(String d) { pass++; System.out.println(" ok " + d); }
static void bad(String d, Object got) { fail++; System.out.println(" FAIL " + d + " (got: " + got + ")"); }
static void eq(Object a, Object b, String d) { if (a == null ? b == null : a.equals(b)) ok(d); else bad(d, a); }
static void truthy(boolean x, String d) { if (x) ok(d); else bad(d, x); }
public static void main(String[] args) {
DeviceAuthSigner s = new DeviceAuthSigner("dev-device-token", "dev-device-secret");
String body = "{\"call_id\":\"c1\"}";
long ts = 1751932800L;
String nonce = "n0nce123";
Map<String, String> h = s.headers(body, ts, nonce);
eq(h.get("Authorization"), "Bearer dev-device-token", "Authorization: Bearer <token>");
eq(h.get("X-Request-Ts"), "1751932800", "X-Request-Ts is unix seconds");
eq(h.get("X-Request-Nonce"), "n0nce123", "X-Request-Nonce echoed");
String sig = h.get("X-Signature");
truthy(sig != null && sig.matches("[0-9a-f]{64}"), "X-Signature is 64 lowercase hex");
truthy(s.headers((String) null, ts, nonce).get("X-Signature") != null, "null body signs as empty (server sees \"\")");
truthy(!s.newNonce().equals(s.newNonce()), "newNonce() is random");
truthy(s.newNonce().matches("[0-9a-f]{32}"), "newNonce() is 128-bit hex");
// String and byte[] overloads agree for an ASCII body
String sigBytes = s.headers(body.getBytes(java.nio.charset.StandardCharsets.UTF_8), ts, nonce).get("X-Signature");
eq(sigBytes, sig, "byte[] overload == String overload for ASCII body");
// UTF-8 Czech body via the byte[] path (ě = ě) — emitted for the harness's Ruby cross-check
String cz = "{\"notes\":\"Děkujeme\"}";
String sigCz = s.headers(cz.getBytes(java.nio.charset.StandardCharsets.UTF_8), ts, nonce).get("X-Signature");
System.out.println("SIG=" + sig); // ASCII body cross-check
System.out.println("SIG_CZ=" + sigCz); // UTF-8 body cross-check
System.out.println("\n" + pass + " passed, " + fail + " failed");
System.exit(fail == 0 ? 0 : 1);
}
}