mirror of
https://github.com/smittix/intercept.git
synced 2026-07-06 00:28:12 -07:00
Remove GSM spy functionality for legal compliance
Remove all GSM cellular intelligence features including tower scanning, signal monitoring, rogue detection, crowd density analysis, and OpenCellID integration across routes, templates, utils, tests, and build configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,360 +0,0 @@
|
||||
"""Unit tests for GSM Spy parsing and validation functions."""
|
||||
|
||||
import pytest
|
||||
from routes.gsm_spy import (
|
||||
parse_grgsm_scanner_output,
|
||||
parse_tshark_output,
|
||||
arfcn_to_frequency,
|
||||
validate_band_names,
|
||||
REGIONAL_BANDS
|
||||
)
|
||||
|
||||
|
||||
class TestParseGrgsmScannerOutput:
|
||||
"""Tests for parse_grgsm_scanner_output()."""
|
||||
|
||||
def test_valid_output_line(self):
|
||||
"""Test parsing a valid grgsm_scanner output line."""
|
||||
line = "ARFCN: 23, Freq: 940.6M, CID: 31245, LAC: 1234, MCC: 214, MNC: 01, Pwr: -48"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
|
||||
assert result is not None
|
||||
assert result['type'] == 'tower'
|
||||
assert result['arfcn'] == 23
|
||||
assert result['frequency'] == 940.6
|
||||
assert result['cid'] == 31245
|
||||
assert result['lac'] == 1234
|
||||
assert result['mcc'] == 214
|
||||
assert result['mnc'] == 1
|
||||
assert result['signal_strength'] == -48.0
|
||||
assert 'timestamp' in result
|
||||
|
||||
def test_freq_without_suffix(self):
|
||||
"""Test parsing frequency without M suffix."""
|
||||
line = "ARFCN: 975, Freq: 925.2, CID: 13522, LAC: 38722, MCC: 262, MNC: 1, Pwr: -58"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is not None
|
||||
assert result['frequency'] == 925.2
|
||||
|
||||
def test_config_line(self):
|
||||
"""Test that configuration lines are skipped."""
|
||||
line = " Configuration: 1 CCCH, not combined"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_neighbour_line(self):
|
||||
"""Test that neighbour cell lines are skipped."""
|
||||
line = " Neighbour Cells: 57, 61, 70, 71, 72, 86"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_cell_arfcn_line(self):
|
||||
"""Test that cell ARFCN lines are skipped."""
|
||||
line = " Cell ARFCNs: 63, 76"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_progress_line(self):
|
||||
"""Test that progress/status lines are skipped."""
|
||||
line = "Scanning GSM900 band..."
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_empty_line(self):
|
||||
"""Test handling of empty lines."""
|
||||
result = parse_grgsm_scanner_output("")
|
||||
assert result is None
|
||||
|
||||
def test_invalid_data(self):
|
||||
"""Test handling of non-numeric values."""
|
||||
line = "ARFCN: abc, Freq: xyz, CID: bad, LAC: data, MCC: bad, MNC: bad, Pwr: bad"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_no_identity_filtered(self):
|
||||
"""Test that MCC=0/MNC=0 entries (no network identity) are filtered out."""
|
||||
line = "ARFCN: 115, Freq: 925.0M, CID: 0, LAC: 0, MCC: 0, MNC: 0, Pwr: -100"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_mcc_zero_mnc_zero_filtered(self):
|
||||
"""Test that MCC=0/MNC=0 even with valid CID is filtered out."""
|
||||
line = "ARFCN: 113, Freq: 924.6M, CID: 1234, LAC: 5678, MCC: 0, MNC: 0, Pwr: -90"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_cid_zero_valid_mcc_passes(self):
|
||||
"""Test that CID=0 with valid MCC/MNC passes (partially decoded cell)."""
|
||||
line = "ARFCN: 115, Freq: 958.0M, CID: 0, LAC: 21864, MCC: 234, MNC: 10, Pwr: -51"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is not None
|
||||
assert result['cid'] == 0
|
||||
assert result['mcc'] == 234
|
||||
assert result['signal_strength'] == -51.0
|
||||
|
||||
def test_valid_cid_nonzero(self):
|
||||
"""Test that valid non-zero CID/MCC entries pass through."""
|
||||
line = "ARFCN: 115, Freq: 925.0M, CID: 19088, LAC: 21864, MCC: 234, MNC: 10, Pwr: -58"
|
||||
result = parse_grgsm_scanner_output(line)
|
||||
assert result is not None
|
||||
assert result['cid'] == 19088
|
||||
assert result['signal_strength'] == -58.0
|
||||
|
||||
|
||||
class TestParseTsharkOutput:
|
||||
"""Tests for parse_tshark_output()."""
|
||||
|
||||
def test_valid_full_output(self):
|
||||
"""Test parsing tshark output with all fields."""
|
||||
line = "5\t0xABCD1234\t123456789012345\t1234\t31245"
|
||||
result = parse_tshark_output(line)
|
||||
|
||||
assert result is not None
|
||||
assert result['type'] == 'device'
|
||||
assert result['ta_value'] == 5
|
||||
assert result['tmsi'] == '0xABCD1234'
|
||||
assert result['imsi'] == '123456789012345'
|
||||
assert result['lac'] == 1234
|
||||
assert result['cid'] == 31245
|
||||
assert result['distance_meters'] == 5 * 554 # TA * 554 meters
|
||||
assert 'timestamp' in result
|
||||
|
||||
def test_missing_optional_fields(self):
|
||||
"""Test parsing with missing optional fields (empty tabs).
|
||||
|
||||
A packet with TA but no TMSI/IMSI is discarded since there's
|
||||
no device identifier to track.
|
||||
"""
|
||||
line = "3\t\t\t1234\t31245"
|
||||
result = parse_tshark_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_missing_optional_fields_with_tmsi(self):
|
||||
"""Test parsing with TMSI but missing TA, IMSI, CID."""
|
||||
line = "\t0xABCD\t\t1234\t"
|
||||
result = parse_tshark_output(line)
|
||||
|
||||
assert result is not None
|
||||
assert result['ta_value'] is None
|
||||
assert result['tmsi'] == '0xABCD'
|
||||
assert result['imsi'] is None
|
||||
assert result['lac'] == 1234
|
||||
assert result['cid'] is None
|
||||
|
||||
def test_no_ta_value(self):
|
||||
"""Test parsing without TA value (empty first field)."""
|
||||
line = "\t0xABCD1234\t123456789012345\t1234\t31245"
|
||||
result = parse_tshark_output(line)
|
||||
|
||||
assert result is not None
|
||||
assert result['ta_value'] is None
|
||||
assert result['tmsi'] == '0xABCD1234'
|
||||
assert result['imsi'] == '123456789012345'
|
||||
assert result['lac'] == 1234
|
||||
assert result['cid'] == 31245
|
||||
|
||||
def test_invalid_line(self):
|
||||
"""Test handling of invalid tshark output."""
|
||||
line = "invalid data"
|
||||
result = parse_tshark_output(line)
|
||||
assert result is None
|
||||
|
||||
def test_empty_line(self):
|
||||
"""Test handling of empty lines."""
|
||||
result = parse_tshark_output("")
|
||||
assert result is None
|
||||
|
||||
def test_partial_fields(self):
|
||||
"""Test with fewer than 5 fields."""
|
||||
line = "5\t0xABCD1234" # Only 2 fields
|
||||
result = parse_tshark_output(line)
|
||||
assert result is None
|
||||
|
||||
|
||||
class TestArfcnToFrequency:
|
||||
"""Tests for arfcn_to_frequency()."""
|
||||
|
||||
def test_gsm850_arfcn(self):
|
||||
"""Test ARFCN in GSM850 band."""
|
||||
# GSM850: ARFCN 128-251, 869-894 MHz
|
||||
arfcn = 128
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 869000000 # 869 MHz
|
||||
|
||||
arfcn = 251
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 893600000 # 893.6 MHz
|
||||
|
||||
def test_egsm900_arfcn(self):
|
||||
"""Test ARFCN in EGSM900 band."""
|
||||
# EGSM900: ARFCN 0-124, DL = 935 + 0.2*ARFCN MHz
|
||||
arfcn = 0
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 935000000 # 935.0 MHz
|
||||
|
||||
arfcn = 22
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 939400000 # 939.4 MHz
|
||||
|
||||
arfcn = 124
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 959800000 # 959.8 MHz
|
||||
|
||||
def test_egsm900_ext_arfcn(self):
|
||||
"""Test ARFCN in EGSM900 extension band."""
|
||||
# EGSM900_EXT: ARFCN 975-1023, DL = 925.2 + 0.2*(ARFCN-975) MHz
|
||||
arfcn = 975
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 925200000 # 925.2 MHz
|
||||
|
||||
arfcn = 1023
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 934800000 # 934.8 MHz
|
||||
|
||||
def test_dcs1800_arfcn(self):
|
||||
"""Test ARFCN in DCS1800 band."""
|
||||
# DCS1800: ARFCN 512-885, 1805-1880 MHz
|
||||
# Note: ARFCN 512 also exists in PCS1900 and will match that first
|
||||
# Use ARFCN 811+ which is only in DCS1800
|
||||
arfcn = 811 # Beyond PCS1900 range (512-810)
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
# 811 is ARFCN offset from 512, so freq = 1805MHz + (811-512)*200kHz
|
||||
expected = 1805000000 + (811 - 512) * 200000
|
||||
assert freq == expected
|
||||
|
||||
arfcn = 885
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
assert freq == 1879600000 # 1879.6 MHz
|
||||
|
||||
def test_pcs1900_arfcn(self):
|
||||
"""Test ARFCN in PCS1900 band."""
|
||||
# PCS1900: ARFCN 512-810, 1930-1990 MHz
|
||||
# Note: overlaps with DCS1800 ARFCN range, but different frequencies
|
||||
arfcn = 512
|
||||
freq = arfcn_to_frequency(arfcn)
|
||||
# Will match first band (DCS1800 in Europe config)
|
||||
assert freq > 0
|
||||
|
||||
def test_invalid_arfcn(self):
|
||||
"""Test ARFCN outside known ranges."""
|
||||
with pytest.raises(ValueError, match="not found in any known GSM band"):
|
||||
arfcn_to_frequency(9999)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
arfcn_to_frequency(-1)
|
||||
|
||||
def test_arfcn_200khz_spacing(self):
|
||||
"""Test that ARFCNs are 200kHz apart."""
|
||||
arfcn1 = 128
|
||||
arfcn2 = 129
|
||||
freq1 = arfcn_to_frequency(arfcn1)
|
||||
freq2 = arfcn_to_frequency(arfcn2)
|
||||
assert freq2 - freq1 == 200000 # 200 kHz
|
||||
|
||||
|
||||
class TestValidateBandNames:
|
||||
"""Tests for validate_band_names()."""
|
||||
|
||||
def test_valid_americas_bands(self):
|
||||
"""Test valid band names for Americas region."""
|
||||
bands = ['GSM850', 'PCS1900']
|
||||
result, error = validate_band_names(bands, 'Americas')
|
||||
assert result == bands
|
||||
assert error is None
|
||||
|
||||
def test_valid_europe_bands(self):
|
||||
"""Test valid band names for Europe region."""
|
||||
# Note: Europe uses EGSM900, not GSM900
|
||||
bands = ['EGSM900', 'DCS1800', 'GSM850', 'GSM800']
|
||||
result, error = validate_band_names(bands, 'Europe')
|
||||
assert result == bands
|
||||
assert error is None
|
||||
|
||||
def test_valid_asia_bands(self):
|
||||
"""Test valid band names for Asia region."""
|
||||
# Note: Asia uses EGSM900, not GSM900
|
||||
bands = ['EGSM900', 'DCS1800']
|
||||
result, error = validate_band_names(bands, 'Asia')
|
||||
assert result == bands
|
||||
assert error is None
|
||||
|
||||
def test_invalid_band_for_region(self):
|
||||
"""Test invalid band name for a region."""
|
||||
bands = ['GSM900', 'INVALID_BAND']
|
||||
result, error = validate_band_names(bands, 'Americas')
|
||||
assert result == []
|
||||
assert error is not None
|
||||
assert 'Invalid bands' in error
|
||||
assert 'INVALID_BAND' in error
|
||||
|
||||
def test_invalid_region(self):
|
||||
"""Test invalid region name."""
|
||||
bands = ['GSM900']
|
||||
result, error = validate_band_names(bands, 'InvalidRegion')
|
||||
assert result == []
|
||||
assert error is not None
|
||||
assert 'Invalid region' in error
|
||||
|
||||
def test_empty_bands_list(self):
|
||||
"""Test with empty bands list."""
|
||||
result, error = validate_band_names([], 'Americas')
|
||||
assert result == []
|
||||
assert error is None
|
||||
|
||||
def test_single_valid_band(self):
|
||||
"""Test with single valid band."""
|
||||
bands = ['GSM850']
|
||||
result, error = validate_band_names(bands, 'Americas')
|
||||
assert result == ['GSM850']
|
||||
assert error is None
|
||||
|
||||
def test_case_sensitive_band_names(self):
|
||||
"""Test that band names are case-sensitive."""
|
||||
bands = ['gsm850'] # lowercase
|
||||
result, error = validate_band_names(bands, 'Americas')
|
||||
assert result == []
|
||||
assert error is not None
|
||||
|
||||
def test_multiple_invalid_bands(self):
|
||||
"""Test with multiple invalid bands."""
|
||||
bands = ['INVALID1', 'GSM850', 'INVALID2']
|
||||
result, error = validate_band_names(bands, 'Americas')
|
||||
assert result == []
|
||||
assert error is not None
|
||||
assert 'INVALID1' in error
|
||||
assert 'INVALID2' in error
|
||||
|
||||
|
||||
class TestRegionalBandsConfig:
|
||||
"""Tests for REGIONAL_BANDS configuration."""
|
||||
|
||||
def test_all_regions_defined(self):
|
||||
"""Test that all expected regions are defined."""
|
||||
assert 'Americas' in REGIONAL_BANDS
|
||||
assert 'Europe' in REGIONAL_BANDS
|
||||
assert 'Asia' in REGIONAL_BANDS
|
||||
|
||||
def test_all_bands_have_required_fields(self):
|
||||
"""Test that all bands have required configuration fields."""
|
||||
for region, bands in REGIONAL_BANDS.items():
|
||||
for band_name, band_config in bands.items():
|
||||
assert 'start' in band_config
|
||||
assert 'end' in band_config
|
||||
assert 'arfcn_start' in band_config
|
||||
assert 'arfcn_end' in band_config
|
||||
|
||||
def test_frequency_ranges_valid(self):
|
||||
"""Test that frequency ranges are positive and start < end."""
|
||||
for region, bands in REGIONAL_BANDS.items():
|
||||
for band_name, band_config in bands.items():
|
||||
assert band_config['start'] > 0
|
||||
assert band_config['end'] > 0
|
||||
assert band_config['start'] < band_config['end']
|
||||
|
||||
def test_arfcn_ranges_valid(self):
|
||||
"""Test that ARFCN ranges are valid."""
|
||||
for region, bands in REGIONAL_BANDS.items():
|
||||
for band_name, band_config in bands.items():
|
||||
assert band_config['arfcn_start'] >= 0
|
||||
assert band_config['arfcn_end'] >= 0
|
||||
assert band_config['arfcn_start'] <= band_config['arfcn_end']
|
||||
Reference in New Issue
Block a user