from unittest.mock import MagicMock import pytest from app.workflows.actions import execute_local_action, execute_case_action def test_remove_tag_always_preserves_manual_and_legacy(): cursor=MagicMock() execute_local_action(cursor,{'entity_type':'case','entity_id':'12'},{'type':'remove_tag','tag_id':4,'allow_manual':True}) sql,params=cursor.execute.call_args.args assert "source IN ('workflow','keyword','ai')" in sql assert params==('case',12,4) def test_chained_workflow_cannot_repeat(): cursor=MagicMock() with pytest.raises(ValueError,match='loop'): execute_local_action(cursor,{'entity_type':'case','entity_id':'12','chain':[5]},{'type':'start_workflow','workflow_id':5}) cursor.execute.assert_not_called() def test_automated_group_conflict_becomes_proposal(): cursor=MagicMock() cursor.fetchone.side_effect=[{'id':4,'tag_group_id':7,'behavior':'single'},{'exists':1}] execute_local_action(cursor,{'entity_type':'case','entity_id':'12'},{'type':'add_tag','tag_id':4}) assert 'INSERT INTO workflow_tag_proposals' in cursor.execute.call_args.args[0] assert all('DELETE' not in call.args[0] for call in cursor.execute.call_args_list) def test_user_assignment_validates_membership_before_mutation(): cursor=MagicMock() cursor.fetchone.side_effect=[{'id':12,'assigned_group_id':7},None] with pytest.raises(ValueError,match='belong'): execute_case_action(cursor,{'entity_type':'case','entity_id':'12'},{'type':'assign_user','user_id':3}) assert all('UPDATE' not in call.args[0].split(' FOR ')[0] for call in cursor.execute.call_args_list)