v3.25.5: the PLI dictionary override guard fix
The live report: POST /api/pli-dict did not change the dictionary.
_PLI_DATA held a '' placeholder for every qualifier, and the clear
branch's pop removed the placeholder even when there was no override.
The PWA's Save posts every field, so one Save wiped the placeholders;
the guard `if code in _PLI_DATA` then silently skipped every later
override for those qualifiers (HTTP 200 + the unchanged/default dict)
until a restart - and the next Save wiped them again.
_PLI_DATA is now overrides-only ({}; the GET and _pli_data_hex already
fall back to PLI_DEFAULTS) and the guard tests PLI_QUALIFIER_NAMES, so a
new override always sticks and a clear never poisons a later set.
Tests: the endpoint test now saves/restores the dict (its pop(0x01)
cleanup was the same trap) plus a regression - the PWA-style full-dict
POST, then new overrides with and without defaults must stick, a clear
restores the default and a later set still works, an unknown qualifier
is ignored. 886 frontend + 874 Python; sw cache simple-v429.
This commit is contained in:
+1
-1
@@ -1905,7 +1905,7 @@
|
||||
// ===== Version =====
|
||||
// Single source of truth for the PWA version: shown in the header and used
|
||||
// by the server version check in pysimConnect().
|
||||
const SIMPLE_VERSION = '3.25.4';
|
||||
const SIMPLE_VERSION = '3.25.5';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== view history (the browser Back/Forward buttons) =====
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v428';
|
||||
const CACHE = 'simple-v429';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "pysim-simple-server"
|
||||
version = "3.25.4"
|
||||
version = "3.25.5"
|
||||
description = "HTTP REST server wrapping pysim for the SIMple PWA"
|
||||
requires-python = ">=3.8"
|
||||
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
|
||||
|
||||
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
|
||||
from osmocom.utils import rpad
|
||||
|
||||
|
||||
VERSION = '3.25.4'
|
||||
VERSION = '3.25.5'
|
||||
|
||||
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
|
||||
|
||||
@@ -2669,7 +2669,13 @@ PLI_DEFAULTS = {
|
||||
0x0E: '3F02' + '0308',
|
||||
}
|
||||
|
||||
_PLI_DATA = {q: '' for q in PLI_QUALIFIER_NAMES} # the operator overrides
|
||||
# The operator's overrides only, keyed by qualifier code - the built-in
|
||||
# PLI_DEFAULTS above are the base layer and stay the fallback; a field cleared
|
||||
# through /api/pli-dict removes its entry (v3.25.5). Keeping this dict free
|
||||
# of placeholder keys is deliberate: the POST's guard tests the known
|
||||
# qualifiers, never "an override already exists" (a full-dict Save posts every
|
||||
# field - an empty one must not make later overrides no-ops).
|
||||
_PLI_DATA = {}
|
||||
|
||||
_BIP = httpota.BipTerminal()
|
||||
# Active BIP session, shared by the SCP81 listener and the Simulator's
|
||||
@@ -9102,7 +9108,10 @@ class PysimHandler(BaseHTTPRequestHandler):
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
code = int(k, 16)
|
||||
if code in _PLI_DATA:
|
||||
# a known qualifier - never "an override already
|
||||
# exists": a new override must stick, and a clear
|
||||
# must not poison a later set (v3.25.5, live report)
|
||||
if code in PLI_QUALIFIER_NAMES:
|
||||
if v:
|
||||
bytes.fromhex(v) # valid hex only
|
||||
_PLI_DATA[code] = v
|
||||
|
||||
@@ -142,6 +142,7 @@ class PliDictHttpTests(unittest.TestCase):
|
||||
|
||||
def test_defaults_overrides_and_clearing(self):
|
||||
import pysim_simple_server.server as srv
|
||||
saved = dict(srv._PLI_DATA)
|
||||
try:
|
||||
status, resp = self._req('GET', '/api/pli-dict')
|
||||
self.assertEqual(status, 200, resp)
|
||||
@@ -162,8 +163,46 @@ class PliDictHttpTests(unittest.TestCase):
|
||||
# a bad value is ignored
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'01': 'ZZ'})
|
||||
self.assertEqual(resp['01'], srv.PLI_DEFAULTS[0x01])
|
||||
self.assertNotIn(0x01, srv._PLI_DATA)
|
||||
finally:
|
||||
srv._PLI_DATA.pop(0x01, None)
|
||||
srv._PLI_DATA.clear()
|
||||
srv._PLI_DATA.update(saved)
|
||||
|
||||
def test_a_new_override_after_a_full_save_sticks(self):
|
||||
# The PWA's Save posts every field; the empty ones used to pop the
|
||||
# dictionary's placeholder keys, after which the old `code in
|
||||
# _PLI_DATA` guard silently skipped every later override for those
|
||||
# qualifiers - POST answered 200 with the unchanged/default dict
|
||||
# (live report 2026-10-11). The guard now tests the known qualifiers.
|
||||
import pysim_simple_server.server as srv
|
||||
saved = dict(srv._PLI_DATA)
|
||||
try:
|
||||
full = {('%02X' % q): '' for q in srv.PLI_QUALIFIER_NAMES}
|
||||
status, resp = self._req('POST', '/api/pli-dict', full)
|
||||
self.assertEqual(status, 200, resp)
|
||||
self.assertEqual(srv._PLI_DATA, {})
|
||||
# a new override for a qualifier with a default sticks
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'0A': '630102'})
|
||||
self.assertEqual(resp['0A'], '630102')
|
||||
# and one for a qualifier without a default
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'02': 'AABBCC'})
|
||||
self.assertEqual(resp['02'], 'AABBCC')
|
||||
status, resp = self._req('GET', '/api/pli-dict')
|
||||
self.assertEqual(resp['0A'], '630102')
|
||||
self.assertEqual(resp['02'], 'AABBCC')
|
||||
# clearing restores the default and a later set still works
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'0A': ''})
|
||||
self.assertEqual(resp['0A'], srv.PLI_DEFAULTS[0x0A])
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'0A': '630103'})
|
||||
self.assertEqual(resp['0A'], '630103')
|
||||
# an unknown qualifier is ignored
|
||||
status, resp = self._req('POST', '/api/pli-dict', {'18': 'AABB'})
|
||||
self.assertEqual(status, 200, resp)
|
||||
self.assertNotIn(0x18, srv._PLI_DATA)
|
||||
self.assertEqual(resp['0A'], '630103')
|
||||
finally:
|
||||
srv._PLI_DATA.clear()
|
||||
srv._PLI_DATA.update(saved)
|
||||
|
||||
|
||||
class CardFreeGetTests(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user