from contextlib import contextmanager from unittest.mock import MagicMock from decimal import Decimal import pytest from fastapi import HTTPException from app.workflows import resources def test_card_rejects_monthly_price_before_any_write(): payload=resources.ResourceUpdate(workflow_coverage={},monthly_price=Decimal('10')) with pytest.raises(HTTPException) as error: resources.update_resource('card',1,payload,{'id':4}) assert error.value.status_code==422 def test_inactive_tag_rejected_without_mutating_resource(monkeypatch): cursor=MagicMock() cursor.fetchone.return_value={'id':1,'customer_id':24} cursor.fetchall.return_value=[] @contextmanager def transaction(): yield cursor monkeypatch.setattr(resources,'transaction',transaction) payload=resources.ResourceUpdate(workflow_coverage={'tag_ids':[7]}) with pytest.raises(HTTPException) as error: resources.update_resource('agreement',1,payload,{'id':4}) assert error.value.status_code==422 assert all(not call.args[0].startswith(('INSERT','UPDATE','DELETE')) for call in cursor.execute.call_args_list) def test_resource_audit_and_explicit_null_monthly_price(monkeypatch): cursor=MagicMock() original={'id':1,'customer_id':24,'workflow_coverage':{},'monthly_price':Decimal('99')} updated=dict(original,monthly_price=None,workflow_coverage={'tag_ids':[],'excluded_tag_ids':[],'include_descendants':False}) cursor.fetchone.side_effect=[original,original,updated] cursor.fetchall.return_value=[] @contextmanager def transaction(): yield cursor monkeypatch.setattr(resources,'transaction',transaction) result=resources.update_resource('agreement',1,resources.ResourceUpdate(workflow_coverage={},monthly_price=None),{'id':4}) assert result['monthly_price'] is None update=next(call for call in cursor.execute.call_args_list if call.args[0].startswith('UPDATE customer_fixed')) assert 'monthly_price=%s' in update.args[0] assert update.args[1][1] is None assert update.args[1][0].adapted['auto_billing'] is True assert any('INSERT INTO bmc_workflow_resource_history' in call.args[0] for call in cursor.execute.call_args_list) def test_customer_card_coverage_saves_profile_and_audit(monkeypatch): cursor=MagicMock() cursor.fetchone.side_effect=[{'id':24}, None] cursor.fetchall.return_value=[] @contextmanager def transaction(): yield cursor monkeypatch.setattr(resources,'transaction',transaction) result=resources.update_customer_card_coverage(24,resources.Coverage(),{'id':4}) assert result['workflow_coverage']['auto_billing'] is True calls=[c.args for c in cursor.execute.call_args_list] assert any('INSERT INTO bmc_workflow_customer_card_coverage' in c[0] and c[1][0]==24 for c in calls) assert any('INSERT INTO bmc_workflow_resource_history' in c[0] and c[1][0]=='customer_card' for c in calls) assert not any('UPDATE tticket_prepaid_cards' in c[0] for c in calls) def test_customer_card_coverage_rejects_inactive_tags(monkeypatch): cursor=MagicMock() cursor.fetchone.return_value={'id':24} cursor.fetchall.return_value=[] @contextmanager def transaction(): yield cursor monkeypatch.setattr(resources,'transaction',transaction) with pytest.raises(HTTPException) as error: resources.update_customer_card_coverage(24,resources.Coverage(tag_ids=[9]),{'id':4}) assert error.value.status_code==422 assert not any(c.args[0].startswith(('INSERT','UPDATE')) for c in cursor.execute.call_args_list)