# Document fixtures for MRZ and document-scan tests

Synthetic travel documents for testing a feature that reads a machine readable
zone (MRZ): a passport scanner, a residence permit upload, a visa check.
Requires `@mmerterden/multi-agent-toolkit-mcp` ≥ v3.19.0.

## The two tools

| Tool | Returns | Writes |
|---|---|---|
| `document_mrz_build` | `{format, lines, checkDigits, documentNumber, truncated, fields}` | nothing |
| `document_generate` | the same plus `{path, width, height, label, layout}` | a PNG |

Formats: `passport` is TD3 (2 lines of 44), `residence_permit` is TD1 (3 lines
of 30), `visa` is MRV-A (2x44, default) or MRV-B (2x36) via `visa_format`.

Main inputs: `type`, `surname`, `given_names`, `nationality`, `issuing_state`
(defaults to nationality), `birth_date` and `expiry_date` as `YYYY-MM-DD`,
`sex` (`M`, `F`, `X`), `document_number` (omit it for a deterministic synthetic
number from `seed`), `optional_data`. `document_generate` adds `output`,
`include_data_block` and `scale`.

Every PNG carries a fixed "TEST SPECIMEN - NOT A TRAVEL DOCUMENT" label and a
plain layout. It is a test input for a parser and an OCR pipeline, not a
document likeness.

## Rules

- Synthetic identities only. Use made-up names and the ICAO test codes (`UTO`
  for nationality and issuing state) unless the test is about a specific
  country's rules. Never a real person's data, never a real document number.
- Fix `seed` in a test so the fixture is the same on every run.
- Assert on what the app extracted against `fields`, not against a re-read of
  the image: the tool's `fields` and `checkDigits` are the expected values.

## Getting the fixture to the app

| Path into the app | How |
|---|---|
| Photo library import | iOS: `ios_add_media` with the PNG `path`. Android: copy it to the device's pictures folder with adb and trigger a media scan |
| Debug deep link or test hook | pass `lines` straight in, when the app has a debug entry for MRZ text |
| Live camera | a simulator or emulator has no camera stream to aim at the image; this path needs a device and stays a manual check. Say so in the result |

## Cases worth a test each

| Case | Input |
|---|---|
| Happy path, each format | one `passport`, one `residence_permit`, one `visa` |
| Long names | a surname or given names longer than the line allows; check `truncated` and that the app does not reject a valid truncated zone |
| Transliteration | names with diacritics or non-Latin letters; the zone carries the ICAO transliteration, the data block the original |
| Sex `X` | written `<` in the zone; the app must not require `M` or `F` |
| Expired document | `expiry_date` in the past; the app's own rule decides, the zone stays valid |
| TD1 number overflow | a document number longer than 9 characters spills into the optional field |
| Broken check digit | build a valid zone, then change one digit in `lines` before passing it in; the app must reject it |

## A repro with a fixture

1. `document_generate` with a fixed `seed` and the case's input.
2. Put the PNG into the app's import path (table above).
3. Drive the scan with locators.
4. `expect_text` for each extracted field against `fields`: document number,
   birth date, expiry date, names.

The last step failing with `ASSERTION_FAILED` on one field is the bug, and the
same sequence saved with `save_as` is its regression test.
