fix: report the real PoR/remote-SW result of every RAM install step (v3.6.2)
The RAM installer read `por['decoded']['response_status']`, but the compact
response decoder's `decoded` only carries {number_of_commands,
last_status_word, last_response_data} - so the suffix was always empty and
every step showed the useless `por_error_`, even when the PoR was por_ok.
The PoR verdict is the top-level `response_status`.
With that fixed, the decoded details also show that the remote command's own
status word was the real verdict all along: a captured live install returned
por_ok with `last_status_word` 6700/6F00 (the card rejected every RAM APDU)
while the installer reported success.
- new `_ram_step_result()`/`_por_remote_sw()`/`_ram_remote_sw_ok()`: a step
fails on a non-por_ok PoR, on a remote SW outside the success set (9000,
61xx more data, 62xx/63xx warnings, CAFE GP "more data"), or on an
undecodable PoR (a 9000 transport SW with no PoR at all is `no_por`, not a
failure). A decoded 61xx is explicitly success, per GP/ISO.
- step records gain por_sw/por_type/por_cntr/por_data/por_raw and
`por_error`; the response carries `failed_step` and a detailed error such
as `LOAD (1/9): remote SW 6700`; the log line now prints
`status=por_ok remote_sw=6700`.
- PWA: new pure `ramStepLine()` renders e.g.
`❌ Шаг 2: LOAD (1/9) — PoR ok · remote SW 6700 (Wrong length in Lc) · 274 B / 3 SMS`
(SW meaning via the existing lookupSw decoder) and the transport SW only
when it is not 9000.
- tests: tests/test_ota_helpers.py RamPorStepTest (success set incl. 61xx,
the captured 6700 failure, no-PoR/undecodable, expanded responses);
ram.test.js ramStepLine cases.
- docs/api.md RAM install step fields + failure semantics; AGENTS updated.
603 frontend / 487 Python green; version 3.6.2; sw simple-v275.
This commit is contained in:
@@ -30,6 +30,9 @@ from pysim_simple_server.server import (
|
||||
_ota_reference,
|
||||
_parse_select_item,
|
||||
_parse_setup_menu_items,
|
||||
_por_remote_sw,
|
||||
_ram_remote_sw_ok,
|
||||
_ram_step_result,
|
||||
_record_tr,
|
||||
_send_secured_packet,
|
||||
_spi_from_bytes,
|
||||
@@ -1243,3 +1246,62 @@ class TerminalProfileTest(unittest.TestCase):
|
||||
self.assertEqual(server_obj.event_list, [0x09])
|
||||
self.assertEqual(server_obj.sim_menu, 'menu')
|
||||
self.assertTrue(resp['menu'])
|
||||
|
||||
|
||||
class RamPorStepTest(unittest.TestCase):
|
||||
"""The RAM install reports the PoR verdict and the remote command's own
|
||||
status word. Vectors: a captured live response where every remote
|
||||
command returned SW 6700/6F00 while the PoR itself was por_ok, and
|
||||
synthetic success responses (9000, 6113)."""
|
||||
|
||||
# 02 71 00 | 000e 0a | TAR b00011/000000 | CNTR | PCNT | STS=00 | compact
|
||||
POR_INSTALL = '027100000e0a000000000000011b0000026700'
|
||||
POR_INSTALL_OK = '027100000e0ab0001100000000000000019000'
|
||||
POR_INSTALL_61XX = '027100000e0ab0001100000000000000016113'
|
||||
|
||||
@staticmethod
|
||||
def _por(hexstr):
|
||||
return _decode_por('00', '00', '01', '01', '0000000000',
|
||||
'00' * 16, '00' * 16, hexstr)
|
||||
|
||||
def test_remote_sw_success_set(self):
|
||||
for sw in ('9000', '6113', '62F1', '6310', 'CAFE'):
|
||||
self.assertTrue(_ram_remote_sw_ok(sw), sw)
|
||||
for sw in ('6700', '6F00', '6A82', '6400', '', None):
|
||||
self.assertFalse(_ram_remote_sw_ok(sw), sw)
|
||||
|
||||
def test_step_reports_the_remote_sw_failure(self):
|
||||
por = self._por(self.POR_INSTALL)
|
||||
self.assertEqual(por['response_status'], 'por_ok')
|
||||
step, err = _ram_step_result('LOAD (1/9)', '9000', por,
|
||||
self.POR_INSTALL, 274, 3)
|
||||
self.assertEqual(step['por_status'], 'por_ok')
|
||||
self.assertEqual(step['por_sw'], '6700')
|
||||
self.assertEqual(step['por_type'], 'compact')
|
||||
self.assertEqual(err, 'remote SW 6700')
|
||||
self.assertEqual(step['por_error'], 'remote SW 6700')
|
||||
|
||||
def test_step_accepts_9000_and_61xx_remote_sw(self):
|
||||
for hexstr, sw in ((self.POR_INSTALL_OK, '9000'),
|
||||
(self.POR_INSTALL_61XX, '6113')):
|
||||
por = self._por(hexstr)
|
||||
step, err = _ram_step_result('LOAD (1/9)', '9000', por, hexstr, 10, 1)
|
||||
self.assertIsNone(err, sw)
|
||||
self.assertNotIn('por_error', step)
|
||||
self.assertEqual(step['por_sw'], sw)
|
||||
|
||||
def test_step_no_por_and_undecodable(self):
|
||||
# transport SW 9000 with no response data at all: no PoR expected
|
||||
step, err = _ram_step_result('LOAD', '9000', None, '', 10, 1)
|
||||
self.assertIsNone(err)
|
||||
self.assertEqual(step['por_status'], 'no_por')
|
||||
# response data present but undecodable: a failure with the raw kept
|
||||
step, err = _ram_step_result('LOAD', '9000', None, 'DEADBEEF', 10, 1)
|
||||
self.assertEqual(err, 'PoR undecodable')
|
||||
self.assertEqual(step['por_raw'], 'DEADBEEF')
|
||||
|
||||
def test_remote_sw_from_expanded_response(self):
|
||||
por = {'response_status': 'por_ok', 'decoded': {},
|
||||
'responses': [{'status_word': '9000'}, {'status_word': '6A82'}]}
|
||||
self.assertEqual(_por_remote_sw(por), '6A82')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user