import asyncio import json from io import BytesIO from unittest.mock import MagicMock import pytest from fastapi import HTTPException, UploadFile from app.modules.sag.backend import mail_templates as mail CASE={'id':186,'titel':'Router','customer_id':8,'customer_name':'Testkunde'} def test_tag_ranking_and_customer_scope(): rows=[{'name':'Global','customer_id':None,'match_tags':['MOBIL']},{'name':'Kunde','customer_id':8,'match_tags':['mobil','portering']},{'name':'Andet','customer_id':None,'match_tags':[]}] result=mail.rank_templates(rows,[' mobil ','portering']) assert [r['name'] for r in result]==['Kunde','Global','Andet'] assert result[0]['recommended'] and not result[-1]['recommended'] assert mail.available(rows[0],CASE) assert not mail.available({'customer_id':9},CASE) def test_variables_and_unknown_placeholder(): assert mail.render_text('{sag_id} {{kunde_navn}} {kontakt_navn} {{customer_name}} {andet}',CASE,{'name':'Anne'})=='186 Testkunde Anne Testkunde {andet}' def connection(monkeypatch,fetches,rows): conn=MagicMock();cur=conn.cursor.return_value.__enter__.return_value cur.fetchone.side_effect=fetches;cur.fetchall.return_value=rows monkeypatch.setattr(mail,'get_db_connection',lambda:conn) monkeypatch.setattr(mail,'release_db_connection',lambda c:None) return conn,cur @pytest.mark.parametrize('rows',[[],[{'customer_id':9}]]) def test_attachment_cannot_escape_customer(monkeypatch,rows): conn,_=connection(monkeypatch,[CASE],rows) with pytest.raises(HTTPException):mail.send_attachments(186,[1]) conn.rollback.assert_called();conn.commit.assert_not_called() def test_attachment_bytes_for_existing_sender(monkeypatch): connection(monkeypatch,[CASE],[{'customer_id':8,'filename':'ark.pdf','content_type':'application/pdf','content':memoryview(b'PDF'),'size_bytes':3}]) assert mail.send_attachments(186,[1])==[{'filename':'ark.pdf','content_type':'application/pdf','content':b'PDF','size':3}] @pytest.mark.parametrize('payload',[{},[],{'request_id':'invalid'}, {'request_id':'11111111-1111-1111-1111-111111111111','name':'','subject':'X','body':'X'}]) def test_invalid_input_rejected_before_db(payload): with pytest.raises(HTTPException):asyncio.run(mail.create_template(186,json.dumps(payload),[])) def test_create_template_keeps_scope_and_files(monkeypatch): conn,cur=connection(monkeypatch,[CASE,None,{'id':123}],[]) payload={'request_id':'11111111-1111-1111-1111-111111111111','name':'Mobil','subject':'Portering','body':'Hej','scope':'customer','match_tags':['mobil']} assert asyncio.run(mail.create_template(186,json.dumps(payload),[UploadFile(filename='../ark.pdf',file=BytesIO(b'PDF'))]))=={'id':123} inserts=[call.args for call in cur.execute.call_args_list if 'INSERT INTO' in call.args[0]] assert inserts[0][1][4]==8 assert inserts[1][1]==(123,'ark.pdf','application/octet-stream',b'PDF',3) conn.commit.assert_called_once() def test_replay_rejects_changed_files(monkeypatch): payload={'request_id':'11111111-1111-1111-1111-111111111111','name':'Mobil','subject':'Portering','body':'Hej','scope':'customer','match_tags':[]} previous=dict(payload,id=123,customer_id=8) conn,_=connection(monkeypatch,[CASE,previous],[{'filename':'old','content_type':'x','content':b'old','size_bytes':3}]) with pytest.raises(HTTPException) as exc:asyncio.run(mail.create_template(186,json.dumps(payload),[])) assert exc.value.status_code==409 conn.commit.assert_not_called()