From abafba0762fafe0136552da012711173ce87a5d1 Mon Sep 17 00:00:00 2001 From: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:24:29 +0530 Subject: [PATCH] refactor(signal): correct STT-fallback comment, type the markdown wrapper, make AAC test portable Review follow-up on the salvaged AAC + markdown changes: - Fix an inaccurate comment claiming the STT layer has a sniff-and-remux fallback (verified: no such fallback exists; the ffmpeg-absent path caches raw ADTS and STT may reject it). - Type the _markdown_to_signal wrapper as tuple[str, list[str]] to match the shared helper instead of a bare tuple. - Replace the hardcoded /home/pi/... test fixture with a runtime-generated ADTS AAC sample so the remux round-trip actually runs in CI (skips only when ffmpeg is absent) instead of always-skipping. --- gateway/platforms/signal.py | 5 +++-- tests/gateway/test_signal.py | 43 ++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/gateway/platforms/signal.py b/gateway/platforms/signal.py index df9d07b4f71..7b81b2a957a 100644 --- a/gateway/platforms/signal.py +++ b/gateway/platforms/signal.py @@ -801,7 +801,8 @@ class SignalAdapter(BasePlatformAdapter): # require AAC to be muxed into an MP4 container. Remux losslessly # with ``ffmpeg -c:a copy`` so the cached file is a normal .m4a. # No re-encode, sub-100ms on a Pi 5. Graceful no-op if ffmpeg is - # absent; the STT layer has its own sniff-and-remux fallback. + # absent: the raw ADTS file is cached as-is and STT may reject it + # (there is no downstream sniff-and-remux fallback). if ext == ".aac": remuxed: Optional[Tuple[bytes, str]] = await asyncio.to_thread(_remux_aac_to_m4a, raw_data) if remuxed is not None: @@ -904,7 +905,7 @@ class SignalAdapter(BasePlatformAdapter): # ------------------------------------------------------------------ @staticmethod - def _markdown_to_signal(text: str) -> tuple: + def _markdown_to_signal(text: str) -> tuple[str, list[str]]: """Backward-compatible wrapper around shared Signal formatting helper.""" return markdown_to_signal(text) diff --git a/tests/gateway/test_signal.py b/tests/gateway/test_signal.py index b55c4215ecb..e79ee7a8591 100644 --- a/tests/gateway/test_signal.py +++ b/tests/gateway/test_signal.py @@ -201,20 +201,45 @@ class TestSignalHelpers: assert _is_audio_ext(ext) is True def test_remux_aac_to_m4a_round_trip(self): - """Real ADTS file from the audio cache remuxes to a valid MP4 container. + """A real ADTS AAC stream remuxes to a valid MP4 (.m4a) container. - Round-trips the actual Android voice note that triggered the - bug report — proves the end-to-end fix. + Generates a short ADTS AAC sample with ffmpeg at runtime so the + end-to-end remux path actually exercises in CI (skipped only when + ffmpeg is unavailable), rather than depending on a machine-specific + file. """ - import os import shutil + import subprocess + import tempfile from gateway.platforms.signal import _remux_aac_to_m4a - src = "/home/pi/.hermes/audio_cache/audio_fcfc38390b47.mp3" - if not os.path.exists(src) or not shutil.which("ffmpeg"): + + ffmpeg = shutil.which("ffmpeg") + if not ffmpeg: import pytest - pytest.skip("ffmpeg or source file not available in this env") - with open(src, "rb") as f: - aac_data = f.read() + pytest.skip("ffmpeg not available in this env") + + # Synthesize 0.5s of silence encoded as raw ADTS AAC. + with tempfile.NamedTemporaryFile(suffix=".aac", delete=False) as tmp: + adts_path = tmp.name + try: + gen = subprocess.run( + [ffmpeg, "-y", "-loglevel", "error", "-f", "lavfi", + "-i", "anullsrc=r=44100:cl=mono", "-t", "0.5", + "-c:a", "aac", "-f", "adts", adts_path], + capture_output=True, timeout=30, + ) + if gen.returncode != 0: + import pytest + pytest.skip("ffmpeg could not produce an ADTS AAC sample") + with open(adts_path, "rb") as f: + aac_data = f.read() + finally: + try: + import os + os.unlink(adts_path) + except OSError: + pass + result = _remux_aac_to_m4a(aac_data) assert result is not None m4a_bytes, ext = result