test(audit): обмен и маппинг не пишут audit_event (OQ-55)
Негативное требование спеки audit-trail: ack, результат, маппинг и таймаут вахтёра не создают строк audit_event. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
f9b8a21410
commit
24d556e41e
@ -13,7 +13,7 @@ import json
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime, timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -23,6 +23,7 @@ from loguru import logger
|
|||||||
from sqlalchemy import func, select
|
from sqlalchemy import func, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||||
|
|
||||||
|
from src.contract import ack_msg_id, result_msg_id
|
||||||
from src.core.config import Settings, get_settings
|
from src.core.config import Settings, get_settings
|
||||||
from src.models.access import AppUser
|
from src.models.access import AppUser
|
||||||
from src.models.accounting import ChartOfAccount
|
from src.models.accounting import ChartOfAccount
|
||||||
@ -43,6 +44,7 @@ from src.models.enums import (
|
|||||||
from src.models.files import Document, RecognitionJob, StoredFile
|
from src.models.files import Document, RecognitionJob, StoredFile
|
||||||
from src.models.osv import OsvEntry
|
from src.models.osv import OsvEntry
|
||||||
from src.services import result_mapping as rm
|
from src.services import result_mapping as rm
|
||||||
|
from src.services.recognition_exchange import expire_deadlines, handle_message
|
||||||
from src.services.result_mapping import (
|
from src.services.result_mapping import (
|
||||||
MAPPING_ATTEMPTS_KEY,
|
MAPPING_ATTEMPTS_KEY,
|
||||||
MAPPING_ERROR_KEY,
|
MAPPING_ERROR_KEY,
|
||||||
@ -56,6 +58,7 @@ from src.services.result_mapping import (
|
|||||||
)
|
)
|
||||||
from tests.conftest import FakeStorage
|
from tests.conftest import FakeStorage
|
||||||
from tests.factories import make_document, make_legal_entity, make_result_object, make_stored_file
|
from tests.factories import make_document, make_legal_entity, make_result_object, make_stored_file
|
||||||
|
from tests.test_recognition_inbox import envelope, fake_msg
|
||||||
|
|
||||||
API_DIR = Path(__file__).resolve().parents[1]
|
API_DIR = Path(__file__).resolve().parents[1]
|
||||||
MINIMAL_RESULT = API_DIR / "contracts" / "examples" / "result-osv-minimal.json"
|
MINIMAL_RESULT = API_DIR / "contracts" / "examples" / "result-osv-minimal.json"
|
||||||
@ -1020,3 +1023,50 @@ async def test_new_file_supersedes_several_files(committed_sessionmaker: async_s
|
|||||||
for event in events:
|
for event in events:
|
||||||
assert event.user_id is None
|
assert event.user_id is None
|
||||||
assert event.after_value == {"file_id": str(new_id), "version_no": expected_version}
|
assert event.after_value == {"file_id": str(new_id), "version_no": expected_version}
|
||||||
|
|
||||||
|
|
||||||
|
# --- audit-trail: обмен и маппинг не пишут audit_event (ASSUMPTION(OQ-55)) ----------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
async def _audit_count(maker: async_sessionmaker[AsyncSession]) -> int:
|
||||||
|
async with maker() as session:
|
||||||
|
return (await session.execute(select(func.count()).select_from(AuditEvent))).scalar_one()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_exchange_and_mapping_write_no_audit_events(committed_sessionmaker: async_sessionmaker[AsyncSession]):
|
||||||
|
"""spec audit-trail «События файлов и документов»: смена статусов задания и файла (ack, результат, маппинг) и
|
||||||
|
автозамена без заменяемых файлов `audit_event` не пишут: путь `UPLOADED → PROCESSING → PROCESSED` оставляет таблицу
|
||||||
|
аудита неизменной."""
|
||||||
|
maker, storage = committed_sessionmaker, FakeStorage()
|
||||||
|
file_id, job_id, _ = await _prepare(
|
||||||
|
maker, storage, _minimal(), job_status=RecognitionStatus.published, file_status=FileStatus.UPLOADED
|
||||||
|
)
|
||||||
|
before = await _audit_count(maker)
|
||||||
|
|
||||||
|
await handle_message(
|
||||||
|
fake_msg(envelope(job_id, file_id, "processing"), ack_msg_id(job_id, 1)), session_factory=maker
|
||||||
|
)
|
||||||
|
assert (await _file(maker, file_id)).status is FileStatus.PROCESSING
|
||||||
|
await handle_message(fake_msg(envelope(job_id, file_id, "done"), result_msg_id(job_id, 1)), session_factory=maker)
|
||||||
|
assert (await _job(maker, job_id)).status is RecognitionStatus.done
|
||||||
|
assert await process_once(maker, storage, _settings()) is True
|
||||||
|
|
||||||
|
assert (await _file(maker, file_id)).status is FileStatus.PROCESSED
|
||||||
|
assert await _audit_count(maker) == before
|
||||||
|
|
||||||
|
|
||||||
|
async def test_watchdog_timeout_writes_no_audit_event(committed_sessionmaker: async_sessionmaker[AsyncSession]):
|
||||||
|
"""Путь ошибки: вахтёр переводит задание в `timeout`, файл — в `ERROR`, `audit_event` не пишется."""
|
||||||
|
maker, storage = committed_sessionmaker, FakeStorage()
|
||||||
|
file_id, job_id, _ = await _prepare(maker, storage, _minimal(), job_status=RecognitionStatus.processing)
|
||||||
|
async with maker() as session, session.begin():
|
||||||
|
job = await session.get(RecognitionJob, job_id)
|
||||||
|
assert job is not None
|
||||||
|
job.reply_deadline = _now() - timedelta(minutes=1)
|
||||||
|
before = await _audit_count(maker)
|
||||||
|
|
||||||
|
assert await expire_deadlines(maker, now=_now) >= 1
|
||||||
|
|
||||||
|
assert (await _file(maker, file_id)).status is FileStatus.ERROR
|
||||||
|
assert (await _job(maker, job_id)).status is RecognitionStatus.timeout
|
||||||
|
assert await _audit_count(maker) == before
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user