import asyncio from unittest.mock import AsyncMock import pytest from app.services import email_workflow_service as module @pytest.mark.parametrize('classification', ['spam', 'newsletter', 'invoice', 'general']) def test_known_reply_routes_without_identity_approval(monkeypatch, classification): service = module.EmailWorkflowService() service.enabled = True monkeypatch.setattr(service, '_find_sag_id_from_thread_headers', lambda data: 42) monkeypatch.setattr(service, '_find_sag_id_from_thread_key', lambda key: 42) route = AsyncMock(return_value={'status': 'completed', 'sag_id': 42}) monkeypatch.setattr(service, '_handle_helpdesk_sag_routing', route) monkeypatch.setattr(service, '_find_matching_workflows', AsyncMock(return_value=[])) result = asyncio.run(service.execute_workflows({ 'id': 7, 'classification': classification, 'confidence_score': 0, 'sender_email': 'new-cc@example.dk', 'in_reply_to': ''})) assert result['workflows_succeeded'] == 1 route.assert_awaited_once() def test_unknown_newsletter_reply_does_not_override_skip(monkeypatch): service = module.EmailWorkflowService() service.enabled = True monkeypatch.setattr(service, '_find_sag_id_from_thread_headers', lambda data: None) monkeypatch.setattr(service, '_find_sag_id_from_thread_key', lambda key: None) route = AsyncMock() monkeypatch.setattr(service, '_handle_helpdesk_sag_routing', route) monkeypatch.setattr(service, '_find_matching_workflows', AsyncMock(return_value=[])) asyncio.run(service.execute_workflows({'id': 8, 'classification': 'newsletter', 'in_reply_to': ''})) route.assert_not_awaited() @pytest.mark.parametrize('known_reply', [True, False]) def test_processor_bypasses_manual_gate_only_for_verified_route(monkeypatch, known_reply): from app.services import email_processor_service as processor service = processor.EmailProcessorService.__new__(processor.EmailProcessorService) service.rules_enabled = False monkeypatch.setattr(processor, 'execute_query', lambda *args: []) monkeypatch.setattr(processor, 'execute_update', lambda *args: None) monkeypatch.setattr(processor.settings, 'WHISPER_ENABLED', False) monkeypatch.setattr(processor.settings, 'EMAIL_AUTO_CLASSIFY', False) monkeypatch.setattr(processor.settings, 'EMAIL_REQUIRE_MANUAL_APPROVAL', True) monkeypatch.setattr(processor.settings, 'EMAIL_WORKFLOWS_ENABLED', True) monkeypatch.setattr(processor.email_workflow_service, 'has_verified_automatic_route', lambda data: known_reply) execute = AsyncMock(return_value={'workflows_executed': 1, 'workflows_succeeded': 1}) monkeypatch.setattr(processor.email_workflow_service, 'execute_workflows', execute) park = AsyncMock() monkeypatch.setattr(service, '_set_awaiting_user_action', park) result = asyncio.run(service.process_single_email({'id': 9, 'classification': 'general', 'confidence_score': 0})) if known_reply: execute.assert_awaited_once() park.assert_not_awaited() assert not result['awaiting_user_action'] else: execute.assert_not_awaited() park.assert_awaited_once() assert result['awaiting_user_action'] def test_unknown_reply_header_is_not_a_verified_automatic_route(monkeypatch): service = module.EmailWorkflowService() monkeypatch.setattr(service, '_find_sag_id_from_thread_headers', lambda data: None) monkeypatch.setattr(service, '_find_sag_id_from_thread_key', lambda key: None) assert service.has_helpdesk_routing_hint({'in_reply_to': ''}) is True assert service.has_verified_automatic_route({'in_reply_to': ''}) is False