Files
MeshChatX/tests/backend/test_voicemail_manager_extended.py
2026-01-05 11:47:35 -06:00

173 lines
5.5 KiB
Python

import os
import shutil
import tempfile
from unittest.mock import MagicMock, patch
import pytest
from meshchatx.src.backend.voicemail_manager import VoicemailManager
@pytest.fixture
def temp_dir():
dir_path = tempfile.mkdtemp()
yield dir_path
shutil.rmtree(dir_path)
@pytest.fixture
def mock_deps():
with (
patch("meshchatx.src.backend.voicemail_manager.shutil.which") as mock_which,
patch("meshchatx.src.backend.voicemail_manager.subprocess.run") as mock_run,
patch("meshchatx.src.backend.voicemail_manager.Pipeline") as mock_pipeline,
patch("meshchatx.src.backend.voicemail_manager.OpusFileSink") as mock_sink,
patch("meshchatx.src.backend.voicemail_manager.OpusFileSource") as mock_source,
patch("RNS.log"),
):
# Mock finding espeak and ffmpeg
mock_which.side_effect = lambda x: f"/usr/bin/{x}"
yield {
"which": mock_which,
"run": mock_run,
"Pipeline": mock_pipeline,
"OpusFileSink": mock_sink,
"OpusFileSource": mock_source,
}
def test_voicemail_manager_init(mock_deps, temp_dir):
mock_db = MagicMock()
mock_config = MagicMock()
mock_tel = MagicMock()
vm = VoicemailManager(mock_db, mock_config, mock_tel, temp_dir)
assert vm.storage_dir == os.path.join(temp_dir, "voicemails")
assert vm.has_espeak is True
assert vm.has_ffmpeg is True
assert os.path.exists(vm.greetings_dir)
assert os.path.exists(vm.recordings_dir)
def test_generate_greeting(mock_deps, temp_dir):
mock_db = MagicMock()
mock_config = MagicMock()
mock_tel = MagicMock()
# Setup config mocks
mock_config.voicemail_tts_speed.get.return_value = 175
mock_config.voicemail_tts_pitch.get.return_value = 50
mock_config.voicemail_tts_voice.get.return_value = "en"
mock_config.voicemail_tts_word_gap.get.return_value = 10
vm = VoicemailManager(mock_db, mock_config, mock_tel, temp_dir)
with patch("os.path.exists", return_value=True), patch("os.remove"):
vm.generate_greeting("Hello world")
# Should have run espeak and ffmpeg
assert mock_deps["run"].call_count == 2
def test_start_recording_currently_disabled(mock_deps, temp_dir):
mock_db = MagicMock()
mock_config = MagicMock()
mock_tel_manager = MagicMock()
mock_tel = MagicMock()
mock_tel_manager.telephone = mock_tel
vm = VoicemailManager(mock_db, mock_config, mock_tel_manager, temp_dir)
mock_caller_identity = MagicMock()
# Use actual bytes for hash, which has a .hex() method
mock_caller_identity.hash = b"remote_hash_32_bytes_long_012345"
# Recording requires telephone.active_call to exist
# Without it, recording won't start
mock_tel.active_call = None
vm.start_recording(mock_caller_identity)
# Without active_call, recording should not start
assert vm.is_recording is False
def test_stop_recording(mock_deps, temp_dir):
mock_db = MagicMock()
mock_config = MagicMock()
mock_tel = MagicMock()
vm = VoicemailManager(mock_db, mock_config, mock_tel, temp_dir)
vm.is_recording = True
mock_pipeline_inst = MagicMock()
vm.recording_pipeline = mock_pipeline_inst
mock_sink = MagicMock()
vm.recording_sink = mock_sink
vm.recording_filename = "test.opus"
mock_remote_id = MagicMock()
# Use a mock for hash so we can mock its hex() method
mock_hash = MagicMock()
mock_hash.hex.return_value = "72656d6f7465"
mock_remote_id.hash = mock_hash
vm.recording_remote_identity = mock_remote_id
vm.recording_start_time = 100
vm.get_name_for_identity_hash = MagicMock(return_value="Test User")
# Create the recording file so add_voicemail gets called
recording_path = os.path.join(vm.recordings_dir, "test.opus")
os.makedirs(vm.recordings_dir, exist_ok=True)
with open(recording_path, "wb") as f:
f.write(b"fake opus data")
with (
patch("time.time", return_value=110),
patch.object(vm, "_fix_recording"),
):
vm.stop_recording()
assert vm.is_recording is False
mock_pipeline_inst.stop.assert_called()
mock_sink.stop.assert_called()
mock_db.voicemails.add_voicemail.assert_called()
def test_start_voicemail_session(mock_deps, temp_dir):
mock_db = MagicMock()
mock_config = MagicMock()
mock_tel_manager = MagicMock()
mock_tel = MagicMock()
mock_tel_manager.telephone = mock_tel
vm = VoicemailManager(mock_db, mock_config, mock_tel_manager, temp_dir)
mock_caller = MagicMock()
mock_caller.hash = b"caller"
# Set call_status to RINGING (4) so answer() gets called
mock_tel.call_status = 4
mock_tel.answer.return_value = True
mock_tel.audio_input = MagicMock()
# Mocking threading.Thread to run the job synchronously for testing
with patch("threading.Thread") as mock_thread, patch("time.sleep"):
vm.start_voicemail_session(mock_caller)
# Verify answer was called
mock_tel.answer.assert_called_with(mock_caller)
# Verify mic was stopped
mock_tel.audio_input.stop.assert_called()
# Get the job function and run it
job_func = mock_thread.call_args[1]["target"]
# We need to setup more mocks for the job to run without crashing
mock_tel.active_call = MagicMock()
mock_tel.active_call.audio_source = MagicMock()
with patch.object(vm, "start_recording") as mock_start_rec:
# Run the job
job_func()
mock_start_rec.assert_called()