"""Tests for the DMR / Digital Voice decoding module.""" from unittest.mock import patch, MagicMock import pytest from routes.dmr import parse_dsd_output # ============================================ # parse_dsd_output() tests # ============================================ def test_parse_sync_dmr(): """Should parse DMR sync line.""" result = parse_dsd_output('Sync: +DMR (data)') assert result is not None assert result['type'] == 'sync' assert 'DMR' in result['protocol'] def test_parse_sync_p25(): """Should parse P25 sync line.""" result = parse_dsd_output('Sync: +P25 Phase 1') assert result is not None assert result['type'] == 'sync' assert 'P25' in result['protocol'] def test_parse_talkgroup_and_source(): """Should parse talkgroup and source ID.""" result = parse_dsd_output('TG: 12345 Src: 67890') assert result is not None assert result['type'] == 'call' assert result['talkgroup'] == 12345 assert result['source_id'] == 67890 def test_parse_slot(): """Should parse slot info.""" result = parse_dsd_output('Slot 1') assert result is not None assert result['type'] == 'slot' assert result['slot'] == 1 def test_parse_voice(): """Should parse voice frame info.""" result = parse_dsd_output('Voice Frame 1') assert result is not None assert result['type'] == 'voice' def test_parse_nac(): """Should parse P25 NAC.""" result = parse_dsd_output('NAC: 293') assert result is not None assert result['type'] == 'nac' assert result['nac'] == '293' def test_parse_talkgroup_dsd_fme_format(): """Should parse dsd-fme comma-separated TG/Src format.""" result = parse_dsd_output('TG: 12345, Src: 67890') assert result is not None assert result['type'] == 'call' assert result['talkgroup'] == 12345 assert result['source_id'] == 67890 def test_parse_talkgroup_with_slot(): """TG line with slot info should capture both.""" result = parse_dsd_output('Slot 1 Voice LC, TG: 100, Src: 200') assert result is not None assert result['type'] == 'call' assert result['talkgroup'] == 100 assert result['source_id'] == 200 assert result['slot'] == 1 def test_parse_voice_with_slot(): """Voice frame with slot info should be voice, not slot.""" result = parse_dsd_output('Slot 2 Voice Frame') assert result is not None assert result['type'] == 'voice' assert result['slot'] == 2 def test_parse_empty_line(): """Empty lines should return None.""" assert parse_dsd_output('') is None assert parse_dsd_output(' ') is None def test_parse_unrecognized(): """Unrecognized lines should return raw event for diagnostics.""" result = parse_dsd_output('some random text') assert result is not None assert result['type'] == 'raw' assert result['text'] == 'some random text' # ============================================ # Endpoint tests # ============================================ @pytest.fixture def auth_client(client): """Client with logged-in session.""" with client.session_transaction() as sess: sess['logged_in'] = True return client def test_dmr_tools(auth_client): """Tools endpoint should return availability info.""" resp = auth_client.get('/dmr/tools') assert resp.status_code == 200 data = resp.get_json() assert 'dsd' in data assert 'rtl_fm' in data assert 'protocols' in data def test_dmr_status(auth_client): """Status endpoint should work.""" resp = auth_client.get('/dmr/status') assert resp.status_code == 200 data = resp.get_json() assert 'running' in data def test_dmr_start_no_dsd(auth_client): """Start should fail gracefully when dsd is not installed.""" with patch('routes.dmr.find_dsd', return_value=(None, False)): resp = auth_client.post('/dmr/start', json={ 'frequency': 462.5625, 'protocol': 'auto', }) assert resp.status_code == 503 data = resp.get_json() assert 'dsd' in data['message'] def test_dmr_start_no_rtl_fm(auth_client): """Start should fail when rtl_fm is missing.""" with patch('routes.dmr.find_dsd', return_value=('/usr/bin/dsd', False)), \ patch('routes.dmr.find_rtl_fm', return_value=None): resp = auth_client.post('/dmr/start', json={ 'frequency': 462.5625, }) assert resp.status_code == 503 def test_dmr_start_invalid_protocol(auth_client): """Start should reject invalid protocol.""" with patch('routes.dmr.find_dsd', return_value=('/usr/bin/dsd', False)), \ patch('routes.dmr.find_rtl_fm', return_value='/usr/bin/rtl_fm'): resp = auth_client.post('/dmr/start', json={ 'frequency': 462.5625, 'protocol': 'invalid', }) assert resp.status_code == 400 def test_dmr_stop(auth_client): """Stop should succeed.""" resp = auth_client.post('/dmr/stop') assert resp.status_code == 200 data = resp.get_json() assert data['status'] == 'stopped' def test_dmr_stream_mimetype(auth_client): """Stream should return event-stream content type.""" resp = auth_client.get('/dmr/stream') assert resp.content_type.startswith('text/event-stream')