Правки финального ревью: умолчания subject'ов не называем фактическими
M-3: docstring воркера, T5.4: docstring и сообщение roundtrip-теста, M-5: CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7f7a856c40
commit
1b50d652af
@ -4,7 +4,7 @@
|
||||
|
||||
## Что это
|
||||
Stateless-сервис разбора XLS/XLSX/XLSB: ОСВ по счетам (§1 правил) и ББ/ОФР из Excel (§12). Вход — NATS
|
||||
`files.tabular_uploaded` + файл в S3 `uploads/`; выход — JSON по `../docs/contracts/recognition-result.schema.json`
|
||||
`files.tabular_uploaded` (по умолчанию; задаётся OPENBAO__NATS__NATS_*_SUBJECT) + файл в S3 `uploads/`; выход — JSON по `../docs/contracts/recognition-result.schema.json`
|
||||
в S3 `results/` + NATS `files.tabular_parsed`. **Нет доступа к БД.** Не знает про организации, периметры, план счетов —
|
||||
возвращает структуру файла; нормализация и резолв — в arm-core.
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
"""Воркер распознавания: запрос `files.tabular_uploaded` → ack → S3 → парсер → S3 → `files.tabular_parsed`.
|
||||
"""Воркер распознавания: запрос на subject запросов → ack → S3 → парсер → S3 → subject ответа
|
||||
(по умолчанию `files.tabular_uploaded` / `files.tabular_parsed`).
|
||||
|
||||
Design D-2, D-4, D-5, D-7; spec tabular-recognition-service «Подтверждение запроса до обработки», «Результат по
|
||||
контракту», «Ошибки транспорта превращаются в результат», «Парсер — заменяемый интерфейс».
|
||||
@ -200,7 +201,8 @@ class RecognitionWorker:
|
||||
|
||||
# --- приём запроса ----------------------------------------------------------------------------------------------
|
||||
async def on_request(self, msg: InboundMessage) -> None:
|
||||
"""Обработчик подписки на `files.tabular_uploaded`; исключения наружу не выходят."""
|
||||
"""Обработчик подписки на subject запросов (по умолчанию `files.tabular_uploaded`);
|
||||
исключения наружу не выходят."""
|
||||
if not self._accepting:
|
||||
logger.warning("Воркер останавливается: запрос не принят (без ack, ядро переотправит)")
|
||||
return
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
Приложение поднимается в процессе теста через lifespan (`src.app`) с `NATS_ENABLED=True` — тот же код, что в проде:
|
||||
`_connect_s3`, `NatsConnection` с подпиской в queue group `tabular-parser`, `RecognitionWorker` с парсером по умолчанию
|
||||
(`get_parser(settings)` → `OsvParser`); исходник — битый zip, поэтому результат — ошибка `UNREADABLE`.
|
||||
Тест играет роль ядра: кладёт исходник в MinIO `uploads/…`, публикует запрос в `files.tabular_uploaded` с
|
||||
`Nats-Msg-Id = <job_id>:<attempt>` и собирает конверты из `files.tabular_parsed`.
|
||||
Тест играет роль ядра: кладёт исходник в MinIO `uploads/…`, публикует запрос на subject запросов прогона с
|
||||
`Nats-Msg-Id = <job_id>:<attempt>` и собирает конверты с subject'а ответа прогона.
|
||||
|
||||
Spec tabular-recognition-service: «Ack раньше результата», «Повторный запрос во время обработки» (здесь — после
|
||||
обработки: битый файл отвергается мгновенно; повтор во время обработки покрыт unit-тестами воркера), ошибка разбора
|
||||
@ -14,7 +14,7 @@ Spec tabular-recognition-service: «Ack раньше результата», «
|
||||
|
||||
Изоляция: subject'ы прогона уникальны — контейнер стенда сообщения теста не получает (OQ-83);
|
||||
`job_id`/`file_id`/ключ S3 уникальны на прогон (uuid4), исходник удаляется после теста; на шине может
|
||||
работать другой сервис — сообщения `files.tabular_parsed` фильтруются по своему `job_id`. Ожидания — по событиям с
|
||||
работать другой сервис — сообщения с subject'а ответа фильтруются по своему `job_id`. Ожидания — по событиям с
|
||||
таймаутом; единственная фиксированная пауза — явное окно тишины `QUIET_WINDOW` для отрицательных проверок.
|
||||
"""
|
||||
|
||||
@ -124,7 +124,7 @@ def _assert_ack(msg: Msg, request: dict[str, Any]) -> None:
|
||||
|
||||
|
||||
class Core:
|
||||
"""Тестовый клиент в роли ядра: публикует запросы и собирает `files.tabular_parsed` по своим `job_id`."""
|
||||
"""Тестовый клиент в роли ядра: публикует запросы и собирает сообщения с subject'а ответа по своим `job_id`."""
|
||||
|
||||
def __init__(self, client: NATS) -> None:
|
||||
self.client = client
|
||||
@ -151,7 +151,7 @@ class Core:
|
||||
return await asyncio.wait_for(self._inboxes[request["job_id"]].get(), MESSAGE_TIMEOUT)
|
||||
except TimeoutError:
|
||||
pytest.fail(
|
||||
f"нет сообщения files.tabular_parsed для job_id={request['job_id']} за {MESSAGE_TIMEOUT} с "
|
||||
f"нет сообщения на subject ответа прогона для job_id={request['job_id']} за {MESSAGE_TIMEOUT} с "
|
||||
"(сервис не ответил; если на шине есть другой инстанс tabular-parser, запрос мог уйти ему)",
|
||||
pytrace=False,
|
||||
)
|
||||
@ -241,7 +241,7 @@ async def service(monkeypatch: pytest.MonkeyPatch) -> AsyncIterator[None]:
|
||||
|
||||
@pytest.fixture
|
||||
async def core(service: None) -> AsyncIterator[Core]:
|
||||
"""Клиент «ядра», подписанный на `files.tabular_parsed` до первой публикации запроса."""
|
||||
"""Клиент «ядра», подписанный на subject ответа прогона до первой публикации запроса."""
|
||||
client = NATS()
|
||||
await client.connect(get_settings().OPENBAO.NATS.NATS_URL, connect_timeout=2, max_reconnect_attempts=1)
|
||||
core = Core(client)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user