#!/bin/bash # Test: voice-bridge.py # Validates the voice command bridge script. Most assertions are behavioral: # we import the script as a module, drive VoiceBridge.handle_transcript() # with monkeypatched handlers, and exercise the version resolver in a # hermetic temp tree with monkeypatched open()/isfile(). Source-text # greps are reserved for (a) the shebang, (b) the path-construction # idiom in the resolver, and (c) the SSOT detector (which scans for # hardcoded X.Y.Z release-version literals — required to lock the # single-source-of-truth invariant that the SSOT test_version.sh # also enforces). # # 20 assertions total. # # Note: some assertions reference variables (e.g. VOICE_BRIDGE) in # single-quoted `bash -c` subshells. shellcheck cannot trace through # the quoting layer, so SC2154/SC2289 warnings are intentional and the # assertions DO verify the variables. # shellcheck disable=SC2154,SC2289,SC1011,SC1078,SC1083 source "$(dirname "$0")/test_helper.sh" suite "voice-bridge.py tests" VOICE_BRIDGE="$SCRIPTS_DIR/voice-bridge.py" # Configurable temp dir for tests that need filesystem isolation. # CI environments with read-only /tmp can override via $SAMIOS_TEST_TMPDIR. # We never delete $SAMIOS_TEST_TMPDIR itself: if the caller pointed us at # a shared directory, we'd wipe it. Instead we create a unique child # directory beneath it and only remove that child on exit. SAMIOS_TEST_TMPDIR="${SAMIOS_TEST_TMPDIR:-/tmp}" if [ ! -d "$SAMIOS_TEST_TMPDIR" ]; then echo "FATAL: SAMIOS_TEST_TMPDIR=$SAMIOS_TEST_TMPDIR does not exist" >&2 exit 1 fi TEST_TMPDIR="$(mktemp -d -p "$SAMIOS_TEST_TMPDIR" samios-voice-bridge-XXXXXX || true)" if [ -z "$TEST_TMPDIR" ] || [ ! -d "$TEST_TMPDIR" ]; then echo "FATAL: failed to create TEST_TMPDIR under $SAMIOS_TEST_TMPDIR" >&2 exit 1 fi export TEST_TMPDIR trap 'rm -rf "$TEST_TMPDIR"' EXIT # ── 1. File presence + Python AST validity ──────────────────────────────── assert_file_exists "voice-bridge.py exists" "$VOICE_BRIDGE" assert "voice-bridge.py parses as valid Python (ast.parse)" \ bash -c ' python3 -c "import ast; ast.parse(open(\"'"$VOICE_BRIDGE"'\").read())" ' assert "voice-bridge.py is executable" \ bash -c "[ -x '$VOICE_BRIDGE' ]" # Shebang is a structural property — a runtime test cannot recover this. assert "voice-bridge.py starts with python shebang" \ bash -c 'head -1 "'"$VOICE_BRIDGE"'" | grep -q "#!/usr/bin/env python3"' # ── 2. --version output format (exact equality) ────────────────────────── assert "voice-bridge.py --version prints exact 'SamiOS voice-bridge v'" \ bash -c ' expected="SamiOS voice-bridge v$(head -n1 "'"$REPO_ROOT"'/VERSION")" actual="$(python3 "'"$VOICE_BRIDGE"'" --version)" if [ "$actual" != "$expected" ]; then echo "expected=$expected actual=$actual" exit 1 fi ' assert "voice-bridge.py -V short flag prints exact same banner" \ bash -c ' expected="SamiOS voice-bridge v$(head -n1 "'"$REPO_ROOT"'/VERSION")" actual="$(python3 "'"$VOICE_BRIDGE"'" -V)" if [ "$actual" != "$expected" ]; then echo "expected=$expected actual=$actual" exit 1 fi ' # ── 3. --version VALUE matches canonical VERSION file ───────────────────── assert "voice-bridge --version VALUE matches repo VERSION" \ bash -c ' expected="$(head -n1 "'"$REPO_ROOT"'/VERSION")" actual="$(python3 "'"$VOICE_BRIDGE"'" --version | sed -E "s/^SamiOS voice-bridge v//")" if [ "$actual" != "$expected" ]; then echo "expected=$expected actual=$actual" exit 1 fi ' # ── 4. SSOT: no hardcoded X.Y.Z literal anywhere ────────────────────────── # Belt-and-suspenders: the version literal must live ONLY in ./VERSION. assert "voice-bridge.py has no hardcoded release-version literal" \ bash -c ' version_pattern="(^|[^a-zA-Z0-9])(v?[0-9]+[.][0-9]+[.][0-9]+([-+][a-zA-Z0-9.-]+)*)([^a-zA-Z0-9]|$)" allowed_pattern="(^|[^a-zA-Z0-9])(0[.]0[.]0-unknown|127[.]0[.]0[.]1|127[.]0[.]1[.]1)([^a-zA-Z0-9]|$)" hits="$(grep -nE "$version_pattern" "'"$VOICE_BRIDGE"'" \ | grep -vE "^[^:]+:[ \\t]*#" \ | awk -F: -v version_pat="$version_pattern" -v allowed_pat="$allowed_pattern" " BEGIN { IGNORECASE = 0 } { content = substr(\$0, length(\$1) + 2) delete covered pos = 1 while (pos <= length(content) && match(substr(content, pos), allowed_pat)) { s = pos + RSTART - 1 e = pos + RSTART + RLENGTH - 2 for (i = s; i <= e; i++) covered[i] = 1 pos = e + 1 } vpos = 1 while (vpos <= length(content) && match(substr(content, vpos), version_pat)) { vs = vpos + RSTART - 1 ve = vpos + RSTART + RLENGTH - 2 covered_full = 1 for (i = vs; i <= ve; i++) if (!covered[i]) { covered_full = 0; break } if (!covered_full) { print \$0; break } vpos = ve + 1 } } ")" if [ -n "$hits" ]; then echo "HARD-CODED RELEASE VERSIONS in voice-bridge.py:" echo "$hits" exit 1 fi ' # ── 5. Walk-up behavior: resolver finds VERSION in a parent directory ───── # Behavioral test: copy voice-bridge.py into a temp tree nested several # directories deep, place a synthetic VERSION at the temp root, and verify # the resolver walks up and returns the synthetic value. assert "voice-bridge.py: walk-up resolver finds VERSION in parent directory" \ bash -c ' nested_dir="$TEST_TMPDIR/walkup/nested/deep" mkdir -p "$nested_dir" cp "'"$VOICE_BRIDGE"'" "$nested_dir/voice-bridge.py" echo "9.9.9-walkup" > "$TEST_TMPDIR/walkup/VERSION" cd "$nested_dir" && \ actual="$(python3 -c "import importlib.util; spec = importlib.util.spec_from_file_location(\"voice_bridge\", \"voice-bridge.py\"); m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m); print(m.VERSION)")" if [ "$actual" = "9.9.9-walkup" ]; then exit 0 fi echo "walk-up expected=9.9.9-walkup actual=$actual" exit 1 ' # ── 5b. Path-construction shape check ───────────────────────────────────── # The resolver MUST build a candidate path using either string concat # ("/VERSION") or os.path.join(... "VERSION"). This is a structural # check — the behavioral version is test 5. assert "voice-bridge.py: resolver constructs VERSION candidate path" \ bash -c ' grep -qE "(os\\.path\\.join\\([^)]*VERSION|\"/VERSION\"|os\\.path\\.join\\(d, .VERSION.\\))" "'"$VOICE_BRIDGE"'" ' # ── 6. Runtime: VERSION constant matches repo VERSION ───────────────────── assert "voice-bridge.py: VERSION constant resolves from repo root" \ bash -c ' cd "'"$REPO_ROOT"'" && \ expected="$(head -n1 VERSION)" && \ actual="$(cd "'"$(dirname "$VOICE_BRIDGE")"'" && python3 -c "import importlib.util; spec = importlib.util.spec_from_file_location(\"voice_bridge\", \"voice-bridge.py\"); m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m); print(m.VERSION)")" && \ [ "$actual" = "$expected" ] || { echo "expected=$expected actual=$actual"; exit 1; } ' # ── 7. /etc/samios-version fallback: BEHAVIORAL with call-flag ──────────── # Instrument the fake open() with a flag that proves it was called. Load # the module from a temp tree where no ancestor VERSION exists so the # walk-up falls through to /etc/samios-version. Assert both that the # fallback was reached AND that the synthesized value was returned. assert "voice-bridge.py: /etc/samios-version fallback returns synthesized value" \ bash -c ' isolated_dir="$TEST_TMPDIR/fallback/iso/nested" mkdir -p "$isolated_dir" || exit 1 cp "'"$VOICE_BRIDGE"'" "$isolated_dir/voice-bridge.py" cd "$isolated_dir" || exit 1 python3 << "PYEOF" import importlib.util, builtins, os spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) SYNTH = "9.9.9-etc-synth" etc_called = [False] real_open = builtins.open real_isfile = os.path.isfile class FakeEtcFile: def __enter__(self): return self def __exit__(self, *a): pass def read(self): return SYNTH + chr(10) def readline(self): return SYNTH + chr(10) def fake_open(path, *args, **kwargs): if isinstance(path, str) and path == "/etc/samios-version": etc_called[0] = True return FakeEtcFile() return real_open(path, *args, **kwargs) # Suppress ALL ancestor VERSION candidates so the resolver falls through # to /etc/samios-version. Without this, a VERSION file anywhere on the # walk-up path (including SAMIOS_TEST_TMPDIR=/) would short-circuit the # test. builtins.open = fake_open os.path.isfile = lambda p: False try: spec.loader.exec_module(m) finally: builtins.open = real_open os.path.isfile = real_isfile assert etc_called[0], "fake /etc open was never called -- fallback not reached" assert m.VERSION == SYNTH, f"fallback returned {m.VERSION!r}, expected {SYNTH!r}" print("OK", m.VERSION) PYEOF ' # ── 8. Placeholder fallback: hermetic, asserts exactly 0.0.0-unknown ────── # Suppress ALL candidate VERSION sources (walk-up + /etc) and assert the # resolver returns EXACTLY the documented placeholder. This is the strongest # fallback test — it isolates the failure mode. assert "voice-bridge.py: placeholder fallback returns exactly '0.0.0-unknown'" \ bash -c ' isolated_dir="$TEST_TMPDIR/placeholder/iso/nested" mkdir -p "$isolated_dir" cp "'"$VOICE_BRIDGE"'" "$isolated_dir/voice-bridge.py" cd "$isolated_dir" || exit 1 python3 << "PYEOF" import importlib.util, builtins, os, sys spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) def always_fail(path, *args, **kwargs): raise OSError("forced failure for hermetic test") real_open = builtins.open real_isfile = os.path.isfile builtins.open = always_fail os.path.isfile = lambda p: False try: spec.loader.exec_module(m) finally: builtins.open = real_open os.path.isfile = real_isfile assert m.VERSION == "0.0.0-unknown", f"placeholder fallback returned {m.VERSION!r}" print("OK", m.VERSION) PYEOF ' # ── 9. VoiceBridge class: behavioral smoke test ─────────────────────────── # Construct a VoiceBridge, exercise its public attributes and methods # (handle_transcript, _handle_dictation, _enter_dictation, _exit_dictation, # mode transitions) with monkeypatched xdotool/hermes — no source grep. assert "voice-bridge.py: VoiceBridge is constructible and starts in IDLE mode" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() assert bridge.mode == m.VoiceMode.IDLE, f"expected IDLE, got {bridge.mode}" assert bridge.running is False assert hasattr(bridge, "dictation_commands") assert "stop dictation" in bridge.dictation_commands assert "new paragraph" in bridge.dictation_commands print("OK") PYEOF ' assert "voice-bridge.py: 'start dictation' command enters DICTATION mode" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() assert bridge.mode == m.VoiceMode.IDLE bridge.handle_transcript({"text": "start dictation"}) assert bridge.mode == m.VoiceMode.DICTATION, f"expected DICTATION after wake, got {bridge.mode}" bridge.handle_transcript({"text": "stop dictation"}) assert bridge.mode == m.VoiceMode.IDLE, f"expected IDLE after stop, got {bridge.mode}" print("OK") PYEOF ' assert "voice-bridge.py: 'computer' wake word strips prefix from command" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() captured = [] bridge._handle_command = lambda cmd, conf: captured.append((cmd, conf)) bridge._send_to_hermes = lambda cmd: None bridge.handle_transcript({"text": "computer open firefox", "confidence": 1.0}) assert captured == [("open firefox", 1.0)], f"expected [(open firefox, 1.0)], got {captured}" print("OK") PYEOF ' assert "voice-bridge.py: 'computer' wake word is case-insensitive" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() captured = [] bridge._handle_command = lambda cmd, conf: captured.append((cmd, conf)) bridge._send_to_hermes = lambda cmd: None # Wake word is matched via text.lower().startswith("computer"), so # uppercase/mixed-case "COMPUTER" must also trigger. bridge.handle_transcript({"text": "COMPUTER shutdown", "confidence": 1.0}) assert captured == [("shutdown", 1.0)], f"expected [(shutdown, 1.0)], got {captured}" print("OK") PYEOF ' assert "voice-bridge.py: dictation mode routes known phrases to key actions" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() sent_keys = [] bridge._send_key = lambda k: sent_keys.append(k) bridge._enter_dictation() assert bridge.mode == m.VoiceMode.DICTATION bridge.handle_transcript({"text": "new paragraph", "confidence": 1.0}) bridge.handle_transcript({"text": "tab", "confidence": 1.0}) assert sent_keys == ["Return", "Tab"], f"expected [Return, Tab], got {sent_keys}" print("OK") PYEOF ' # ── 10. main() entry point is wired correctly ───────────────────────────── assert "voice-bridge.py: main() entry point exists and exits 0 on --version" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util, sys spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) assert callable(m.main) saved_argv = sys.argv try: sys.argv = ["voice-bridge.py", "--version"] try: m.main() sys.exit(2) except SystemExit as e: if e.code != 0: print(f"main() exited with {e.code}") sys.exit(1) print("OK") finally: sys.argv = saved_argv PYEOF ' # ── 11. dictation_commands dict is reachable and contains expected keys ─── assert "voice-bridge.py: dictation_commands dict has at least 10 entries" \ bash -c ' cd "'"$(dirname "$VOICE_BRIDGE")"'" || exit 1 python3 << "PYEOF" import importlib.util spec = importlib.util.spec_from_file_location("voice_bridge", "voice-bridge.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) bridge = m.VoiceBridge() n = len(bridge.dictation_commands) assert n >= 10, f"expected at least 10 dictation commands, got {n}" print("OK", n) PYEOF ' print_summary