v3.23.8 review: the mixed-case command lookup and the flat-list docs

Two findings from the review of 2b994a7 (and c9ab15c, reviewed clean):

- trCommandCode() uppercased the query but compared it with the raw
  CMD_NAMES values, so the one mixed-case entry ('End of the proactive UICC
  session', 0x81) never matched.  No visible effect today (0x81 has no table
  entry - it falls back to the flat list), but a future map entry for a
  mixed-case name would silently not group.  The comparison is now
  case-insensitive, pinned by a test.
- The docs now say that the commands the spec's tables omit (SEND SS / SEND
  USSD / SEND SHORT MESSAGE / GEOGRAPHICAL LOCATION REQUEST / End of the
  proactive UICC session) keep the flat list, like ANY and the drain's rows.

Also added independent semantic pins for the parsed table (REFRESH 03/08,
PLAY TONE 09, TIMER MANAGEMENT 24, LAUNCH BROWSER 26, OPEN CHANNEL 28/3A/3B,
the MMS commands 27/3D, GET READER STATUS 38 - a column-shift regression in
the parse would break them).

861 frontend + 842 Python.
This commit is contained in:
2026-10-08 22:03:39 +03:00
parent 2b994a7a89
commit dc8d71c6a7
3 changed files with 26 additions and 2 deletions
+3 -1
View File
@@ -8094,7 +8094,9 @@ const TR_RESULT_ALIASES = {
function trCommandCode(name) {
const want = String(name === undefined || name === null ? '' : name).toUpperCase();
for (const k in CMD_NAMES) {
if (CMD_NAMES[k] === want) return parseInt(k, 16);
// case-insensitive: CMD_NAMES carries a mixed-case entry
// ('End of the proactive UICC session')
if (String(CMD_NAMES[k]).toUpperCase() === want) return parseInt(k, 16);
}
return NaN;
}
+19
View File
@@ -154,6 +154,22 @@ test('the command/result grouping table matches the parsed spec fixture', () =>
for (const code of Object.keys(TR_RESULT_BY_COMMAND)) {
assert.ok(CMD_NAMES[code], 'the mapped code 0x' + code + ' must be a known command');
}
// independent semantic pins (from the spec's command clauses): a column
// shift in the parsed table would break these
const has = (cmd, res) => TR_RESULT_BY_COMMAND[cmd].indexOf(res) >= 0;
assert.ok(has('01', '03') && has('01', '08'), 'REFRESH: additional EFs / NAA not active');
assert.ok(has('20', '09'), 'PLAY TONE: tone not played');
assert.ok(!has('01', '09') && !has('20', '03'), 'the command-specific values do not leak');
assert.ok(has('27', '24'), 'TIMER MANAGEMENT: timer state');
assert.ok(has('15', '26'), 'LAUNCH BROWSER: generic error');
assert.ok(has('40', '28') && has('40', '3A') && has('40', '3B'), 'OPEN CHANNEL: BIP / access technology');
assert.ok(has('60', '27') && has('60', '3D'), 'the MMS commands: MMS errors');
assert.ok(has('33', '38'), 'GET READER STATUS: MultipleCard');
// the spec's tables omit SEND SS / SEND USSD / SEND SHORT MESSAGE /
// GEOGRAPHICAL LOCATION REQUEST / End of the proactive UICC session -
// those keep the flat list (the fallback is checked in the grouping test)
assert.ok(trCommandCode('End of the proactive UICC session') === 0x81,
'a mixed-case command name must still resolve');
});
test('trResultOptions groups the list by the selected command', () => {
@@ -162,6 +178,9 @@ test('trResultOptions groups the list by the selected command', () => {
assert.strictEqual(flat.length, TR_RESULTS.length + 1);
assert.strictEqual(trResultOptions({ command: 'ANY' }).filter(o => o.group).length, 0,
'ANY stays flat');
// a command the spec's tables omit keeps the flat list too
assert.strictEqual(trResultOptions({ command: 'SEND SS' }).filter(o => o.group).length, 0,
'SEND SS (not in the tables) stays flat');
const grouped = trResultOptions({ command: 'REFRESH' });
assert.deepStrictEqual(grouped.filter(o => o.group).map(o => o.group),
['relevant for REFRESH', 'all other results']);