bmc_hub/tests/test_email_purchase_link.py

69 lines
2.9 KiB
Python
Raw Normal View History

from contextlib import contextmanager
import json
import pytest
from fastapi import HTTPException
from app.services import email_purchase_link as service
@pytest.fixture
def db(monkeypatch):
state = {'writes': [], 'mail': {'id': 7, 'linked_case_id': None},
'purchase': {'id': 10, 'sag_id': 42, 'description': 'Router', 'case_title': 'Netværk'}, 'previous': None}
@contextmanager
def transaction():
try:
yield
state['committed'] = True
except Exception:
state['rolled_back'] = True
raise
def read(sql, params):
if 'FROM email_messages' in sql:
assert 'FOR UPDATE' in sql
return state['mail']
if 'FROM sag_salgsvarer' in sql:
assert "p.type = 'purchase'" in sql and "p.status <> 'cancelled'" in sql
return state['purchase']
return state['previous']
monkeypatch.setattr(service, 'atomic_transaction', transaction)
monkeypatch.setattr(service, 'execute_query_single', read)
monkeypatch.setattr(service, 'execute_update', lambda sql, params: state['writes'].append((sql, params)))
return state
@pytest.mark.parametrize('kind,tracking', [('order_confirmation', None), ('freight_note', 'TRACK-123')])
def test_purchase_mail_links_and_logs_without_marking_goods_received(db, kind, tracking):
result = service.link_purchase_mail(7, 10, kind, tracking)
assert result['sag_id'] == 42
assert db['committed']
assert len(db['writes']) == 3
assert not any('UPDATE sag_salgsvarer' in sql for sql, _ in db['writes'])
metadata = json.loads(db['writes'][-1][1][2])
assert metadata == {'purchase_id': 10, 'sag_id': 42, 'kind': kind, 'tracking_number': tracking}
def test_repeated_identical_purchase_link_is_idempotent(db):
db['previous'] = {'metadata': {'purchase_id': 10, 'sag_id': 42, 'kind': 'freight_note', 'tracking_number': None}}
assert service.link_purchase_mail(7, 10, 'freight_note')['idempotent']
assert not db['writes']
@pytest.mark.parametrize('conflict', ['case', 'purchase', 'missing_mail', 'missing_purchase'])
def test_conflicting_or_missing_target_does_not_write(db, conflict):
if conflict == 'case': db['mail']['linked_case_id'] = 99
if conflict == 'purchase': db['previous'] = {'metadata': {'purchase_id': 99}}
if conflict == 'missing_mail': db['mail'] = None
if conflict == 'missing_purchase': db['purchase'] = None
with pytest.raises(HTTPException): service.link_purchase_mail(7, 10, 'order_confirmation')
assert not db['writes']
assert db['rolled_back']
def test_failure_between_link_and_audit_rolls_back(monkeypatch, db):
def fail(sql, params):
if 'UPDATE email_messages' in sql: raise RuntimeError('DB failed')
monkeypatch.setattr(service, 'execute_update', fail)
with pytest.raises(RuntimeError): service.link_purchase_mail(7, 10, 'freight_note')
assert db['rolled_back']
assert not db.get('committed')