bb13ed45f8
The startup init (server booted with the card already in the reader) equips the card and reads the ICCID itself, but never created server.net_state - only /api/command equip and the auto-equip worker did. With a card present at boot the monitor stayed empty, net-sim responses carried net_state: null and the panel only started updating after pressing Refresh (which lazily created the state). Fix: - factor _netstate_read(app) / _netstate_install() / _netstate_init() in server.py; _apply_equipped_card uses _netstate_init() - __main__ startup: read the monitored EFs in the same CAT-free window as the ICCID and install the state once the server object exists - _netstate_ensure() lazily initializes the state in the net-sim/event hooks so any equip path predating the monitor cannot leave it dead - netSimRun falls back to netStateFetch() when a response has no net_state - tests: tests/test_netstate_server.py (candidate order, record vs transparent, absent files, install, ensure); SW cache simple-v204
98 lines
4.0 KiB
Python
98 lines
4.0 KiB
Python
# coding=utf-8
|
|
"""Tests for the server-side Network-state monitor plumbing.
|
|
|
|
Covers the read helper (candidate order, transparent vs record files, absent
|
|
files) and the install/ensure helpers shared by the equip paths and the HTTP
|
|
endpoints. The card I/O is faked; ``_select_path`` is stubbed so no pySim
|
|
file tree is needed.
|
|
"""
|
|
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
|
|
from pysim_simple_server import server
|
|
|
|
|
|
class FakeLchan:
|
|
def __init__(self, files):
|
|
self.files = files # path -> ('transparent', hex) | ('record', [hex])
|
|
self.calls = []
|
|
self.selected = None
|
|
self.selected_file = None
|
|
self.selected_file_fcp = True
|
|
self.selected_file_type = lambda: 'ef'
|
|
self.selected_file_structure = lambda: self.files[self.selected][0]
|
|
self.selected_file_num_of_rec = lambda: len(self.files[self.selected][1])
|
|
self.read_binary = lambda: (self.files[self.selected][1], '9000')
|
|
self.read_record = lambda i: (self.files[self.selected][1][i - 1], '9000')
|
|
|
|
def select(self, path):
|
|
self.calls.append(('select', path))
|
|
if path not in self.files:
|
|
raise RuntimeError('file not found: %s' % path)
|
|
self.selected = path
|
|
|
|
|
|
class NetStateServerTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self._orig_select = server._select_path
|
|
|
|
def fake_select(lchan, path, app):
|
|
lchan.select(path)
|
|
return None, None
|
|
|
|
server._select_path = fake_select
|
|
|
|
def tearDown(self):
|
|
server._select_path = self._orig_select
|
|
|
|
def test_read_walks_candidates_and_marks_missing(self):
|
|
lchan = FakeLchan({
|
|
'DF.GSM/6F07': ('transparent', '0829AA'), # 2nd IMSI candidate
|
|
'ADF.USIM/6FE4': ('linear_fixed', ['AABB', 'CCDD']), # EPSNSC records
|
|
})
|
|
app = SimpleNamespace(rs=SimpleNamespace(lchan=[lchan]))
|
|
out = server._netstate_read(app, ['imsi', 'epsnsc', 'spdi'])
|
|
self.assertTrue(out['imsi']['present'])
|
|
self.assertEqual(out['imsi']['path'], 'DF.GSM/6F07')
|
|
self.assertEqual(out['imsi']['kind'], 'transparent')
|
|
self.assertEqual(out['imsi']['data'], '0829AA')
|
|
self.assertEqual(out['epsnsc']['kind'], 'record')
|
|
self.assertEqual(out['epsnsc']['records'],
|
|
[{'num': 1, 'data': 'AABB'},
|
|
{'num': 2, 'data': 'CCDD'}])
|
|
self.assertFalse(out['spdi']['present'])
|
|
# candidates are walked in FILE_DEFS order (spdi sits before epsnsc)
|
|
self.assertEqual(lchan.calls,
|
|
[('select', 'ADF.USIM/6F07'),
|
|
('select', 'DF.GSM/6F07'),
|
|
('select', 'ADF.USIM/6FCD'),
|
|
('select', 'ADF.USIM/6FE4')])
|
|
|
|
def test_install_builds_the_state_and_network(self):
|
|
files = {'imsi': {'name': 'EF.IMSI', 'fid': '6F07', 'present': True,
|
|
'kind': 'transparent', 'data': '082982608200002080'}}
|
|
srv = SimpleNamespace(mcc_mnc_path=None)
|
|
server._netstate_install(srv, files)
|
|
self.assertEqual(srv.net_state['files']['imsi']['source'], 'init')
|
|
self.assertEqual(srv.net_state['files']['imsi']['data'],
|
|
'082982608200002080')
|
|
self.assertIsNotNone(srv.net_state['read_at'])
|
|
self.assertIsNotNone(srv.net_state['network'])
|
|
|
|
def test_ensure_creates_the_state_only_with_a_readable_iccid(self):
|
|
lchan = FakeLchan({})
|
|
app = SimpleNamespace(rs=SimpleNamespace(lchan=[lchan]))
|
|
srv = SimpleNamespace(iccid='8970119000004600098', app=app,
|
|
mcc_mnc_path=None)
|
|
state = server._netstate_ensure(srv)
|
|
self.assertIsNotNone(state)
|
|
self.assertEqual(srv.net_state, state)
|
|
srv2 = SimpleNamespace(iccid=None, app=app, mcc_mnc_path=None)
|
|
self.assertIsNone(server._netstate_ensure(srv2))
|
|
self.assertFalse(hasattr(srv2, 'net_state'))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|