Срез 0 (issue #1): раздел «Соглашения шаблона» (что оставлено от шаблона команды, что вырезается, что добавляется); change openspec/changes/slice-0-skeleton — proposal, design с таблицей «Decisions made without discussion», спеки и tasks; в openspec/config.yaml строки с двоеточием взяты в кавычки (YAML разбирал их как словари). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
47 lines
3.4 KiB
Markdown
47 lines
3.4 KiB
Markdown
# Spec Delta: scan-emulator
|
||
|
||
## Purpose
|
||
|
||
Эмулятор ИС Сканирование для разработки и тестов: воспроизводит протокол горячих папок и формат ответов Content AI,
|
||
отдавая реальные ответы из `../fixtures/ocr` по имени файла, чтобы конвейер `scan-gateway` проверялся без доступа к
|
||
банковской системе.
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: Эмулятор отвечает по протоколу горячих папок
|
||
Процесс `python -m src.scan_emulator` SHALL опрашивать входящий каталог каждую секунду; для нового файла
|
||
`<uuid>_<name>.<ext>` через `SCAN_EMULATOR_DELAY` секунд (по умолчанию 2) SHALL создать `<output>/<uuid>/<uuid>.json`;
|
||
после обработки входящий файл SHALL быть переименован в `<uuid>_<name>.<ext>.done`, чтобы не обрабатываться повторно.
|
||
|
||
#### Scenario: Один файл
|
||
- **WHEN** во входящем каталоге появляется `3f1c…_aep_5.pdf`
|
||
- **THEN** через ≤ 5 с существует `Output/3f1c…/3f1c….json`, а входящий файл переименован в `…pdf.done`
|
||
|
||
### Requirement: Ответ берётся из фикстур по имени
|
||
Эмулятор SHALL искать в `SCAN_FIXTURES_DIR` файл `<name>.json`, затем `<name>*.json` (первый по алфавиту), где `<name>` —
|
||
имя входящего файла без префикса `<uuid>_` и расширения; содержимое SHALL быть записано в ответ с `file_uid = <uuid>`.
|
||
|
||
#### Scenario: Точное имя
|
||
- **WHEN** входящий файл — `…_aep_5.pdf`, в фикстурах есть `aep_5.json`
|
||
- **THEN** ответ равен `aep_5.json` с заменённым `file_uid`
|
||
|
||
#### Scenario: Префикс
|
||
- **WHEN** входящий файл — `…_aep_8.pdf`, в фикстурах есть `aep_8_form4.json`
|
||
- **THEN** ответ равен `aep_8_form4.json` с заменённым `file_uid`
|
||
|
||
### Requirement: Неизвестный файл — ошибка в формате Content AI
|
||
Если пара не найдена, эмулятор SHALL записать ответ `{"file_uid": "<uuid>", "error": {"code": "INVALID_DOCUMENT",
|
||
"message": "<имя>: нет эталонного ответа в фикстурах"}, "data_ocr": []}`.
|
||
|
||
#### Scenario: Нет пары
|
||
- **WHEN** входящий файл — `…_unknown.pdf`
|
||
- **THEN** ответ содержит `error.code = INVALID_DOCUMENT` и пустой `data_ocr`; `scan-gateway` публикует конверт с тем же кодом
|
||
|
||
### Requirement: Эмулятор запускается из того же образа
|
||
В `docs/dev/docker-compose.yml` сервис `scan-emulator` SHALL использовать образ `scan-gateway` с командой
|
||
`python -m src.scan_emulator`, общий том `hotfolder` и `../../fixtures/ocr`, смонтированный только для чтения.
|
||
|
||
#### Scenario: Compose
|
||
- **WHEN** выполнен `docker compose --profile services up -d`
|
||
- **THEN** контейнер `scan-emulator` работает, его лог сообщает каталоги и число найденных пар фикстур
|