v3.22.0: the event action uses the Simulator's forms (all modelled events)

The test-script event action edited its fields as a JSON textarea and only
accepted the four events events.py builds; every other event needed the raw
envelope action with hand-copied hex.  It now renders the same EVENT_FORMS
form the Phone tab's send dialog uses, for every modelled event (29; no
release gating - a script may target another card).

- PWA: the field renderer/collector is shared (eventFormFieldsHtml /
  eventFormValues / eventFormPatternError / eventFormSrc / eventFieldGroupsUpdate);
  the Phone tab keeps its ids, its release gating and its onchange updaters,
  the step editor uses test-f-ev- ids, the generic group updater and a live
  Event data (hex) preview with the one-ENVELOPE check.  The event select
  lists all EVENT_FORMS events (the numeric keys are decimal strings -
  parseInt(k, 10)); the action stores the collected fields, the built data
  and the event's source (fixed cfg.src or the cfg.srcField input - the
  action never carried src before, so MT call / IMS registration / slices
  could not be network-sourced).  The JSON textarea is gone.
- engine: the `event` action keeps `fields` validation for the four
  server-built events (a stray data hex is ignored) and accepts the
  form-built `data` for every other event - hex-validated and checked
  against events.EVENT_DATA_MAX (244, the one-ENVELOPE budget; server.py's
  _EVENT_DATA_MAX now reads it from events.py).  The runner builds the four
  at run time (fresh host clock) and sends the stored data for the rest.
- tests: the engine's data path (accepted / missing / malformed / oversized,
  the four still fields-built), the runner sending the data (fake scc), the
  editor's collect/prefill/patterns/source (Node), the shared helpers.
  Docs: test-scripts.md, api.md, help EN/RU.  Tests: 838 frontend +
  797 Python; sw cache simple-v401.
This commit is contained in:
2026-10-06 01:07:12 +03:00
parent bcd9fb9a4c
commit 40c1e0f148
11 changed files with 389 additions and 112 deletions
+39 -2
View File
@@ -15,6 +15,7 @@ if str(PY_SIM) not in sys.path:
sys.path.insert(0, str(PY_SIM))
import pysim_simple_server.server as S
import pysim_simple_server.events as events
import pysim_simple_server.testscript as T
@@ -218,6 +219,27 @@ class TestValidation(unittest.TestCase):
T.normalise_script({'steps': [{'type': 'action', 'kind': 'envelope',
'params': {'event': 3, 'src': 'zz'}}]}, _resolver)
def test_event_action_accepts_the_form_built_data(self):
# The events events.py does not model carry the PWA form's built hex;
# a modelled event builds from `fields` (a stray data hex is ignored).
script = T.normalise_script({'steps': [
{'type': 'action', 'kind': 'event',
'params': {'event': '0x00', 'data': '1c0100', 'src': '83'}}]}, _resolver)
self.assertEqual(script['steps'][0]['params'],
{'event': 0x00, 'data': '1C0100', 'src': '83'})
script = T.normalise_script({'steps': [
{'type': 'action', 'kind': 'event',
'params': {'event': '0x03', 'fields': {'status': 1}, 'data': 'AA'}}]}, _resolver)
self.assertNotIn('data', script['steps'][0]['params'])
self.assertEqual(script['steps'][0]['params']['fields'], {'status': 1})
# missing, malformed and oversized data are refused before the run
for bad in ({'event': '0x00'},
{'event': '0x00', 'data': 'ABC'},
{'event': '0x00', 'data': 'AA' * (events.EVENT_DATA_MAX + 1)}):
with self.assertRaises(T.ScriptError):
T.normalise_script({'steps': [{'type': 'action', 'kind': 'event',
'params': bad}]}, _resolver)
def test_scp80_keyset_number_and_new_content_checks(self):
# kvn (1..15) selects the preset's keyset; `por`/`files` are expect
# content checks (the PoR contents / the REFRESH file list).
@@ -850,8 +872,7 @@ class TestRunnerActions(RunnerTestCase):
# the runner logs one step-annotated ENVELOPE line, not the generic
# sender pair as well (its line carries the name, src and SW)
log = buf.getvalue()
self.assertIn('TEST-RUN step 1: ENVELOPE(Event Download) type=0x03 '
'(location_status) data=9B0100930752F01000FF0001 src=82 -> SW=9000',
self.assertIn('TEST-RUN step 1: ENVELOPE(Event Download) type=0x03 ' '(location_status) data=9B0100930752F01000FF0001 src=82 -> SW=9000',
log)
self.assertNotIn('ENVELOPE(Event Download): type=', log)
self.assertNotIn('ENVELOPE SW:', log)
@@ -878,6 +899,22 @@ class TestRunnerActions(RunnerTestCase):
self.assertEqual(run['status'], 'ok', run['steps'])
self.assertIn('99010082028381', scc3.sent[0].upper())
def test_event_action_sends_the_form_built_data(self):
# The events events.py does not model (e.g. MT call) run with the
# PWA form's built hex and its source (network).
scc = FakeScc()
scc.push('80C2', '', '9000')
buf = io.StringIO()
with contextlib.redirect_stderr(buf):
run = self.run_script(FakeServer(scc), [
{'type': 'action', 'kind': 'event',
'params': {'event': '0x00', 'data': '1C0100', 'src': '83'}},
])
self.assertEqual(run['status'], 'ok', run['steps'])
self.assertIn('99010082028381' + '1C0100', scc.sent[0].upper())
self.assertIn('type=0x00 (0x00) data=1C0100 src=83 -> SW=9000',
buf.getvalue())
def test_scp80_uses_the_preset_and_advances_the_counter(self):
scc = FakeScc()
server = FakeServer(scc)