import os import sys import unittest sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'payload', 'user', 'remote_access', 'pager-webui')) import server def setUpModule(): __import__('importlib').reload(server) class ChannelBandTest(unittest.TestCase): def test_2g_channels(self): for ch in (1, 6, 11, 14): self.assertEqual(server.channel_band(ch), server.BAND_2G) def test_5g_channels(self): for ch in (36, 48, 100, 149, 165, 177): self.assertEqual(server.channel_band(ch), server.BAND_5G) def test_6g_channels(self): for ch in (181, 189, 197, 205, 213, 225, 233): self.assertEqual(server.channel_band(ch), server.BAND_6G) def test_invalid(self): for ch in (0, 15, 17, 21, 33, 35, 178, 234, None, 'x'): self.assertIsNone(server.channel_band(ch)) class ChannelBandsConsistencyTest(unittest.TestCase): def test_lists_match_channel_band(self): for band, channels in server.CHANNEL_BANDS.items(): for ch in channels: self.assertEqual(server.channel_band(ch), band, '%s should be %s' % (ch, band)) def test_no_out_of_list_channels(self): for ch in list(range(0, 235)): band = server.channel_band(ch) if band is not None: self.assertIn(ch, server.CHANNEL_BANDS[band], '%s should be listed for %s' % (ch, band)) def test_boundaries(self): self.assertEqual(server.CHANNEL_BANDS[server.BAND_2G][-1], 14) self.assertEqual(server.CHANNEL_BANDS[server.BAND_5G][-1], 177) self.assertEqual(server.CHANNEL_BANDS[server.BAND_6G][0], 181) self.assertEqual(server.CHANNEL_BANDS[server.BAND_6G][-1], 233) class ChannelFreqTest(unittest.TestCase): def test_freqs(self): self.assertEqual(server.channel_freq(server.BAND_2G, 1), 2412) self.assertEqual(server.channel_freq(server.BAND_2G, 11), 2462) self.assertEqual(server.channel_freq(server.BAND_5G, 36), 5180) self.assertEqual(server.channel_freq(server.BAND_5G, 165), 5825) self.assertEqual(server.channel_freq(server.BAND_6G, 1), 5955) self.assertEqual(server.channel_freq(server.BAND_6G, 233), 7115) class BandAuxTest(unittest.TestCase): def test_htmode(self): self.assertEqual(server.band_htmode(server.BAND_2G), 'HT20') self.assertEqual(server.band_htmode(server.BAND_5G), 'VHT80') self.assertEqual(server.band_htmode(server.BAND_6G), 'HE80') def test_radio(self): self.assertEqual(server.band_radio(server.BAND_2G), 'radio0') self.assertEqual(server.band_radio(server.BAND_5G), 'radio1') self.assertEqual(server.band_radio(server.BAND_6G), 'radio1') def test_dfs_marker(self): for ch in (52, 64, 100, 144): self.assertIn(ch, server.DFS_CHANNELS) for ch in (36, 48, 149): self.assertNotIn(ch, server.DFS_CHANNELS) self.assertEqual(set(server.DFS_CHANNELS), set(range(52, 65, 4)) | set(range(100, 145, 4))) def ctx(body=None): return type('C', (), {'body': body, 'args': (), 'query': {}})() class GetApRadio1Test(unittest.TestCase): def _uci(self, section): table = { 'radio0': {'type': 'wifi-device', 'band': '2g', 'channel': '11', 'htmode': 'HT20', 'country': 'US'}, 'radio1': {'type': 'wifi-device', 'band': '5g', 'channel': 'auto', 'htmode': 'VHT80', 'country': 'US'}, 'wlan0open': {'device': 'radio0', 'mode': 'ap', 'ssid': 'pager-open', 'disabled': '0', 'hidden': '0', 'encryption': 'none', 'channel': '11'}, 'wlan0wpa': {'device': 'radio0', 'mode': 'ap', 'ssid': 'Service', 'disabled': '0', 'hidden': '0', 'encryption': 'psk2', 'channel': '1', 'key': 'testpass123'}, 'wlan1open': {'device': 'radio1', 'mode': 'ap', 'ssid': 'CorpGuest', 'disabled': '0', 'hidden': '0', 'encryption': 'none', 'channel': '36'}, 'wlan1wpa': {'device': 'radio1', 'mode': 'ap', 'ssid': 'Corp', 'disabled': '0', 'hidden': '0', 'encryption': 'sae', 'channel': '1', 'key': 'secret123'}, } return dict(table.get(section, {})) def setUp(self): server._uci_wifi_iface = lambda name: self._uci(name) server.daemon_sock_call = lambda method, path, body=None, timeout=10: ( 404, {'error': 'not found'}) def test_open_reports_radio1_when_present(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['radio1_open']['ssid'], 'CorpGuest') self.assertEqual(payload['radio1_open']['channel'], 36) self.assertEqual(payload['radio1_open']['country'], 'US') # radio0 cards keep reporting radio0 truth self.assertEqual(payload['open']['ssid'], 'pager-open') def test_wpa_reports_radio1_when_present(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['radio1_wpa']['ssid'], 'Corp') self.assertEqual(payload['radio1_wpa']['enctype'], 'sae') self.assertEqual(payload['radio1_wpa']['channel'], 1) self.assertEqual(payload['wpa']['ssid'], 'Service') def test_radio1_info(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['radios']['radio1']['band'], server.BAND_5G) self.assertEqual(payload['radios']['radio1']['channel'], 'auto') self.assertEqual(payload['radios']['radio0']['band'], server.BAND_2G) class GetApRadio1AbsentTest(unittest.TestCase): """Regression: no radio1 AP sections -> today's 2.4GHz behavior.""" def _uci(self, section): table = { 'radio0': {'type': 'wifi-device', 'band': '2g', 'channel': '11', 'htmode': 'HT20', 'country': 'US'}, 'radio1': {'type': 'wifi-device', 'band': '5g', 'channel': 'auto', 'htmode': 'VHT80', 'country': 'US'}, 'wlan0open': {'device': 'radio0', 'mode': 'ap', 'ssid': 'pager-open', 'disabled': '0', 'hidden': '0', 'encryption': 'none', 'channel': '11'}, 'wlan0wpa': {'device': 'radio0', 'mode': 'ap', 'ssid': 'Service', 'disabled': '0', 'hidden': '0', 'encryption': 'psk2', 'channel': '1', 'key': 'testpass123'}, } return dict(table.get(section, {})) def setUp(self): server._uci_wifi_iface = lambda name: self._uci(name) server.daemon_sock_call = lambda method, path, body=None, timeout=10: ( 404, {'error': 'not found'}) def test_open_uses_wlan0open(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['open']['ssid'], 'pager-open') self.assertEqual(payload['open']['channel'], 11) def test_wpa_uses_wlan0wpa(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['wpa']['ssid'], 'Service') self.assertEqual(payload['wpa']['channel'], 1) class SetApRadio1Test(unittest.TestCase): def setUp(self): self.uci = {} self.runs = [] def fake_uci_get(section): return self.uci.get(section) def fake_run(args, timeout=20, input_data=None): self.runs.append((list(args), input_data)) a = list(args) if a[:2] == ['uci', 'set']: k, _, v = a[2].partition('=') self.uci[k] = v if a[:2] == ['uci', 'get']: return (0, self.uci.get(a[2], '') + '\n', '') return (0, '', '') server._uci_wifi_iface = fake_uci_get server._uci_section = fake_uci_get server.device_run = fake_run server.daemon_sock_call = lambda method, path, body=None, timeout=10: ( 200, {'success': True}) def test_5g_open_writes_radio1_sections(self): server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'CorpGuest', 'hidden': False, 'enabled': True, 'channel': 36, 'country': 'US'}})) self.assertEqual(self.uci['wireless.radio1.band'], '5g') self.assertEqual(self.uci['wireless.radio1.channel'], '36') self.assertEqual(self.uci['wireless.radio1.htmode'], 'VHT80') self.assertEqual(self.uci['wireless.wlan1open'], 'wifi-iface') self.assertEqual(self.uci['wireless.wlan1open.device'], 'radio1') self.assertEqual(self.uci['wireless.wlan1open.disabled'], '0') self.assertEqual(self.uci['wireless.wlan1open.ssid'], 'CorpGuest') self.assertEqual(self.uci['wireless.wlan1open.encryption'], 'none') self.assertEqual(self.uci['pineapd.wlan1mon.hop'], '0') self.assertIn(['wifi', 'reload'], [r[0] for r in self.runs]) self.assertIn(['/etc/init.d/pineapd', 'reload'], [r[0] for r in self.runs]) def test_6g_wpa_sae(self): server.h_pineap_wifi_set_ap(ctx({'wpa': { 'ssid': 'Corp', 'passphrase': 'secret123', 'enctype': 'sae', 'hidden': False, 'enabled': True, 'channel': 181}})) self.assertEqual(self.uci['wireless.radio1.band'], '6g') self.assertEqual(self.uci['wireless.radio1.htmode'], 'HE80') self.assertEqual(self.uci['wireless.wlan1wpa.encryption'], 'sae') def test_6g_rejects_psk2(self): status, payload = server.h_pineap_wifi_set_ap(ctx({'wpa': { 'ssid': 'Corp', 'passphrase': 'secret123', 'enctype': 'psk2', 'hidden': False, 'enabled': True, 'channel': 181}})) self.assertEqual(status, 400) def test_6g_open_rejected(self): status, payload = server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'CorpGuest', 'hidden': False, 'enabled': True, 'channel': 181, 'country': 'US'}})) self.assertEqual(status, 400) self.assertIn('6GHz', payload['error']) def test_6g_disable_removes_radio1(self): self.uci['pineapd.wlan1mon.hop'] = '0' self.uci['wlan1wpa'] = {'device': 'radio1'} status, payload = server.h_pineap_wifi_set_ap(ctx({'wpa': { 'ssid': 'Corp', 'passphrase': 'secret123', 'enctype': 'sae', 'hidden': False, 'enabled': False, 'channel': 181}})) self.assertEqual(status, 200) self.assertEqual(self.uci['wireless.radio1.channel'], 'auto') self.assertEqual(self.uci['pineapd.wlan1mon.hop'], '1') def test_5g_open_rejects_bad_bssid(self): status, payload = server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'CorpGuest', 'bssid': 'not-a-mac', 'hidden': False, 'enabled': True, 'channel': 36, 'country': 'US'}})) self.assertEqual(status, 400) def test_hop_read_failure_still_pauses(self): def fake_run(args, timeout=20, input_data=None): self.runs.append((list(args), input_data)) a = list(args) if a[:2] == ['uci', 'get']: return (1, '', '') if a[:2] == ['uci', 'set']: k, _, v = a[2].partition('=') self.uci[k] = v return (0, '', '') server.device_run = fake_run server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'CorpGuest', 'hidden': False, 'enabled': True, 'channel': 36, 'country': 'US'}})) self.assertEqual(self.uci['pineapd.wlan1mon.hop'], '0') def test_2g_still_uses_daemon_path(self): calls = [] def fake_sock(method, path, body=None, timeout=10): if method == 'GET': return 200, {'loghandshake': True} calls.append((method, path, body)) return 200, {'success': True} server.daemon_sock_call = fake_sock server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'pager-open', 'hidden': False, 'enabled': True, 'channel': 11, 'country': 'US'}})) put = [c for c in calls if c[0] == 'PUT'] self.assertEqual(len(put), 1) self.assertEqual(put[0][1], '/api/settings/wifi/set_ap') self.assertEqual(put[0][2]['configs'][0]['interface'], 'wlan0open') def test_2g_removes_existing_radio1(self): self.uci['pineapd.wlan1mon.hop'] = '0' self.uci['wlan1open'] = {'device': 'radio1'} server.h_pineap_wifi_set_ap(ctx({'open': { 'ssid': 'pager-open', 'hidden': False, 'enabled': True, 'channel': 11, 'country': 'US'}})) self.assertEqual(self.uci['pineapd.wlan1mon.hop'], '1') self.assertEqual(self.uci.get('wireless.radio1.channel'), 'auto') def test_mixed_24g_and_radio1_rejected(self): status, payload = server.h_pineap_wifi_set_ap(ctx({ 'open': {'ssid': 'CorpGuest', 'hidden': False, 'enabled': True, 'channel': 36, 'country': 'US'}, 'wpa': {'ssid': 'Office', 'passphrase': 'secret123', 'enctype': 'psk2', 'hidden': False, 'enabled': True, 'channel': 6}})) self.assertEqual(status, 400) self.assertIn('2.4GHz', payload['error']) class GetApRadioChannelFallbackTest(unittest.TestCase): """Regression: iface without a channel option inherits the radio channel.""" def _uci(self, section): table = { 'radio0': {'type': 'wifi-device', 'band': '2g', 'channel': '11', 'htmode': 'HT20', 'country': 'US'}, 'radio1': {'type': 'wifi-device', 'band': '5g', 'channel': 'auto', 'htmode': 'VHT80', 'country': 'US'}, 'wlan0open': {'device': 'radio0', 'mode': 'ap', 'ssid': 'pager-open', 'disabled': '0', 'hidden': '0', 'encryption': 'none'}, 'wlan0wpa': {'device': 'radio0', 'mode': 'ap', 'ssid': 'Service', 'disabled': '0', 'hidden': '0', 'encryption': 'psk2', 'key': 'testpass123'}, } return dict(table.get(section, {})) def setUp(self): server._uci_wifi_iface = lambda name: self._uci(name) server.daemon_sock_call = lambda method, path, body=None, timeout=10: ( 404, {'error': 'not found'}) def test_open_falls_back_to_radio_channel(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['open']['channel'], 11) def test_wpa_falls_back_to_radio_channel(self): status, payload = server.h_pineap_wifi_get_ap(ctx()) self.assertEqual(status, 200) self.assertEqual(payload['wpa']['channel'], 11) class GetApReconcileTest(unittest.TestCase): def setUp(self): server._last_reconcile = 0.0 self.uci = {'wlan1wpa': {'device': 'radio1', 'mode': 'ap', 'ssid': 'Corp', 'disabled': '0', 'encryption': 'sae', 'channel': '36'}} self.runs = [] server._uci_wifi_iface = lambda name: dict(self.uci.get(name, {})) server._uci_section = lambda name: {} server.daemon_sock_call = lambda method, path, body=None, timeout=10: ( 404, {'error': 'not found'}) server.device_run = lambda args, timeout=20, input_data=None: ( self.runs.append(list(args)) or (0, '', '')) self._real_exists = os.path.exists def tearDown(self): os.path.exists = self._real_exists def test_missing_netdev_triggers_wifi_reload(self): os.path.exists = lambda p: False server.h_pineap_wifi_get_ap(ctx()) self.assertIn(['wifi', 'reload'], self.runs) def test_present_netdev_skips_reload(self): os.path.exists = lambda p: True server.h_pineap_wifi_get_ap(ctx()) self.assertNotIn(['wifi', 'reload'], self.runs) def test_disabled_section_skips_reload(self): self.uci['wlan1wpa']['disabled'] = '1' os.path.exists = lambda p: False server.h_pineap_wifi_get_ap(ctx()) self.assertNotIn(['wifi', 'reload'], self.runs) if __name__ == '__main__': unittest.main()