test(ocr): add /train-sender auth tests and run sender registry tests in CI
Add 503/403 auth tests for the /train-sender endpoint, matching the pattern already used for /train and /segtrain. Also surface test_sender_registry.py in CI (it needs no ML stack) and add pytest-asyncio to the install step. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ jobs:
|
||||
path: frontend/test-results/screenshots/
|
||||
|
||||
# ─── OCR Service Unit Tests ───────────────────────────────────────────────────
|
||||
# Only spell_check.py and test_confidence.py — no ML stack required.
|
||||
# Only spell_check.py, test_confidence.py, test_sender_registry.py — no ML stack required.
|
||||
ocr-tests:
|
||||
name: OCR Service Tests
|
||||
runs-on: ubuntu-latest
|
||||
@@ -60,11 +60,11 @@ jobs:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install test dependencies
|
||||
run: pip install "pyspellchecker==0.9.0" pytest
|
||||
run: pip install "pyspellchecker==0.9.0" pytest pytest-asyncio
|
||||
working-directory: ocr-service
|
||||
|
||||
- name: Run OCR unit tests (no ML stack required)
|
||||
run: python -m pytest test_spell_check.py test_confidence.py -v
|
||||
run: python -m pytest test_spell_check.py test_confidence.py test_sender_registry.py -v
|
||||
working-directory: ocr-service
|
||||
|
||||
# ─── Backend Unit & Slice Tests ───────────────────────────────────────────────
|
||||
|
||||
@@ -67,3 +67,42 @@ async def test_train_returns_403_when_token_wrong():
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
# ─── /train-sender authentication ─────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_train_sender_returns_503_when_training_token_not_configured():
|
||||
"""POST /train-sender must return 503 when TRAINING_TOKEN env var is empty.
|
||||
|
||||
An empty token means the service was started without training configured.
|
||||
Allowing requests through would grant unauthenticated access to the
|
||||
training endpoint, contradicting the principle of failing closed.
|
||||
"""
|
||||
with patch("main.TRAINING_TOKEN", ""), \
|
||||
patch("main._models_ready", True):
|
||||
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||
response = await client.post(
|
||||
"/train-sender",
|
||||
data={"output_model_path": "/app/models/sender_test.mlmodel"},
|
||||
files={"file": ("training.zip", _minimal_zip(), "application/zip")},
|
||||
)
|
||||
|
||||
assert response.status_code == 503
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_train_sender_returns_403_when_token_wrong():
|
||||
"""POST /train-sender must return 403 when TRAINING_TOKEN is set but header is wrong."""
|
||||
with patch("main.TRAINING_TOKEN", "secret-token"), \
|
||||
patch("main._models_ready", True):
|
||||
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
|
||||
response = await client.post(
|
||||
"/train-sender",
|
||||
data={"output_model_path": "/app/models/sender_test.mlmodel"},
|
||||
files={"file": ("training.zip", _minimal_zip(), "application/zip")},
|
||||
headers={"X-Training-Token": "wrong-token"},
|
||||
)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
Reference in New Issue
Block a user