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:
Smittix
2026-02-08 22:04:12 +00:00
parent b64bff24d9
commit 9f6632c5e0
17 changed files with 21 additions and 6516 deletions
-360
View File
@@ -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']