v3.22.2: the runner's TERMINAL RESPONSE inherits the terminal's own objects

Two reported gaps:

- a scripted PROVIDE LOCAL INFORMATION expectation answered with an empty
  TR - the PLI dictionary (and the host clock for the date/time qualifier)
  was silently inert during runs while the poll path used it all along, so
  an applet waiting for its data stalled behind a passing expectation.  A
  POLL INTERVAL expectation likewise never echoed the Duration (6.8.4).
  testscript.build_tr() gained `answer_tlvs` (inserted after the Result per
  the 6.8.0 order, before text/item/raw) and the shared
  _test_terminal_response() wires the expect step, the proactive-drain
  action and the run cleanup to it; the script's `raw` overrides, and a
  non-success result gets no data object.
- a GP INSTALL/LOAD/DELETE step always failed the default exact-9000 check
  although the envelope SW 91XX is the successful answer (the re-announced
  SET UP MENU; XX is the pending command's length, never pinnable).
  Documented rather than loosening the default: a new "Driving a RAM install
  from a script" subsection in docs/test-scripts.md (the 9??? mask + por
  any, the per-step pending rule - a proactive-drain/expect after every
  91XX step), a sentence at the sw-default bullet, and an scp80
  default-pinning test.

Docs: api.md's respond clause, help EN/RU's TR Config note.
Tests: 839 frontend + 812 Python; sw cache simple-v406.
This commit is contained in:
2026-10-06 20:55:07 +03:00
parent 28a940c9f5
commit a76e0154b6
10 changed files with 223 additions and 15 deletions
+4 -1
View File
@@ -772,7 +772,10 @@ REFRESH, TS 102 223 8.18; `files` is a list of path hex strings, matched
order-insensitively); `respond` is the TERMINAL RESPONSE in the TS 102 223
6.8.0 object order (`result` name or value; `text`+`dcs` for GET INKEY/GET
INPUT, `dcs` `00` packed / `04` 8-bit / `08` UCS2; `item_id`; extra `raw`
TLVs appended after the standard objects).
TLVs appended after the standard objects). The terminal's own objects ride
along unless `raw` overrides them: a successful PROVIDE LOCAL INFORMATION
answer carries the TR Config PLI dictionary entry (or the host clock for the
date/time qualifier) and a POLL INTERVAL answer the current-interval echo.
The response is the initial state (`running: true`), the final counter
(`scp80_counter`) and the step list; poll `/api/test/status`. The **server
+56 -1
View File
@@ -352,7 +352,10 @@ next step.
```
- `sw` - default `{"mode": "exact", "value": "9000"}` (a `status` action with
`attempts > 1` defaults to the `91??` mask).
`attempts > 1` defaults to the `91??` mask). A GP INSTALL/LOAD/DELETE
envelope answers `91XX` - the card announces a proactive command - so give
those steps the `9???` mask and a `proactive-drain` after them (see
*Driving a RAM install from a script*).
- `data` - the response data (for SCP80: the inline PoR packet). The
`proactive-drain` action does not take a `data` check - assert the drained
commands with `require` and the final status with `sw`.
@@ -445,6 +448,13 @@ identities, **Result**, Duration, Text string, Item identifier, …):
- `item_id` - the Item identifier for a successful SELECT ITEM answer.
- `raw` - extra COMPREHENSION-TLVs appended after the standard objects.
The terminal's own command-specific objects ride with the scripted result
unless `raw` overrides them: a successful PROVIDE LOCAL INFORMATION answer
takes the TR Config **PLI dictionary** entry for its qualifier (the current
date and time for the date/time qualifier `03`), and a POLL INTERVAL answer
echoes the current interval (TS 102 223 §6.8.4) - the same objects the
terminal's own answers carry.
UCS2 answer to a GET INPUT:
```json
@@ -544,6 +554,51 @@ For this mode the terminal must **not** reset the card
(TS 102 223 §6.4.7), so the run continues - the following `file-read`
verifies the updated content.
### Driving a RAM install from a script
The RAM APDU list (`POST /api/scp81/gen-install`, or the PWA's *Install from
.cap*) is a sequence of GP commands - INSTALL [for load], the LOAD blocks,
INSTALL [for install], plus a DELETE first when the package is already
loaded. Drive it from a test script as one `scp80` step per APDU, addressed
to the preset's **ISD TAR** (`000000`; the preset supplies the keys, the
counter and the SPI1 from that TAR's MSL):
```json
{"name": "install applet",
"steps": [
{"type": "action", "kind": "scp80",
"params": {"apdu": "<INSTALL [for load] C-APDU>", "tar": "000000"},
"check": {"sw": {"mode": "mask", "value": "9???"}, "por": "any"},
"label": "INSTALL [for load]"},
{"type": "action", "kind": "scp80",
"params": {"apdu": "<LOAD 1 C-APDU>", "tar": "000000"},
"check": {"sw": {"mode": "mask", "value": "9???"}, "por": "any"},
"label": "LOAD 1"},
{"type": "action", "kind": "scp80",
"params": {"apdu": "<INSTALL [for install] C-APDU>", "tar": "000000"},
"check": {"sw": {"mode": "mask", "value": "9???"}, "por": "any"},
"label": "INSTALL [for install]"},
{"type": "action", "kind": "proactive-drain", "params": {}}
]}
```
A successful GP step answers **`91XX`**, not `9000`: the card has a pending
proactive command (the re-announced SET UP MENU). `XX` is that command's TLV
length - `915d` for the 93-byte SET UP MENU, `910b` for an 11-byte PROVIDE
LOCAL INFORMATION, `91b1` for a 177-byte SEND SHORT MESSAGE - so it can never
be pinned exactly. The default exact-`9000` check therefore fails a step
whose side effect succeeded; give every INSTALL/LOAD/DELETE step the `9???`
mask. `por` stays `any` by default - assert the remote command's own status
word with `{"por": {"status": "por_ok", "sw": "…"}}` when you want the
stronger check.
The runner refuses to start an action while a command is pending: after
**any** step that answered `91XX` the next step must be an `expect` or a
`proactive-drain`. The card re-announces the menu after the delete and after
the install, so a drain after those two groups is enough; a drain after any
`91XX` step is always safe. The drain fetches the SET UP MENU, so the cached
menu (text-based `menu-select`) is current afterwards.
### Incoming data → particular PoR, no other actions
Inline PoR: